Merge branch 'release/UpdateProd_01'
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>8.16.2606.1117</Version>
|
||||
<Version>8.16.2606.1312</Version>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>MP_TAB3</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo MAPOSPEC </i>
|
||||
<h4>Versione: 8.16.2606.1117</h4>
|
||||
<h4>Versione: 8.16.2606.1312</h4>
|
||||
<br /> Note di rilascio:
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.16.2606.1117
|
||||
8.16.2606.1312
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>8.16.2606.1117</version>
|
||||
<version>8.16.2606.1312</version>
|
||||
<url>https://nexus.steamware.net/repository/SWS/MP-TAB3/stable/LAST/MP-TAB3.zip</url>
|
||||
<changelog>https://nexus.steamware.net/repository/SWS/MP-TAB3/stable/LAST/ChangeLog.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
@@ -197,11 +197,12 @@ namespace MP.Data.Repository.Production
|
||||
var DateFrom = new SqlParameter("@DtInizio", startDate);
|
||||
var DateTo = new SqlParameter("@DtFine", endDate);
|
||||
var pFlagAtt = new SqlParameter("@flgAttive", flagAttive);
|
||||
var pFlagKit = new SqlParameter("@flgPodlKit", true);
|
||||
var pFlagKChild = new SqlParameter("@flgPodChild", flagKitChild);
|
||||
|
||||
return await dbCtx
|
||||
.DbSetPODLExp
|
||||
.FromSqlRaw("EXEC stp_PODL_getByFiltSpecKit @Lanciato, @KeyRich, @CodGruppo, @IdxMacchina, @DtInizio, @DtFine, @flgAttive, @flgPodChild", Lanc, KeyRich, CodGrp, IdxMacc, DateFrom, DateTo, pFlagAtt, pFlagKChild)
|
||||
.FromSqlRaw("EXEC stp_PODL_getByFiltSpecKit @Lanciato, @KeyRich, @CodGruppo, @IdxMacchina, @DtInizio, @DtFine, @flgAttive, @flgPodlKit, @flgPodChild", Lanc, KeyRich, CodGrp, IdxMacc, DateFrom, DateTo, pFlagAtt, pFlagKit, pFlagKChild)
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
@@ -368,8 +368,8 @@ namespace MP.Data.Services
|
||||
var cacheOptions = new FusionCacheEntryOptions()
|
||||
.SetDuration(expiration)
|
||||
.SetFailSafe(true);
|
||||
// cache in RAM per 1/3 del tempo x risparmiare risorse
|
||||
cacheOptions.MemoryCacheDuration = expiration / 3;
|
||||
// cache in RAM per 1/2 del tempo x risparmiare risorse
|
||||
cacheOptions.MemoryCacheDuration = expiration / 2;
|
||||
|
||||
var final = await _cache.GetOrSetAsync<T>(
|
||||
cacheKey,
|
||||
|
||||
@@ -91,6 +91,19 @@ namespace MP.Data.Services.IOC
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetCurrOdlAsync(string idxMacchina)
|
||||
{
|
||||
string cKey = $"{MP.Data.Utils.redisOdlCurrByMac}:{idxMacchina}";
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "GetCurrOdlAsync",
|
||||
cacheKey: cKey,
|
||||
fetchFunc: async () =>
|
||||
{
|
||||
return await GetCurrOdlByProdAsync(idxMacchina);
|
||||
},
|
||||
expiration: GetRandTOut(redisLongTimeCache),
|
||||
tagList: [idxMacchina]
|
||||
);
|
||||
|
||||
#if false
|
||||
string result = "";
|
||||
string currKey = $"{MP.Data.Utils.redisOdlCurrByMac}:{idxMacchina}";
|
||||
// cerco in redis dato valOut sel macchina...
|
||||
@@ -105,6 +118,7 @@ namespace MP.Data.Services.IOC
|
||||
_redisDb.StringSet(currKey, result, GetRandTOut(redisShortTimeCache));
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -117,39 +131,48 @@ namespace MP.Data.Services.IOC
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> IobInsEnabAsync(string idxMacchina)
|
||||
{
|
||||
#if false
|
||||
string cacheKey = $"IOC_IobInsEnab_{idxMacchina}";
|
||||
return await GetOrFetchAsync(cacheKey, async () =>
|
||||
{
|
||||
var rKey = MP.Data.Utils.RedKeyDatiMacc(idxMacchina, MpIoNS);
|
||||
|
||||
string? val = await _redisDb.HashGetAsync(rKey, "insEnabled");
|
||||
|
||||
if (val == null)
|
||||
string cKey = $"IOC_IobInsEnab_{idxMacchina}";
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "StatoProdMacchinaAsync",
|
||||
cacheKey: cKey,
|
||||
fetchFunc: async () =>
|
||||
{
|
||||
var data = await ResetDatiMacchinaAsync(idxMacchina);
|
||||
data.TryGetValue("insEnabled", out val);
|
||||
}
|
||||
var rKey = MP.Data.Utils.RedKeyDatiMacc(idxMacchina, MpIoNS);
|
||||
var val = await _redisDb.HashGetAsync(rKey, "insEnabled");
|
||||
|
||||
return val != null && (val == "1" || val.ToLower() == "true");
|
||||
}, TimeSpan.FromSeconds(5));
|
||||
if (!val.HasValue)
|
||||
{
|
||||
var data = await ResetDatiMacchinaAsync(idxMacchina);
|
||||
// 2. Uso del pattern matching per evitare allocazioni e passaggi intermedi
|
||||
return data != null
|
||||
&& data.TryGetValue("insEnabled", out string? sVal)
|
||||
&& IsStringTrue(sVal);
|
||||
}
|
||||
|
||||
// 3. Conversione efficiente da RedisValue a string (evita l'interpolazione $"{val}")
|
||||
string? sRedisVal = val;
|
||||
return IsStringTrue(sRedisVal);
|
||||
#if false
|
||||
var rKey = MP.Data.Utils.RedKeyDatiMacc(idxMacchina, MpIoNS);
|
||||
var val = await _redisDb.HashGetAsync(rKey, "insEnabled");
|
||||
string sVal = "";
|
||||
if (!val.HasValue)
|
||||
{
|
||||
var data = await ResetDatiMacchinaAsync(idxMacchina);
|
||||
data.TryGetValue("insEnabled", out sVal);
|
||||
}
|
||||
else
|
||||
{
|
||||
sVal = $"{val}";
|
||||
}
|
||||
|
||||
return !string.IsNullOrEmpty(sVal) && (sVal == "1" || sVal.ToLower() == "true");
|
||||
#endif
|
||||
|
||||
bool answ = false;
|
||||
// ORA recupero da memoria redis...
|
||||
var rKey = MP.Data.Utils.RedKeyDatiMacc(idxMacchina, MpIoNS);
|
||||
RedisValue rawData = await _redisDb.HashGetAsync(rKey, (RedisValue)"insEnabled");
|
||||
// se è vuoto... leggo da DB e popolo!
|
||||
if (!rawData.HasValue)
|
||||
{
|
||||
await ResetDatiMacchinaAsync(idxMacchina);
|
||||
// riprovo
|
||||
rawData = await _redisDb.HashGetAsync(rKey, (RedisValue)"insEnabled");
|
||||
}
|
||||
|
||||
// provo conversione
|
||||
bool.TryParse($"{rawData}", out answ);
|
||||
return answ;
|
||||
},
|
||||
expiration: GetRandTOut(60),
|
||||
//expiration: GetRandTOut(30),
|
||||
tagList: ["IOC_IobInsEnab", cKey, idxMacchina]
|
||||
);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -201,7 +224,6 @@ namespace MP.Data.Services.IOC
|
||||
// gestisce i casi DB/REDIS secondo necessità
|
||||
await CheckMicroStatoAsync(idxMacchina, valore, dataOraEvento, contatore, datiMacc);
|
||||
}
|
||||
await ClearFusionCache(idxMacchina);
|
||||
// forzo RESET dati macchina...
|
||||
await ResetDatiMacchinaAsync(idxMacchina);
|
||||
// registro in risposta che è andato tutto bene...
|
||||
@@ -301,30 +323,14 @@ namespace MP.Data.Services.IOC
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#if false
|
||||
/// <summary>
|
||||
/// Restituisce un timeout dai minuti richiesti + tempo random 1..60 sec
|
||||
/// </summary>
|
||||
/// <param name="stdMinutes"></param>
|
||||
/// <returns></returns>
|
||||
protected TimeSpan GetRandTOut(double stdMinutes)
|
||||
{
|
||||
double rndValue = stdMinutes + (double)rand.Next(1, 60) / 60;
|
||||
return TimeSpan.FromMinutes(rndValue);
|
||||
}
|
||||
#endif
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
#if false
|
||||
private readonly IFusionCache _cache;
|
||||
#endif
|
||||
|
||||
private readonly string _className;
|
||||
|
||||
private readonly IIocRepository _repo;
|
||||
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
|
||||
/// <summary>
|
||||
@@ -339,14 +345,38 @@ namespace MP.Data.Services.IOC
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static bool IsStringTrue(string? value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) return false;
|
||||
|
||||
// Evita ToLower() che alloca una nuova stringa in memoria ad ogni chiamata
|
||||
return value == "1"
|
||||
|| value.Equals("true", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
#if false
|
||||
/// <summary>
|
||||
/// Restituisce un timeout dai minuti richiesti + tempo random 1..60 sec
|
||||
/// </summary>
|
||||
/// <param name="stdMinutes"></param>
|
||||
/// <returns></returns>
|
||||
protected TimeSpan GetRandTOut(double stdMinutes)
|
||||
{
|
||||
double rndValue = stdMinutes + (double)rand.Next(1, 60) / 60;
|
||||
return TimeSpan.FromMinutes(rndValue);
|
||||
}
|
||||
#endif
|
||||
#if false
|
||||
private readonly IFusionCache _cache;
|
||||
#endif
|
||||
#if false
|
||||
private int redisLongTimeCache = 5;
|
||||
|
||||
private int redisShortTimeCache = 2;
|
||||
#endif
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// controlla se da il segnale di "microstato" deriva un evento da generare - modalità OFFLINE
|
||||
/// </summary>
|
||||
@@ -534,14 +564,15 @@ namespace MP.Data.Services.IOC
|
||||
// se è vuoto... leggo da DB e popolo!
|
||||
if (!rawData.HasValue)
|
||||
{
|
||||
await ResetDatiMacchinaAsync(idxMacchina);
|
||||
// riprovo
|
||||
rawData = await _redisDb.HashGetAsync(rKey, (RedisValue)"sLogEnabled");
|
||||
}
|
||||
var data = await ResetDatiMacchinaAsync(idxMacchina);
|
||||
|
||||
// provo conversione
|
||||
bool.TryParse($"{rawData}", out answ);
|
||||
return answ;
|
||||
// 2. Uso del pattern matching per evitare allocazioni e passaggi intermedi
|
||||
return data != null
|
||||
&& data.TryGetValue("sLogEnabled", out string? sVal)
|
||||
&& IsStringTrue(sVal);
|
||||
}
|
||||
string? sRedisVal = rawData;
|
||||
return IsStringTrue(sRedisVal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -886,6 +917,8 @@ namespace MP.Data.Services.IOC
|
||||
// Eseguiamo tutto in un unico viaggio verso Redis
|
||||
bool success = await transaction.ExecuteAsync();
|
||||
|
||||
await ForceFlushFusionCacheAsync(idxMacc);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1040,13 +1073,13 @@ namespace MP.Data.Services.IOC
|
||||
/// <returns></returns>
|
||||
private async Task<StatoProdModel> StatoProdMacchinaAsync(string idxMacchina, DateTime dtReq, bool forceDb = false)
|
||||
{
|
||||
string cKey = $"IOC_StatoProd_{idxMacchina}";
|
||||
var stdTTL = TimeSpan.FromSeconds(30);
|
||||
string cKey = $"{MP.Data.Utils.redisStatoProd}:{idxMacchina}";
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "StatoProdMacchinaAsync",
|
||||
cacheKey: cKey,
|
||||
fetchFunc: async () =>
|
||||
{
|
||||
#if false
|
||||
StatoProdModel? result = new StatoProdModel();
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{MP.Data.Utils.redisStatoProd}:{idxMacchina}:{dtReq:HHmm}";
|
||||
@@ -1067,8 +1100,10 @@ namespace MP.Data.Services.IOC
|
||||
result = new StatoProdModel();
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
return await _repo.StatoProdMacchinaAsync(idxMacchina, dtReq);
|
||||
},
|
||||
expiration: stdTTL,
|
||||
expiration: GetRandTOut(redisLongTimeCache),
|
||||
tagList: ["IOC_StatoProd", cKey, idxMacchina]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -63,6 +63,13 @@ namespace MP.Data.Services.Utils
|
||||
/// <returns></returns>
|
||||
Task ResetCache();
|
||||
|
||||
/// <summary>
|
||||
/// Forza il reset della cache REDIS x il pattern richiesto
|
||||
/// </summary>
|
||||
/// <param name="pattern"></param>
|
||||
/// <returns></returns>
|
||||
Task ResetCache(string pattern);
|
||||
|
||||
/// <summary>
|
||||
/// Inserisce o aggiorna in batch le statistiche di dettaglio nel database.
|
||||
/// Opzionalmente elimina i record precedenti nel periodo specificato.
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace MP.Data.Services.Utils
|
||||
IConnectionMultiplexer redis,
|
||||
IFusionCache cache,
|
||||
IStatsDetailRepository repo
|
||||
) : base(config,cache, redis)
|
||||
) : base(config, cache, redis)
|
||||
{
|
||||
_className = "StatsDetail";
|
||||
_repo = repo;
|
||||
@@ -134,6 +134,14 @@ namespace MP.Data.Services.Utils
|
||||
await ClearCacheAsync($"{_redisBaseKey}:*");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ResetCache(string pattern)
|
||||
{
|
||||
// tolgo eventuali ":" finali
|
||||
pattern = pattern.EndsWith(":") ? pattern.Substring(0, pattern.Length - 1) : pattern;
|
||||
await ClearCacheAsync($"{pattern}:*");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<int> UpsertManyAsync(List<StatsDetailModel> listRecords, bool removeOld)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>MP.INVE</RootNamespace>
|
||||
<Version>8.16.2606.1117</Version>
|
||||
<Version>8.16.2606.1312</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo MAPOINVE </i>
|
||||
<h4>Versione: 8.16.2606.1117</h4>
|
||||
<h4>Versione: 8.16.2606.1312</h4>
|
||||
<br /> Note di rilascio:
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.16.2606.1117
|
||||
8.16.2606.1312
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>8.16.2606.1117</version>
|
||||
<version>8.16.2606.1312</version>
|
||||
<url>https://nexus.steamware.net/repository/SWS/MP-INVE/stable/LAST/MP.INVE.zip</url>
|
||||
<changelog>https://nexus.steamware.net/repository/SWS/MP-INVE/stable/LAST/ChangeLog.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace MP.IOC.Components.Pages
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
MpIoNS = Config.GetValue<string>("ServerConf:MpIoNS") ?? "MP";
|
||||
await ReloadData();
|
||||
}
|
||||
|
||||
@@ -105,6 +106,9 @@ namespace MP.IOC.Components.Pages
|
||||
[Inject]
|
||||
private IIocService IocService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IConfiguration Config { get; set; } = null!;
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
@@ -114,8 +118,14 @@ namespace MP.IOC.Components.Pages
|
||||
return currStatSel != null && currStatSel.Title == curKey ? "active" : "";
|
||||
}
|
||||
|
||||
private string MpIoNS = "";
|
||||
|
||||
private async Task DoForceReload()
|
||||
{
|
||||
// svuoto cache dati IoNS...
|
||||
var dtMaccKey = Utils.RedKeyDatiMacc("", MpIoNS);
|
||||
await SDetService.ResetCache(dtMaccKey);
|
||||
// resto delle cache
|
||||
await SDetService.ResetCache();
|
||||
await IocService.ClearFusionCache();
|
||||
DoReset();
|
||||
|
||||
@@ -75,41 +75,61 @@ namespace MP.IOC.Controllers
|
||||
return Ok(answ);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GET: IOB/
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public IActionResult Alive()
|
||||
{
|
||||
return Ok("OK"); // Restituisce Status 200
|
||||
}
|
||||
|
||||
#if false
|
||||
/// <summary>
|
||||
/// GET: IOB/enabled/SIMUL_03
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("enabled/{id}")]
|
||||
public async Task<IActionResult> Enabled(string id)
|
||||
public async Task<string> Enabled(string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||
return "Missing ID";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Il metodo ora restituisce direttamente il booleano logico
|
||||
bool isEnabled = await IOCService.IobInsEnabAsync(id);
|
||||
|
||||
return isEnabled
|
||||
? Ok("OK")
|
||||
: UnprocessableEntity("NO");
|
||||
// Eliminazione delle allocazioni di stringhe e oggetti inutili
|
||||
if (!isEnabled)
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status422UnprocessableEntity;
|
||||
return "NO";
|
||||
}
|
||||
|
||||
// Status 200 di default, scrive direttamente sul body della response
|
||||
return "OK";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Errore durante la verifica abilitazione per {Id}", id);
|
||||
return StatusCode(500, "Errore interno del server");
|
||||
Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||
return "Errore interno del server";
|
||||
}
|
||||
|
||||
//if (string.IsNullOrEmpty(id)) return BadRequest("Missing ID");
|
||||
|
||||
//try
|
||||
//{
|
||||
// // Il metodo ora restituisce direttamente il booleano logico
|
||||
// bool isEnabled = await IOCService.IobInsEnabAsync(id);
|
||||
|
||||
// return isEnabled
|
||||
// ? Ok("OK")
|
||||
// : UnprocessableEntity("NO");
|
||||
//}
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// Log.Error(ex, "Errore durante la verifica abilitazione per {Id}", id);
|
||||
// return StatusCode(500, "Errore interno del server");
|
||||
//}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Processa una chiamata POST per l'invio di un array Json di oggetti input (EVENTI)
|
||||
@@ -1086,7 +1106,7 @@ namespace MP.IOC.Controllers
|
||||
try
|
||||
{
|
||||
answ = await DService.saveCaricoPezzi(id, qty);
|
||||
await IOCService.ClearFusionCache();
|
||||
await IOCService.ClearFusionCache(id);
|
||||
return Ok(answ);
|
||||
}
|
||||
catch (Exception exc)
|
||||
@@ -1449,6 +1469,7 @@ namespace MP.IOC.Controllers
|
||||
#endregion Public Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private IIocService IOCService;
|
||||
|
||||
|
||||
@@ -1224,7 +1224,7 @@ namespace MP.IOC.Data
|
||||
cacheKey: $"{Utils.redisOdlList}:Current:{IdxMacchina}",
|
||||
expiration: TimeSpan.FromSeconds(redisShortTimeCache),
|
||||
fetchFunc: async () => (await IocDbController.OdlCurrByMaccAsync(IdxMacchina)) ?? new ODLExpModel(),
|
||||
tagList: [Utils.redisOdlList]
|
||||
tagList: [Utils.redisOdlList, IdxMacchina]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
using MP.Data.Services.IOC;
|
||||
using NLog;
|
||||
|
||||
namespace MP.IOC.Endpoints
|
||||
{
|
||||
public static class IobEndpoints
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
public static void MapIobHighPerformanceEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
// 1. Root di test base: api/IOB e api/IOB/alive
|
||||
app.MapGet("api/IOB", () => "OK");
|
||||
app.MapGet("api/IOB/alive", () => "OK");
|
||||
|
||||
// 2. Il metodo Enabled ad alto traffico: api/IOB/enabled/{id}
|
||||
app.MapGet("api/IOB/enabled/{id}", async (string id, IIocService iocService, HttpContext context) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
return Results.BadRequest("Missing ID");
|
||||
|
||||
try
|
||||
{
|
||||
bool isEnabled = await iocService.IobInsEnabAsync(id);
|
||||
|
||||
return isEnabled
|
||||
? Results.Ok("OK")
|
||||
: Results.UnprocessableEntity("NO");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Errore durante la verifica abilitazione per {Id}", id);
|
||||
return Results.StatusCode(500);
|
||||
}
|
||||
});
|
||||
|
||||
#if false
|
||||
// 3. Metodo: SetCounter (api/IOB/setCounter/{id}?counter=10)
|
||||
// Nota: 'counter' viene letto automaticamente dalla Query String grazie al Model Binding automatico
|
||||
app.MapGet("api/IOB/setCounter/{id}", async (string id, string counter, IIocService iocService, HttpContext context) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||
return "Missing ID";
|
||||
}
|
||||
|
||||
// Ottimizzazione: esegui il Replace solo se il carattere è effettivamente presente
|
||||
if (id.Contains('|'))
|
||||
{
|
||||
id = id.Replace('|', '#');
|
||||
}
|
||||
|
||||
// Se il log di Debug è disattivato in produzione, eviti l'allocazione della stringa interpolata
|
||||
if (Log.IsDebugEnabled)
|
||||
{
|
||||
Log.Debug($"Salvataggio counter | id: {id} | pzCount: {counter}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string answ = await iocService.SaveCounterAsync(id, counter);
|
||||
return answ; // Ritorna direttamente la stringa (Status 200 di default, zero allocazioni di ObjectResult)
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
// Usiamo la stringa formattata standard di NLog per evitare Environment.NewLine manuale
|
||||
Log.Error(exc, "Errore in SetCounter per macchina {MachineId}", id);
|
||||
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||
return "NO";
|
||||
}
|
||||
});
|
||||
|
||||
// 4. Metodo: GetCurrODL (api/IOB/getCurrODL/{id})
|
||||
app.MapGet("api/IOB/getCurrODL/{id}", async (string id, IIocService iocService, HttpContext context) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||
return "Missing ID";
|
||||
}
|
||||
|
||||
if (id.Contains('|'))
|
||||
{
|
||||
id = id.Replace('|', '#');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var odl = await iocService.GetCurrOdlAsync(id);
|
||||
|
||||
// Ottimizzazione: Se 'odl' è già una stringa, ritornala direttamente.
|
||||
// Se è un oggetto o un numero, il C# gestirà l'estrazione. Evitiamo $"{odl}" che alloca memoria inutilmente.
|
||||
return odl?.ToString() ?? string.Empty;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Log.Error(exc, "Errore GetCurrODL | macchina {MachineId}", id);
|
||||
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||
return "NO";
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
#endregion Private Fields
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Version>8.16.2606.1117</Version>
|
||||
<Version>8.16.2606.1511</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
+6
-1
@@ -6,6 +6,7 @@ using MP.Core.Conf;
|
||||
using MP.Data;
|
||||
using MP.IOC.Components;
|
||||
using MP.IOC.Data;
|
||||
using MP.IOC.Endpoints;
|
||||
using MP.IOC.Services;
|
||||
using NLog;
|
||||
using NLog.Web;
|
||||
@@ -170,9 +171,13 @@ app.UseDefaultFiles();
|
||||
app.UseStaticFiles();
|
||||
app.UseAntiforgery();
|
||||
|
||||
// Mappatura delle API
|
||||
// Mappatura MinimalApi da file ext x metodi high perf
|
||||
app.MapIobHighPerformanceEndpoints();
|
||||
|
||||
// Mappatura delle API "standard"
|
||||
app.MapControllers();
|
||||
|
||||
|
||||
// Mappatura della Dashboard Blazor
|
||||
app.MapRazorComponents<App>()
|
||||
.AddInteractiveServerRenderMode();
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:7050;http://localhost:5264",
|
||||
"applicationUrl": "https://*:7050;http://*:5264",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo MP-IOC </i>
|
||||
<h4>Versione: 8.16.2606.1117</h4>
|
||||
<h4>Versione: 8.16.2606.1511</h4>
|
||||
<br /> Note di rilascio:
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.16.2606.1117
|
||||
8.16.2606.1511
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>8.16.2606.1117</version>
|
||||
<version>8.16.2606.1511</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>
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"Microsoft.EntityFrameworkCore.Database.Command": "Warning",
|
||||
"Microsoft.EntityFrameworkCore.Infrastructure": "Warning",
|
||||
"Microsoft.WebTools.BrowserLink.Net.BrowserLinkMiddleware": "None",
|
||||
"Microsoft.AspNetCore.Watch.BrowserRefresh.BrowserRefreshMiddleware": "None"
|
||||
"Microsoft.AspNetCore.Watch.BrowserRefresh.BrowserRefreshMiddleware": "None",
|
||||
"ZiggyCreatures.Caching.Fusion": "Warning"
|
||||
}
|
||||
},
|
||||
"NLog": {
|
||||
@@ -24,7 +25,7 @@
|
||||
"logfile": {
|
||||
"type": "File",
|
||||
"fileName": "${basedir}/logs/${shortdate}.log",
|
||||
"keepFileOpen": false,
|
||||
"keepFileOpen": true,
|
||||
"archiveEvery": "Day",
|
||||
"archiveFileName": "${basedir}/logs/old/${shortdate}_{#}.log",
|
||||
"archiveNumbering": "DateAndSequence",
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
# Piano Migrazione MpDataService → IIocService
|
||||
|
||||
> **Obiettivo**: Standardizzare l'accesso dati su `IIocService` (con FusionCache) e pensionare `MpDataService` come service layer principale di `MP.IOC`.
|
||||
>
|
||||
> **Strategia**: Ogni metodo `MpDataService` usato dal controller viene migrato in `IocService`. La cache FusionCache avvolge le chiamate Redis/DB con le stesse durate e tag esistenti. I controller usano solo `IIocService`.
|
||||
|
||||
---
|
||||
|
||||
## Architettura Target
|
||||
|
||||
```
|
||||
IOBController
|
||||
└── IIocService (scoped)
|
||||
├── FusionCache (L1 Memory + L2 Redis + L3 DB via tags)
|
||||
├── StackExchange.Redis (IDatabase) ← sorgente "live" per dati esterni
|
||||
└── IIocRepository (scoped) ← DB access
|
||||
```
|
||||
|
||||
**Regola chiave**: Redis diretto (scritto da IOB-WIN, altri programmi) resta accessibile via `IDatabase _redisDb` in `IocService`. FusionCache ha una durata equivalente per validare/invalidare coerentemente.
|
||||
|
||||
---
|
||||
|
||||
## Stato Attuale
|
||||
|
||||
### Minimal APIs — OK
|
||||
|
||||
I 3 endpoint in `IobEndpoints.cs` sono stati puliti e ora contengono solo:
|
||||
- `GET api/IOB` / `api/IOB/alive` — health check
|
||||
- `GET api/IOB/enabled/{id}` — usa `await iocService.IobInsEnabAsync(id)`
|
||||
|
||||
I metodi `setCounter` e `getCurrODL` sono stati rimossi (troppo semplici, nel controller bastano).
|
||||
|
||||
### Controller — Misto
|
||||
|
||||
| Usa DService | Usa IOCService |
|
||||
|---|---|
|
||||
| ~37 metodi | 5 metodi (`getCounterTCRec`, `getCurrODL`, `setCounter`, `input`, `getTask2Exe` in parte) |
|
||||
|
||||
### MpDataService
|
||||
|
||||
- Registrato come **singleton** in `Program.cs:106`
|
||||
- Contiene ~80 metodi, molti con accesso Redis diretto senza FusionCache
|
||||
- `IocService` è **scoped** e usa già FusionCache
|
||||
|
||||
### IocService
|
||||
|
||||
- Eredita da `MP.Data.Services.BaseServ` (contiene `GetOrFetchAsync<T>`)
|
||||
- 12 metodi pubblici nell'interface
|
||||
- Usa FusionCache per i metodi più critici (`IobInsEnabAsync`, `GetCurrOdlAsync`, `StatoProdMacchinaAsync`)
|
||||
|
||||
---
|
||||
|
||||
## Piano di Migrazione — Fasi
|
||||
|
||||
### Fase 0: Preparazione (`IocService` base)
|
||||
|
||||
**File**: `IIocService.cs`, `IocService.cs`
|
||||
|
||||
0.1. `IocService` deve ereditare da `MP.Data.Services.BaseServ` (attuale `IocService` non lo fa) — per accedere a `GetOrFetchAsync<T>` condiviso
|
||||
|
||||
0.2. Aggiunta di `IFusionCache _cache` nel constructor (già presente in `BaseServ`)
|
||||
|
||||
0.3. Verifica: `redisLongTimeCache` e `redisShortTimeCache` usati da `IocService` per le durate FusionCache
|
||||
|
||||
---
|
||||
|
||||
### Fase 1: Metodi KeepAlive + Task Management
|
||||
|
||||
**Priorità**: Alta — usati da quasi tutti gli altri metodi come precursori.
|
||||
|
||||
| MpDataService | → IocService | NOTE |
|
||||
|---|---|---|
|
||||
| `ScriviKeepAliveAsync` | ✓ già duplicato in `IocService` | Unificare, aggiungere FusionCache per la check key |
|
||||
| `AddOptPar4Machine` | → `AddOptPar4Machine` | Redis hash, cache per tag machine |
|
||||
| `AddTask4Machine` | → `AddTask4Machine` | Redis hash + backup, cache per tag machine |
|
||||
| `mOptParMacchina` | → `GetOptParMacchina` | Redis hash read |
|
||||
| `mTaskMacchina` / `mTaskMacchinaAsync` | → `GetTaskMacchina` | Redis hash read |
|
||||
| `RemTask2ExeMacchinaAsync` | → `RemoveTask2ExeAsync` | Redis hash write + delete |
|
||||
| `mSavedTaskMacchina` | → `GetSavedTasksMacchina` | Redis hash read |
|
||||
| `AddCheckTask4Machine` | → `AddCheckTask4Machine` | Redis hash read/write |
|
||||
| `AddTask4MacListAsync` | → `AddTaskListAsync` | Redis hash batch write |
|
||||
|
||||
**Durata cache**: `redisShortTimeCache` (2 min + jitter) per task hash (dati volatile)
|
||||
**Tag cache**: `Task:{idxMacchina}`, `OptPar:{idxMacchina}`, `SavedTask:{idxMacchina}`
|
||||
|
||||
---
|
||||
|
||||
### Fase 2: Metodi Process (INPUT, FLOG, ULOG)
|
||||
|
||||
**Priorità**: Altissima — il cuore del sistema, alto traffico.
|
||||
|
||||
| MpDataService | → IocService | NOTE |
|
||||
|---|---|---|
|
||||
| `ProcessInputAsync` | → `ProcessInputAsync` | GIÀ in `IocService` ✓ |
|
||||
| `ProcessFluxLogAsync` | → `ProcessFluxLogAsync` | DB insert + Redis write |
|
||||
| `ProcessUserLogAsync` | → `ProcessUserLogAsync` | DB insert multi-flux |
|
||||
| `CheckMicroStatoAsync` | → `CheckMicroStatoAsync` | DB upsert + transizione |
|
||||
| `scriviRigaEventoAsync` | → `WriteEventRecordAsync` | DB insert |
|
||||
| `saveSigLogAsync` | → `SaveSignalLogAsync` | DB insert |
|
||||
| `GetSrvDtEvent` | → `GetServerDateTime` | Pure logic — no cache |
|
||||
| `ParseEventTime` | → `ParseEventTime` | GIÀ in `IocService` ✓ |
|
||||
| `preProcInput` | → `PreProcessInput` | Pure logic — no cache |
|
||||
| `ValidateInputParams` / `ValidateinputParams` | → `ValidateInputParams` | Pure logic — no cache |
|
||||
|
||||
**Durata cache**: Nessuna (scrittura). Tag invalidazione per `MachineData:{idxMacchina}` sui dati che influenzano.
|
||||
**Tag cache**: `Input:{idxMacchina}`, `FluxLog:{idxMacchina}`, `UserLog:{idxMacchina}`, `MicroStato:{idxMacchina}`
|
||||
|
||||
---
|
||||
|
||||
### Fase 3: Metodi ODL/PODL
|
||||
|
||||
**Priorità**: Alta — letture frequenti durante produzione.
|
||||
|
||||
| MpDataService | → IocService | NOTE |
|
||||
|---|---|---|
|
||||
| `GetCurrOdlAsync` | → `GetCurrOdlAsync` | GIÀ in `IocService` ✓ |
|
||||
| `GetLastOdlAsync` | → `GetLastOdlAsync` | Usare `GetOrFetchAsync<T>` |
|
||||
| `OdlCurrByMaccAsync` | → `GetCurrentOdlAsync` | Usare `GetOrFetchAsync<T>` |
|
||||
| `AutoStartOdlAsync` | → `AutoStartOdlAsync` | Complesso — Redis + DB + eventi |
|
||||
| `FixDailyDossierAsync` | → `FixDailyDossierAsync` | Chiamata batch |
|
||||
| `OdlAutoDayGenAsync` | → `GenerateDailyOdlAsync` | DB stored procedure |
|
||||
| `OdlAutoDayGenFullAsync` | → `GenerateDailyOdlFullAsync` | DB stored procedure |
|
||||
| `POdlGetByKey` | → `GetPodlByKeyAsync` | Usare `GetOrFetchAsync<T>` |
|
||||
| `POdlGetByMaccArtAsync` | → `GetPodlByMachineAsync` | Usare `GetOrFetchAsync<T>` |
|
||||
|
||||
**Durata cache**: `redisLongTimeCache` (5 min) per ODL/PODL
|
||||
**Tag cache**: `Odl:{idxMacchina}`, `Podl:{idxMacchina}`
|
||||
|
||||
---
|
||||
|
||||
### Fase 4: Metodi Contapezzi
|
||||
|
||||
**Priorità**: Alta — traffico continuo.
|
||||
|
||||
| MpDataService | → IocService | NOTE |
|
||||
|---|---|---|
|
||||
| `pzCounter` | → `GetPzCounterAsync` | Redis StringGet (lettura live) |
|
||||
| `PzCounterTcAsync` | → `PzCounterTcAsync` | GIÀ in `IocService` ✓ |
|
||||
| `saveCaricoPezzi` | → `SavePezziCaricoAsync` | Redis StringSet + DB insert |
|
||||
| `SaveCounterAsync` | → `SaveCounterAsync` | GIÀ in `IocService` ✓ |
|
||||
|
||||
**Durata cache**: `redisShortTimeCache` per counter (dati volatile scritti da IOB esterni)
|
||||
**Tag cache**: `PzCounter:{idxMacchina}`
|
||||
|
||||
---
|
||||
|
||||
### Fase 5: Metodi Parametri Macchina
|
||||
|
||||
**Priorità**: Alta — metodo più chiamato (ogni richiesta legge i parametri).
|
||||
|
||||
| MpDataService | → IocService | NOTE |
|
||||
|---|---|---|
|
||||
| `MachineParamListAsync` | → `GetMachineParamsAsync` | **CRITICO** — Redis read, aggiungere FusionCache |
|
||||
| `MachineParamListPendingWriteAsync` | → `GetPendingWriteParamsAsync` | Wrapper su GetMachineParamsAsync |
|
||||
| `MachineParamListSetAsync` | → `SetMachineParamsAsync` | Redis StringSet |
|
||||
| `MachineParamUpsertAsync` | → `UpsertMachineParamsAsync` | Redis read + write batch |
|
||||
| `UpsertCurrObjItemsAsync` | → `UpsertMachineParamsAsync` | Duplicate — unificare con UpsertMachineParamsAsync |
|
||||
|
||||
**Durata cache**: `redisShortTimeCache` (2 min) per parametri macchina — dati scritti da IOB esterni ogni pochi secondi
|
||||
**Tag cache**: `MachineParams:{idxMacchina}`
|
||||
|
||||
---
|
||||
|
||||
### Fase 6: Metodi Anagrafica e Lookup
|
||||
|
||||
**Priorità**: Media — dati relativamente statici.
|
||||
|
||||
| MpDataService | → IocService | NOTE |
|
||||
|---|---|---|
|
||||
| `AnagStatiGetAllAsync` | → `GetAllStatiAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
|
||||
| `ArticoliGetLastByMaccAsync` | → `GetLastArticlesByMachineAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
|
||||
| `DossierLastByMachAsync` | → `GetLastDossiersByMachineAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
|
||||
| `ListValuesFilt` | → `GetListValuesFilteredAsync` | Redis read (duplicare logica FusionCache) |
|
||||
| `DecNumArtGetFiltAsync` | → `GetDecNumArticoliAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
|
||||
| `MacchineGetFilt` | → `GetMachinesFilteredAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
|
||||
| `Macchine2SlaveGetAllAsync` | → `GetMachineSlaveMapAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
|
||||
| `MacchineRecipeArchive` | → `GetMachineRecipeArchivePathAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
|
||||
| `ConfigGetAllAsync` | → `GetAllConfigAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
|
||||
| `ConfFluxMach` | → `GetConfFluxByMachineAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
|
||||
| `GetArtNumAsync` | → `GetArtNumByCodeAsync` | Interno — wrapper a DecNumArtGetFiltAsync |
|
||||
| `MseGetAllAsync` | → `GetMachineStateExplAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
|
||||
|
||||
**Durata cache**: `redisLongTimeCache` (5 min + jitter)
|
||||
**Tag cache**: `AnagStati`, `Articoli:{idxMacchina}`, `Dossier:{idxMacchina}`, `Config`, `MSE`, `Macchine`
|
||||
|
||||
---
|
||||
|
||||
### Fase 7: Metodi Configurazione IOB + Redis Diretto
|
||||
|
||||
**Priorità**: Bassa — configurazione manuale, basso traffico.
|
||||
|
||||
| MpDataService | → IocService | NOTE |
|
||||
|---|---|---|
|
||||
| `SetIobMemMap` | → `SetIobMemMapAsync` | Redis StringSet |
|
||||
| `SetIobConfYamlAsync` | → `SetIobConfYamlAsync` | Redis StringSet |
|
||||
| `SaveMachineIobConf` | → `SaveMachineIobConfAsync` | Redis HashSet |
|
||||
| `SaveMachine2Iob` | → `SaveMachine2IobAsync` | Redis StringSet |
|
||||
| `SaveDataItemsAsync` | → `SaveDataItemsAsync` | Mongo o DB |
|
||||
| `GetCurrObjItems` | → `GetMachineParamsAsync` | **Duplicate** — usare Fase 5 |
|
||||
|
||||
---
|
||||
|
||||
### Fase 8: Metodi Logging e Monitoring
|
||||
|
||||
**Priorità**: Bassa — writing-only, nessun cache.
|
||||
|
||||
| MpDataService | → IocService | NOTE |
|
||||
|---|---|---|
|
||||
| `AlarmInsertAsync` | → `InsertAlarmLogAsync` | DB insert |
|
||||
| `RemRebootLogAddAsync` | → `AddRebootLogAsync` | DB insert + Redis flush |
|
||||
| `WriteEventRecordAsync` (scriviRigaEventoAsync) | → `WriteEventRecordAsync` | DB insert |
|
||||
| `FluxLogSaveSnapshotAsync` | → `SaveFluxSnapshotAsync` | DB stored procedure |
|
||||
| `DossierLastByMachResetAsync` | → `ResetDossierCacheAsync` | Redis delete |
|
||||
| `FluxLogFirstByMachAsync` | → `GetFirstFluxLogsAsync` | GIÀ usa `GetOrFetchAsync<T>` in MpDataService |
|
||||
|
||||
---
|
||||
|
||||
### Fase 9: Metodi Helper e Internal
|
||||
|
||||
**Priorità**: Bassa — usati internamente.
|
||||
|
||||
| MpDataService | → IocService | NOTE |
|
||||
|---|---|---|
|
||||
| `mDatiMacchineAsync` | → `GetMachineDataAsync` | Redis HashGetAll + reset |
|
||||
| `ResetDatiMacchinaAsync` | → `ResetMachineDataAsync` | Redis transaction batch |
|
||||
| `StateMachInByKeyAsync` | → `GetStateMachineIngressiAsync` | Redis Hash |
|
||||
| `resetMSMIAsync` | → `ResetMultiSMIAsync` | Redis Hash write |
|
||||
| `resetSMIAsync` | → `ResetSMIAsync` | Redis Hash write |
|
||||
| `ValoreSmiAsync` | → `GetSMIValueAsync` | Redis HashField get |
|
||||
| `isMulti` | → `IsMultiMachine` | Pure logic |
|
||||
| `ListMasterAsync` / `ListSlaveAsync` | → `GetMasterListAsync` / `GetSlaveListAsync` | Redis cache |
|
||||
| `getRandTOut` | → `GetRandomTimeout` | Shared in BaseServ |
|
||||
| `RedisCountKey`, `RedisDelKey`, `RedisFlushPatternAsync` | → `FlushKeysByPatternAsync` | Admin only |
|
||||
| `RedisGetHashDictAsync`, `RedisSetHashDictAsync` | → `RedisGet/HashDictAsync` | Wrapper utilities |
|
||||
|
||||
---
|
||||
|
||||
## Fase 10: Pulizia Controller
|
||||
|
||||
### 10.1. Sostituire `DService` con `IOCService` in `IOBController`
|
||||
|
||||
Cambiare constructor e tutte le chiamate:
|
||||
|
||||
```csharp
|
||||
// PRIMA
|
||||
public IOBController(IConfiguration configuration, MpDataService DataService, IIocService IService)
|
||||
{
|
||||
DService = DataService;
|
||||
IOCService = IService;
|
||||
}
|
||||
|
||||
// DOPO
|
||||
public IOBController(IIocService service)
|
||||
{
|
||||
_service = service;
|
||||
}
|
||||
```
|
||||
|
||||
Poi sostituire ogni `DService.Xxx(...)` con `_service.Xxx(...)`.
|
||||
|
||||
### 10.2. Rimuovere `MpDataService` dal controller
|
||||
|
||||
- Rimuovere la property `DService`
|
||||
- Rimuovere il parametro dal constructor
|
||||
- Unificare le chiamate miste (alcuni metodi fanno `DService.ScriviKeepAliveAsync` + `IOCService.Xxx`)
|
||||
|
||||
### 10.3. Spostare i private method in `IocService`
|
||||
|
||||
I metodi `processEvListJsonAsync`, `processFLogJsonAsync`, `processULogJsonAsync` contengono logica di business:
|
||||
|
||||
- `processEvListJsonAsync` → `ProcessEventListJsonAsync` in `IocService`
|
||||
- `processFLogJsonAsync` → `ProcessFluxLogJsonAsync` in `IocService` (complesso — usa `MachineParamListAsync` + `UpsertCurrObjItemsAsync`)
|
||||
- `processULogJsonAsync` → `ProcessUserLogJsonAsync` in `IocService`
|
||||
|
||||
### 10.4. Rimuovere `MpDataService` da `Program.cs`
|
||||
|
||||
```csharp
|
||||
// RIMUOVERE:
|
||||
// builder.Services.AddSingleton<MpDataService>();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Riepilogo Metodi da Migrire
|
||||
|
||||
| Categoria | Metodi da aggiungere a IocService |
|
||||
|---|---|
|
||||
| KeepAlive + Task | ~9 metodi |
|
||||
| Process (INPUT/FLOG/ULOG) | ~9 metodi |
|
||||
| ODL/PODL | ~8 metodi |
|
||||
| Contapezzi | ~3 metodi |
|
||||
| Parametri Macchina | ~4 metodi |
|
||||
| Anagrafica/Lookup | ~11 metodi |
|
||||
| Config IOB | ~5 metodi |
|
||||
| Logging | ~6 metodi |
|
||||
| Helper/Internal | ~10 metodi |
|
||||
| **TOTALE** | **~65 metodi** |
|
||||
|
||||
Di questi, ~15 sono già in `IocService` o `BaseServ`. Quindi **~50 nuovi metodi** da implementare.
|
||||
|
||||
---
|
||||
|
||||
## Criteri di Validazione
|
||||
|
||||
1. **Build** — `./build_all_par.ps1 --agent` senza errori
|
||||
2. **Functional parity** — ogni endpoint del controller restituisce lo stesso output di prima
|
||||
3. **Cache behavior** — i metodi migrati registrano `data.source` come TRACE/DEBUG
|
||||
4. **No `MpDataService` references** nel controller
|
||||
5. **`Program.cs`** — `MpDataService` rimosso dalla DI registration
|
||||
|
||||
---
|
||||
|
||||
## Sequenza di Sviluppo Consigliata
|
||||
|
||||
```
|
||||
Fase 1 → Fase 4: Blocchi "hot" (keepalive, task, counter)
|
||||
Fase 5: Parametri macchina (blocco più critico per perf)
|
||||
Fase 2: Process (INPUT/FLOG/ULOG - cuore dell'app)
|
||||
Fase 3: ODL/PODL
|
||||
Fase 6: Anagrafica (dati statici, meno rischioso)
|
||||
Fase 7-9: Config + Logging + Helper
|
||||
Fase 10: Cleanup finale
|
||||
```
|
||||
|
||||
Ogni fase va build-ata e testata prima di procedere alla successiva.
|
||||
|
||||
---
|
||||
|
||||
## Note Tecniche
|
||||
|
||||
### Caching su Redis diretto
|
||||
|
||||
Per i dati scritti da programmi esterni (IOB-WIN, ecc.), il pattern è:
|
||||
|
||||
```
|
||||
1. Lettura: _redisDb.StringGet/HashGet (dati "live")
|
||||
2. Scrittura: programmi esterni scrivono in Redis direttamente
|
||||
3. FusionCache: durata pari a redisShortTimeCache o redisLongTimeCache
|
||||
- Il backplane Redis invalida L1 negli altri worker
|
||||
- Se un programma esterno scrive, la prossima lettura dal controller fa cache-miss
|
||||
```
|
||||
|
||||
Questo è coerente con l'architettura attuale: il controller legge Redis (sorgente di verità) e FusionCache avvolge per ridurre i round-trip.
|
||||
|
||||
### Tagging Strategy
|
||||
|
||||
```
|
||||
MachineData:{idxMacchina} — dati macchina generali (ResetDatiMacchina)
|
||||
MachineParams:{idxMacchina} — parametri macchina (MachineParamListAsync)
|
||||
Task:{idxMacchina} — task da eseguire
|
||||
PzCounter:{idxMacchina} — contapezzi
|
||||
Odl:{idxMacchina} — ODL correnti
|
||||
Podl:{idxMacchina} — PODL
|
||||
Input:{idxMacchina} — log input
|
||||
FluxLog:{idxMacchina} — log flux
|
||||
UserLog:{idxMacchina} — log user
|
||||
```
|
||||
|
||||
### Transizioni di Scope
|
||||
|
||||
`MpDataService` è singleton → `IIocService` è scoped. Questo va bene per le minimal APIs (che iniettano `IIocService` correttamente). Per il controller, dopo la rimozione di `MpDataService`, il controller diventerà scoped di default (già lo è per `[ApiController]`).
|
||||
|
||||
### IocService constructor
|
||||
|
||||
Attuale (da `MP.Data.Services.IOC.IocService`):
|
||||
```csharp
|
||||
public IocService(IConfiguration config, IConnectionMultiplexer redis,
|
||||
IFusionCache cache, IIocRepository repo,
|
||||
IServiceScopeFactory scopeFactory)
|
||||
```
|
||||
|
||||
Da `MP.Data.Services.BaseServ`:
|
||||
```csharp
|
||||
public BaseServ(IConfiguration configuration, IFusionCache cache, IConnectionMultiplexer redConn)
|
||||
```
|
||||
|
||||
La gerarchia sarà: `IocService` estende `BaseServ` e il constructor chiama `base(configuration, cache, redConn)`.
|
||||
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>MP.Land</RootNamespace>
|
||||
<Version>8.16.2606.1117</Version>
|
||||
<Version>8.16.2606.1312</Version>
|
||||
<Configurations>Debug;Release;Debug_LiManDebug</Configurations>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
<RunAnalyzersDuringBuild>True</RunAnalyzersDuringBuild>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo Tablet MAPO - DotNet6</i>
|
||||
<h4>Versione: 8.16.2606.1117</h4>
|
||||
<h4>Versione: 8.16.2606.1312</h4>
|
||||
<br />
|
||||
Note di rilascio:
|
||||
<ul>
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.16.2606.1117
|
||||
8.16.2606.1312
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>8.16.2606.1117</version>
|
||||
<version>8.16.2606.1312</version>
|
||||
<url>https://nexus.steamware.net/repository/SWS/MP-LAND/stable/LAST/MP.Land.zip</url>
|
||||
<changelog>https://nexus.steamware.net/repository/SWS/MP-LAND/stable/LAST/ChangeLog.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>MP.MON</RootNamespace>
|
||||
<AssemblyName>$(AssemblyName.Replace(' ', '_'))</AssemblyName>
|
||||
<Version>8.16.2606.1117</Version>
|
||||
<Version>8.16.2606.1312</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo MAPOSPEC </i>
|
||||
<h4>Versione: 8.16.2606.1117</h4>
|
||||
<h4>Versione: 8.16.2606.1312</h4>
|
||||
<br /> Note di rilascio:
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.16.2606.1117
|
||||
8.16.2606.1312
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>8.16.2606.1117</version>
|
||||
<version>8.16.2606.1312</version>
|
||||
<url>https://nexus.steamware.net/repository/SWS/MP-MON/stable/LAST/MP.MON.zip</url>
|
||||
<changelog>https://nexus.steamware.net/repository/SWS/MP-MON/stable/LAST/ChangeLog.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>MP.Prog</RootNamespace>
|
||||
<Version>8.16.2606.1117</Version>
|
||||
<Version>8.16.2606.1312</Version>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo gestione Programmi MAPO</i>
|
||||
<h4>Versione: 8.16.2606.1117</h4>
|
||||
<h4>Versione: 8.16.2606.1312</h4>
|
||||
<br />
|
||||
Note di rilascio:
|
||||
<ul>
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.16.2606.1117
|
||||
8.16.2606.1312
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>8.16.2606.1117</version>
|
||||
<version>8.16.2606.1312</version>
|
||||
<url>https://nexus.steamware.net/repository/SWS/MP-PROG/stable/LAST/MP.Prog.zip</url>
|
||||
<changelog>https://nexus.steamware.net/repository/SWS/MP-PROG/stable/LAST/ChangeLog.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>MP.RIOC</RootNamespace>
|
||||
<Version>8.16.2606.1117</Version>
|
||||
<Version>8.16.2606.1312</Version>
|
||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
+35
-3
@@ -10,14 +10,30 @@ using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
// Forza il ThreadPool a tenere pronti almeno 500 thread per i calcoli e 500 per l'I/O di rete
|
||||
ThreadPool.SetMinThreads(500, 500);
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// recupero env corrente
|
||||
// RECUPERO L'AMBIENTE REALE (Che ora IIS passa correttamente come 'Staging')
|
||||
var env = builder.Environment;
|
||||
|
||||
// FORZA IL CARICAMENTO CORRETTO DEI JSON CON LA GERARCHIA DI AMBIENTE
|
||||
builder.Configuration
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true)
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
// recupero env corrente
|
||||
var logger = LogManager.Setup()
|
||||
.LoadConfigurationFromAppSettings()
|
||||
.GetCurrentClassLogger();
|
||||
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Host.UseNLog();
|
||||
|
||||
var assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString();
|
||||
logger.Info($"MP.RIOC | Program.cs: startup | v.{assemblyVersion}");
|
||||
logger.Info($"Current ASPNETCORE_ENVIRONMENT: {env.EnvironmentName}");
|
||||
@@ -57,23 +73,35 @@ builder.Services.AddDbContextFactory<MoonPro_UtilsContext>(options =>
|
||||
builder.Services.AddIocDataLayer();
|
||||
|
||||
// 1. Configurazione dell'invoker personalizzato (Risolve i tuoi errori)
|
||||
// 1. Configurazione dell'invoker personalizzato (Potenziato con il Pooling)
|
||||
var httpClientInvoker = new HttpMessageInvoker(new SocketsHttpHandler
|
||||
{
|
||||
UseProxy = false,
|
||||
AllowAutoRedirect = false,
|
||||
AutomaticDecompression = DecompressionMethods.None,
|
||||
UseCookies = false,
|
||||
// Correzione per il tracing: usa il propagatore corrente di sistema
|
||||
ActivityHeadersPropagator = DistributedContextPropagator.Current,
|
||||
ConnectTimeout = TimeSpan.FromSeconds(60),
|
||||
|
||||
// Gestione certificato (ignora errori per localhost/test)
|
||||
// ==================================================================
|
||||
// 🚀 LE TRE RIGHE PER ABBATTERE IL MURO DEI 2 SECONDI SOTTO STRESS
|
||||
// ==================================================================
|
||||
// Permette a YARP di aprire fino a 5000 canali paralleli contemporanei verso IIS
|
||||
MaxConnectionsPerServer = 5000,
|
||||
|
||||
// Tiene caldi i socket ed evita di rifare l'handshake TCP/TLS a ogni richiesta
|
||||
PooledConnectionLifetime = TimeSpan.FromMinutes(15),
|
||||
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2),
|
||||
// ==================================================================
|
||||
|
||||
// Gestione certificato (Invariata)
|
||||
SslOptions = new System.Net.Security.SslClientAuthenticationOptions
|
||||
{
|
||||
RemoteCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
|
||||
}
|
||||
});
|
||||
|
||||
// Registrazione nella Dependency Injection (Invariata)
|
||||
builder.Services.AddSingleton(httpClientInvoker);
|
||||
builder.Services.AddHttpForwarder();
|
||||
|
||||
@@ -161,6 +189,10 @@ app.MapWhen(ctx => ctx.Request.Path.StartsWithSegments(routePath, StringComparis
|
||||
});
|
||||
});
|
||||
|
||||
// test per ambiente di esecuzione InProcess...
|
||||
app.MapGet("api/alive", () =>
|
||||
$"OK - Girando in: {System.Diagnostics.Process.GetCurrentProcess().ProcessName}");
|
||||
|
||||
// 6. Definizione degli Endpoints locali
|
||||
app.MapRazorPages();
|
||||
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<WebPublishMethod>MSDeploy</WebPublishMethod>
|
||||
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
|
||||
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
||||
<LastUsedPlatform>Any CPU</LastUsedPlatform>
|
||||
<SiteUrlToLaunchAfterPublish>https://iis01.egalware.com/MP/RIOC/</SiteUrlToLaunchAfterPublish>
|
||||
<ExcludeApp_Data>false</ExcludeApp_Data>
|
||||
<ProjectGuid>b9188473-f4ae-4f9f-be2d-70edaace0db9</ProjectGuid>
|
||||
<SelfContained>false</SelfContained>
|
||||
<MSDeployServiceURL>https://iis01.egalware.com:8172/MsDeploy.axd</MSDeployServiceURL>
|
||||
<DeployIisAppPath>Default Web Site/MP/RIOC</DeployIisAppPath>
|
||||
<RemoteSitePhysicalPath />
|
||||
<SkipExtraFilesOnServer>true</SkipExtraFilesOnServer>
|
||||
<MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
|
||||
<EnableMSDeployBackup>true</EnableMSDeployBackup>
|
||||
<EnableMsDeployAppOffline>true</EnableMsDeployAppOffline>
|
||||
<UserName>jenkins</UserName>
|
||||
<_SavePWD>true</_SavePWD>
|
||||
<_TargetId>IISWebDeploy</_TargetId>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<MsDeploySkipRules Include="SkipLogFiles">
|
||||
<ObjectName>filePath</ObjectName>
|
||||
<AbsolutePath>logs\\.*\.log$</AbsolutePath>
|
||||
</MsDeploySkipRules>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<WebPublishMethod>MSDeploy</WebPublishMethod>
|
||||
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
|
||||
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
||||
<LastUsedPlatform>Any CPU</LastUsedPlatform>
|
||||
<SiteUrlToLaunchAfterPublish>https://iis01.egalware.com/MP/RIOC/</SiteUrlToLaunchAfterPublish>
|
||||
<ExcludeApp_Data>false</ExcludeApp_Data>
|
||||
<ProjectGuid>b9188473-f4ae-4f9f-be2d-70edaace0db9</ProjectGuid>
|
||||
<SelfContained>false</SelfContained>
|
||||
<MSDeployServiceURL>https://iis01.egalware.com:8172/MsDeploy.axd</MSDeployServiceURL>
|
||||
<DeployIisAppPath>Default Web Site/MP/RIOC</DeployIisAppPath>
|
||||
<RemoteSitePhysicalPath />
|
||||
<SkipExtraFilesOnServer>true</SkipExtraFilesOnServer>
|
||||
<MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
|
||||
<EnableMSDeployBackup>true</EnableMSDeployBackup>
|
||||
<EnableMsDeployAppOffline>true</EnableMsDeployAppOffline>
|
||||
<UserName>jenkins</UserName>
|
||||
<_SavePWD>true</_SavePWD>
|
||||
<_TargetId>IISWebDeploy</_TargetId>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<PublishReadyToRun>true</PublishReadyToRun>
|
||||
<AllowUntrustedCertificate>True</AllowUntrustedCertificate>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<MsDeploySkipRules Include="SkipLogFiles">
|
||||
<ObjectName>filePath</ObjectName>
|
||||
<AbsolutePath>logs\\.*\.log$</AbsolutePath>
|
||||
</MsDeploySkipRules>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo MP-RIOC </i>
|
||||
<h4>Versione: 8.16.2606.1117</h4>
|
||||
<h4>Versione: 8.16.2606.1312</h4>
|
||||
<br /> Note di rilascio:
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.16.2606.1117
|
||||
8.16.2606.1312
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>8.16.2606.1117</version>
|
||||
<version>8.16.2606.1312</version>
|
||||
<url>https://nexus.steamware.net/repository/SWS/MP-RIOC/stable/LAST/MP.RIOC.zip</url>
|
||||
<changelog>https://nexus.steamware.net/repository/SWS/MP-RIOC/stable/LAST/ChangeLog.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
@@ -33,8 +33,23 @@ namespace MP.RIOC.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
await ProcessDayLiveMetricsAsync();
|
||||
await ProcessHourLiveMetricsAsync();
|
||||
//await ProcessDayLiveMetricsAsync();
|
||||
//await ProcessHourLiveMetricsAsync();
|
||||
|
||||
// 2. 🚀 COSTRINGIAMO IL FLUSH A GIRARE IN BACKGROUND
|
||||
// Task.Run prende l'intera esecuzione e la sposta su un thread separato.
|
||||
// In questo modo, anche se l'Upsert fa calcoli pesanti o blocca momentaneamente,
|
||||
// i thread principali di YARP rimangono liberi al 100% di rispondere a wrk in 2ms.
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
// Cede immediatamente il controllo per evitare di monopolizzare il thread corrente
|
||||
await Task.Yield();
|
||||
|
||||
// Esegue i tuoi due metodi (che manterranno i loro scope interni asincroni)
|
||||
await ProcessDayLiveMetricsAsync();
|
||||
await ProcessHourLiveMetricsAsync();
|
||||
}, stoppingToken);
|
||||
// poi attendo...
|
||||
await Task.Delay(TimeSpan.FromSeconds(interval), stoppingToken);
|
||||
}
|
||||
catch (TaskCanceledException) { break; }
|
||||
@@ -69,20 +84,27 @@ namespace MP.RIOC.Services
|
||||
/// <summary>
|
||||
/// Cancellazione ricorsiva chiavi ausiliarie (:status, :errors) e rimozione dall'indice
|
||||
/// </summary>
|
||||
private async Task DeleteAuxKeysAndIndexAsync(RedisKey sKey, RedisKey indexKey, IBatch batch)
|
||||
private async Task DeleteAuxKeysAndIndexAsync(RedisKey sKey, RedisKey indexKey, IBatch batch, List<Task> pendingOps)
|
||||
{
|
||||
string sKeyStr = sKey.ToString();
|
||||
string keyDir = sKeyStr.Substring(0, sKeyStr.LastIndexOf(':'));
|
||||
|
||||
// Cancella :status dal sorted set e dalla hash
|
||||
string statusKey = keyDir + ":status";
|
||||
await batch.SortedSetRemoveAsync(indexKey, statusKey);
|
||||
await batch.KeyDeleteAsync(statusKey);
|
||||
|
||||
try
|
||||
{
|
||||
pendingOps.Add(batch.SortedSetRemoveAsync(indexKey, statusKey));
|
||||
pendingOps.Add(batch.KeyDeleteAsync(statusKey));
|
||||
}
|
||||
catch { }
|
||||
// Cancella :errors dal sorted set e dalla hash
|
||||
string errorKey = keyDir + ":errors";
|
||||
await batch.SortedSetRemoveAsync(indexKey, errorKey);
|
||||
await batch.KeyDeleteAsync(errorKey);
|
||||
try
|
||||
{
|
||||
pendingOps.Add(batch.SortedSetRemoveAsync(indexKey, errorKey));
|
||||
pendingOps.Add(batch.KeyDeleteAsync(errorKey));
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Cancella chiave ausiliaria :status anche da un eventuale indice days se presente
|
||||
if (statusKey.Contains(":stats:hours:"))
|
||||
@@ -90,7 +112,7 @@ namespace MP.RIOC.Services
|
||||
string daysIndex = statusKey.Replace(":stats:hours:", ":stats:days:");
|
||||
try
|
||||
{
|
||||
await batch.SortedSetRemoveAsync(daysIndex, statusKey);
|
||||
pendingOps.Add(batch.SortedSetRemoveAsync(daysIndex, statusKey));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
@@ -101,12 +123,13 @@ namespace MP.RIOC.Services
|
||||
string daysIndex = errorKey.Replace(":stats:hours:", ":stats:days:");
|
||||
try
|
||||
{
|
||||
await batch.SortedSetRemoveAsync(daysIndex, errorKey);
|
||||
pendingOps.Add(batch.SortedSetRemoveAsync(daysIndex, errorKey));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
#if false
|
||||
/// <summary>
|
||||
/// Recupera il TTL residuo di una chiave Redis (-1 = nessun TTL, -2 = chiave non esiste)
|
||||
/// </summary>
|
||||
@@ -117,7 +140,8 @@ namespace MP.RIOC.Services
|
||||
return _db.KeyTimeToLive(key);
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Processing dati giornalieri (da Redis a DB)
|
||||
@@ -150,6 +174,7 @@ namespace MP.RIOC.Services
|
||||
};
|
||||
|
||||
var batch = _db.CreateBatch();
|
||||
var pendingOps = new List<Task>();
|
||||
foreach (var pattern in patternsToScan)
|
||||
{
|
||||
// Nota: KeyScanAsync/KeysAsync e' disponibile su IServer
|
||||
@@ -165,21 +190,34 @@ namespace MP.RIOC.Services
|
||||
var sKey = (RedisKey)$"{statKey}";
|
||||
if (!TryParseKeyMetadata(sKey, out var meta) || meta.IsHourType) continue;
|
||||
|
||||
// Verifica se la chiave è "orfana" (nessun TTL o TTL troppo lungo >30gg)
|
||||
// Verifica se la chiave è "orfana"
|
||||
bool isOrphanKey = meta.Timestamp < currentDayStart;
|
||||
#if false
|
||||
bool isOrphanKey = false;
|
||||
var keyTtl = GetKeyTtl(sKey);
|
||||
bool isOrphanKey = keyTtl?.TotalSeconds < 0 || keyTtl?.TotalHours > 30.25 * 24;
|
||||
if (keyTtl == null)
|
||||
{
|
||||
isOrphanKey = meta.Timestamp < currentDayStart;
|
||||
}
|
||||
else
|
||||
{
|
||||
isOrphanKey = keyTtl.Value.TotalSeconds < 0 || keyTtl.Value.TotalDays < 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Se era scaduta o orfana e abbiamo il permesso, segnamola per la cancellazione
|
||||
if ((meta.Timestamp < currentDayStart || isOrphanKey) && deleteConfirmed)
|
||||
// Se è orfana e abbiamo il permesso, segnamola per la cancellazione
|
||||
// NOTA: non usiamo il confronto con currentDayStart perché taglierebbe fuori
|
||||
// i dati delle ore/giorni precedenti che devono essere ancora processati dal DB
|
||||
if (isOrphanKey && deleteConfirmed)
|
||||
{
|
||||
// 1. Segna la chiave Hash (Dati) per l'eliminazione
|
||||
keysToDelete.Add(sKey);
|
||||
|
||||
// 2. Rimuovi il riferimento dal Sorted Set (Indice)
|
||||
await batch.SortedSetRemoveAsync(indexKey, statKey);
|
||||
pendingOps.Add(batch.SortedSetRemoveAsync(indexKey, statKey));
|
||||
|
||||
// 3. Cancellazione ricorsiva delle chiavi ausiliarie (:status, :errors, :days)
|
||||
await DeleteAuxKeysAndIndexAsync(sKey, indexKey, batch);
|
||||
await DeleteAuxKeysAndIndexAsync(sKey, indexKey, batch, pendingOps);
|
||||
}
|
||||
|
||||
// Recupero dati dalla Hash
|
||||
@@ -226,6 +264,8 @@ namespace MP.RIOC.Services
|
||||
}
|
||||
}
|
||||
batch.Execute();
|
||||
// attendo conclusione
|
||||
await Task.WhenAll(pendingOps);
|
||||
}
|
||||
|
||||
// --- FASE UPSERT DB ---
|
||||
@@ -241,13 +281,16 @@ namespace MP.RIOC.Services
|
||||
if (deleteConfirmed && keysToDelete.Any())
|
||||
{
|
||||
var batch = _db.CreateBatch();
|
||||
var pendingOps = new List<Task>();
|
||||
int deletedCount = 0;
|
||||
foreach (var key in keysToDelete)
|
||||
{
|
||||
await batch.KeyDeleteAsync(key);
|
||||
pendingOps.Add(batch.KeyDeleteAsync(key));
|
||||
deletedCount++;
|
||||
}
|
||||
batch.Execute();
|
||||
// attendo conclusione
|
||||
await Task.WhenAll(pendingOps);
|
||||
Log.Info($"[CLEANUP DAY] Deleted {deletedCount} expired metric keys from Redis");
|
||||
}
|
||||
}
|
||||
@@ -284,6 +327,7 @@ namespace MP.RIOC.Services
|
||||
};
|
||||
|
||||
var batch = _db.CreateBatch();
|
||||
var pendingOps = new List<Task>();
|
||||
foreach (var pattern in patternsToScan)
|
||||
{
|
||||
// Nota: KeyScanAsync/KeysAsync e' disponibile su IServer
|
||||
@@ -299,21 +343,36 @@ namespace MP.RIOC.Services
|
||||
var sKey = (RedisKey)$"{statKey}";
|
||||
if (!TryParseKeyMetadata(sKey, out var meta) || !meta.IsHourType) continue;
|
||||
|
||||
// Verifica se la chiave è "orfana" (nessun TTL o TTL troppo lungo >30gg)
|
||||
// Verifica se la chiave è "orfana"
|
||||
bool isOrphanKey = meta.Timestamp < currentHourStart;
|
||||
#if false
|
||||
bool isOrphanKey = false;
|
||||
var keyTtl = GetKeyTtl(sKey);
|
||||
bool isOrphanKey = keyTtl?.TotalSeconds < 0 || keyTtl?.TotalHours > 30.25 * 24;
|
||||
if (keyTtl == null)
|
||||
{
|
||||
// Nessun TTL recuperato = chiave senza scadenza = orfana se > 7 gg
|
||||
DateTime cutoffHour = DateTime.Now.AddDays(-7);
|
||||
isOrphanKey = meta.Timestamp < cutoffHour;
|
||||
}
|
||||
else
|
||||
{
|
||||
isOrphanKey = keyTtl.Value.TotalSeconds < 0 || keyTtl.Value.TotalDays < 15;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Se era scaduta o orfana e abbiamo il permesso, segnamola per la cancellazione
|
||||
if ((meta.Timestamp < currentHourStart || isOrphanKey) && deleteConfirmed)
|
||||
// Se è orfana e abbiamo il permesso, segnamola per la cancellazione
|
||||
// NOTA: non usiamo il confronto con currentHourStart perché taglierebbe fuori
|
||||
// i dati delle ore precedenti che devono essere ancora processati dal DB
|
||||
if (isOrphanKey && deleteConfirmed)
|
||||
{
|
||||
// 1. Segna la chiave Hash (Dati) per l'eliminazione
|
||||
keysToDelete.Add(sKey);
|
||||
|
||||
// 2. Rimuovi il riferimento dal Sorted Set (Indice)
|
||||
await batch.SortedSetRemoveAsync(indexKey, statKey);
|
||||
pendingOps.Add(batch.SortedSetRemoveAsync(indexKey, statKey));
|
||||
|
||||
// 3. Cancellazione ricorsiva chiavi ausiliarie
|
||||
await DeleteAuxKeysAndIndexAsync(sKey, indexKey, batch);
|
||||
await DeleteAuxKeysAndIndexAsync(sKey, indexKey, batch, pendingOps);
|
||||
}
|
||||
|
||||
// Recupero dati dalla Hash
|
||||
@@ -326,7 +385,6 @@ namespace MP.RIOC.Services
|
||||
dict.TryGetValue("totalMs", out var totalMsStr))
|
||||
{
|
||||
long count = long.Parse(countStr);
|
||||
count = long.Parse(countStr);
|
||||
double totalMs = double.Parse(totalMsStr, CultureInfo.InvariantCulture);
|
||||
|
||||
double maxMs = dict.ContainsKey("maxMs") ? double.Parse(dict["maxMs"], CultureInfo.InvariantCulture) : 0;
|
||||
@@ -360,6 +418,8 @@ namespace MP.RIOC.Services
|
||||
}
|
||||
}
|
||||
batch.Execute();
|
||||
// attendo conclusione
|
||||
await Task.WhenAll(pendingOps);
|
||||
}
|
||||
|
||||
// --- FASE UPSERT DB ---
|
||||
@@ -375,13 +435,16 @@ namespace MP.RIOC.Services
|
||||
if (deleteConfirmed && keysToDelete.Count > 0)
|
||||
{
|
||||
var batch = _db.CreateBatch();
|
||||
var pendingOps = new List<Task>();
|
||||
int deletedCount = 0;
|
||||
foreach (var key in keysToDelete)
|
||||
{
|
||||
await batch.KeyDeleteAsync(key);
|
||||
pendingOps.Add(batch.KeyDeleteAsync(key));
|
||||
deletedCount++;
|
||||
}
|
||||
batch.Execute();
|
||||
// attendo conclusione
|
||||
await Task.WhenAll(pendingOps);
|
||||
Log.Info($"[CLEANUP HOUR] Deleted {deletedCount} expired metric keys from Redis");
|
||||
}
|
||||
}
|
||||
@@ -396,7 +459,7 @@ namespace MP.RIOC.Services
|
||||
var p = relativeKey.Split(':');
|
||||
if (p.Length < 4) return false;
|
||||
|
||||
meta.IsHourType = p[1].Equals("hour", StringComparison.InvariantCultureIgnoreCase);
|
||||
meta.IsHourType = p[1].Contains("hour", StringComparison.InvariantCultureIgnoreCase);
|
||||
meta.Dest = p[2];
|
||||
if (meta.IsHourType)
|
||||
{
|
||||
|
||||
@@ -98,9 +98,17 @@ namespace MP.RIOC.Services
|
||||
|
||||
// ESECUZIONE FORWARDING
|
||||
var error = await _forwarder.SendAsync(context, destBase, _httpClientInvoker, _forwarderConfig, HttpTransformer.Default, context.RequestAborted);
|
||||
// commento transformer custom
|
||||
//var error = await _forwarder.SendAsync(context, destBase, _httpClientInvoker, _forwarderConfig, _transformer, context.RequestAborted);
|
||||
|
||||
// Se YARP fallisce per un problema di connessione interrotta (come l'errore 995)
|
||||
// e NON è un problema di sintassi della richiesta o di autorizzazione
|
||||
if (error != ForwarderError.None &&
|
||||
error != ForwarderError.RequestBodyClient &&
|
||||
error != ForwarderError.RequestCanceled &&
|
||||
error != ForwarderError.NoAvailableDestinations)
|
||||
{
|
||||
// Fai un secondo e ultimo tentativo immediato prima di dare errore al client
|
||||
Log.Warn($"Micro-reset di rete rilevato ({error}). Tento il secondo invio immediato...");
|
||||
error = await _forwarder.SendAsync(context, destBase, _httpClientInvoker, _forwarderConfig, HttpTransformer.Default, context.RequestAborted);
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
_stats.RecordDuration(sKey, sw.Elapsed);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Yarp": "Warning",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Yarp": "Warning",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning",
|
||||
"Yarp": "Warning",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Microsoft.AspNetCore.Hosting.Diagnostics": "Warning"
|
||||
}
|
||||
},
|
||||
"NLog": {
|
||||
"rules": [
|
||||
{
|
||||
"logger": "Microsoft.*",
|
||||
"maxLevel": "Fatal",
|
||||
"final": true
|
||||
},
|
||||
{
|
||||
"logger": "Yarp.*",
|
||||
"maxLevel": "Fatal",
|
||||
"final": true
|
||||
},
|
||||
{
|
||||
"logger": "*",
|
||||
"minLevel": "Info",
|
||||
"writeTo": "logconsole"
|
||||
},
|
||||
{
|
||||
"logger": "*",
|
||||
"minLevel": "Info",
|
||||
"writeTo": "logfile"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning",
|
||||
"Yarp": "Warning",
|
||||
"Microsoft.EntityFrameworkCore": "Warning",
|
||||
"Microsoft.AspNetCore.Hosting.Diagnostics": "Information"
|
||||
}
|
||||
},
|
||||
"NLog": {
|
||||
"rules": [
|
||||
{
|
||||
"logger": "Microsoft.AspNetCore.Hosting.Diagnostics",
|
||||
"maxLevel": "Info",
|
||||
"finalMinLevel": "Info"
|
||||
},
|
||||
{
|
||||
"logger": "*",
|
||||
"minLevel": "Info",
|
||||
"writeTo": "logfile"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning",
|
||||
"Yarp": "Warning",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Microsoft.AspNetCore.Hosting.Diagnostics": "Warning"
|
||||
}
|
||||
},
|
||||
"NLog": {
|
||||
"rules": [
|
||||
{
|
||||
"logger": "Microsoft.*",
|
||||
"maxLevel": "Fatal",
|
||||
"final": true
|
||||
},
|
||||
{
|
||||
"logger": "Yarp.*",
|
||||
"maxLevel": "Fatal",
|
||||
"final": true
|
||||
},
|
||||
{
|
||||
"logger": "*",
|
||||
"minLevel": "Info",
|
||||
"writeTo": "logconsole"
|
||||
},
|
||||
{
|
||||
"logger": "*",
|
||||
"minLevel": "Info",
|
||||
"writeTo": "logfile"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,11 @@
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Yarp": "Debug",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Yarp": "Information",
|
||||
"Microsoft.AspNetCore.Hosting.Diagnostics": "Information",
|
||||
"Microsoft.EntityFrameworkCore.Database.Command": "Warning",
|
||||
"Microsoft.EntityFrameworkCore": "Warning",
|
||||
"Microsoft.EntityFrameworkCore.Database.Command": "Warning"
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
@@ -24,7 +25,7 @@
|
||||
"logfile": {
|
||||
"type": "File",
|
||||
"fileName": "${basedir}/logs/${shortdate}.log",
|
||||
"keepFileOpen": false,
|
||||
"keepFileOpen": true,
|
||||
"archiveEvery": "Day",
|
||||
"archiveFileName": "${basedir}/logs/old/${shortdate}_{#}.log",
|
||||
"archiveNumbering": "DateAndSequence",
|
||||
@@ -39,9 +40,24 @@
|
||||
}
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"logger": "Microsoft.EntityFrameworkCore.*",
|
||||
"maxLevel": "Info",
|
||||
"final": true
|
||||
},
|
||||
{
|
||||
"logger": "Microsoft.AspNetCore.Hosting.*",
|
||||
"minLevel": "Info",
|
||||
"finalMinLevel": "Info"
|
||||
},
|
||||
{
|
||||
"logger": "Microsoft.AspNetCore.*",
|
||||
"maxLevel": "Info",
|
||||
"finalMinLevel": "Info"
|
||||
},
|
||||
{
|
||||
"logger": "*",
|
||||
"minLevel": "Trace",
|
||||
"minLevel": "Info",
|
||||
"writeTo": "logconsole"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.JSInterop;
|
||||
using MP.AppAuth.Services;
|
||||
using MP.Data.DbModels;
|
||||
using MP.SPEC.Data;
|
||||
using MP.SPEC.Extensions;
|
||||
|
||||
@@ -52,6 +53,14 @@ namespace MP.SPEC.Components
|
||||
|
||||
protected async Task FlushCache()
|
||||
{
|
||||
|
||||
// svuoto cache redis...
|
||||
ConfigModel updRec = new ConfigModel()
|
||||
{
|
||||
Chiave = "AZIENDA",
|
||||
Valore = "*"
|
||||
};
|
||||
await MDService.ConfigUpdateAsync(updRec);
|
||||
await MDService.ForceFlushRedisCache();
|
||||
await MDService.ForceFlushFusionCacheAsync();
|
||||
await ForceReload();
|
||||
|
||||
@@ -169,6 +169,7 @@ namespace MP.SPEC.Components.ProdKit
|
||||
protected async Task toggleClosed()
|
||||
{
|
||||
hasOdl = !hasOdl;
|
||||
currPage = 1;
|
||||
await EC_HasOdl.InvokeAsync(hasOdl);
|
||||
}
|
||||
|
||||
|
||||
@@ -220,12 +220,6 @@ namespace MP.SPEC.Components.ProdKit
|
||||
set => ActFilter.CurrPage = value;
|
||||
}
|
||||
|
||||
private bool hasOdl
|
||||
{
|
||||
get => ActFilter.HasOdl;
|
||||
set => ActFilter.HasOdl = value;
|
||||
}
|
||||
|
||||
private bool isLoading { get; set; } = false;
|
||||
|
||||
private SelectXdlParams lastFilter { get; set; } = new SelectXdlParams() { CurrPage = -1 };
|
||||
|
||||
@@ -54,10 +54,10 @@
|
||||
</table>
|
||||
}
|
||||
<div runat="server" id="divPODL">
|
||||
<b>Elenco promesse per KIT (100%)</b>
|
||||
<b>Elenco promesse KIT (100%)</b> da produrre
|
||||
@if (ListRecPODL == null || ListRecPODL.Count == 0)
|
||||
{
|
||||
<div class="alert bg-success bg-opacity-25 fw-bold text-center">Nessuna PODL compatibile presente</div>
|
||||
<div class="alert bg-success bg-opacity-25 fw-bold text-center">Nessuna PODL KIT presente</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -188,7 +188,7 @@ namespace MP.SPEC.Components.ProdKit
|
||||
{
|
||||
listPOdlCheck = new List<PODLExpModel>();
|
||||
listPOdlAct = await MDService.POdlListGetFiltAsync(ActFilt.HasOdl, ActFilt.CodFase, ActFilt.IdxMacchina, ActFilt.CodReparto, ActFilt.DtStart, ActFilt.DtEnd);
|
||||
listPOdl2Kit = await MDService.POdlToKitListGetFiltAsync(ActFilt.HasOdl, ActFilt.CodFase, ActFilt.IdxMacchina, ActFilt.CodReparto, ActFilt.DtStart, ActFilt.DtEnd, true, true);
|
||||
listPOdl2Kit = await MDService.POdlToKitListGetFiltAsync(false, ActFilt.CodFase, ActFilt.IdxMacchina, ActFilt.CodReparto, ActFilt.DtStart, ActFilt.DtEnd, true, false);
|
||||
listWSM = await MDService.WipKitFiltAsync(keyFilt);
|
||||
listTSM = await MDService.TksScoreAsync(keyFilt, 1000, true);
|
||||
listIKP = await MDService.IstKitFiltAsync("", "");
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (!isActive || hasOdl)
|
||||
@if (true || !isActive || hasOdl)
|
||||
{
|
||||
<div class="small mt-2">
|
||||
<label class="px-2" for="dtMin" title="Selezionare inizio periodo">Inizio Periodo</label>
|
||||
|
||||
@@ -1926,7 +1926,7 @@ namespace MP.SPEC.Data
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "TksScoreAsync",
|
||||
cacheKey: currKey,
|
||||
expiration: TimeSpan.FromMinutes(redisLongTimeCache),
|
||||
expiration: GetRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () => await _productionRepository.TksScoreAsync(KeyFilt, MaxResult) ?? new List<TksScoreModel>(),
|
||||
tagList: [Utils.redisKitScore]
|
||||
);
|
||||
@@ -2016,7 +2016,7 @@ namespace MP.SPEC.Data
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "WipKitFiltAsync",
|
||||
cacheKey: currKey,
|
||||
expiration: TimeSpan.FromMinutes(redisLongTimeCache),
|
||||
expiration: GetRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () => await _productionRepository.WipKitFiltAsync(KeyFilt) ?? new List<WipSetupKitModel>(),
|
||||
tagList: [Utils.redisKitWip]
|
||||
);
|
||||
@@ -2035,6 +2035,7 @@ namespace MP.SPEC.Data
|
||||
fatto = await _productionRepository.WipKitUpsertAsync(currRecord);
|
||||
// svuoto cache KitWip
|
||||
await FlushFusionCacheAsync(Utils.redisKitWip);
|
||||
await FlushFusionCacheAsync(Utils.redisKitScore);
|
||||
activity?.SetTag("data.source", source);
|
||||
activity?.Stop();
|
||||
LogTrace($"WipKitUpsertAsync | {source} | {activity?.Duration.TotalMilliseconds}ms");
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>MP.SPEC</RootNamespace>
|
||||
<Version>8.16.2606.1117</Version>
|
||||
<Version>8.16.2606.1312</Version>
|
||||
<UserSecretsId>1800a78a-6ff1-40f9-b490-87fb8bfc1394</UserSecretsId>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@page "/force-reset"
|
||||
@page "/ForceReset"
|
||||
@using MP.AppAuth.Services
|
||||
@using MP.SPEC.Data
|
||||
|
||||
@@ -47,6 +48,13 @@
|
||||
nextVal = 10;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await Task.Delay(bDelay);
|
||||
// svuoto cache redis...
|
||||
ConfigModel updRec = new ConfigModel()
|
||||
{
|
||||
Chiave = "AZIENDA",
|
||||
Valore = "*"
|
||||
};
|
||||
await MDService.ConfigUpdateAsync(updRec);
|
||||
title = "UserDataReload...";
|
||||
currVal = 50;
|
||||
nextVal = 80;
|
||||
|
||||
@@ -68,8 +68,6 @@ else
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@*<div class="card-footer">
|
||||
</div>*@
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,13 @@
|
||||
using Blazored.SessionStorage;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using MP.Data.DbModels;
|
||||
using MP.Data.Services;
|
||||
using MP.SPEC.Data;
|
||||
|
||||
namespace MP.SPEC.Pages
|
||||
{
|
||||
public partial class Giacenze
|
||||
{
|
||||
#region Protected Fields
|
||||
|
||||
protected List<ODLModel>? elencoOdl;
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
protected int IdxOdl { get; set; } = 0;
|
||||
|
||||
protected ODLExpModel? odlExp { get; set; } = null!;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@@ -41,34 +27,43 @@ namespace MP.SPEC.Pages
|
||||
odlExp = await MDService.OdlByKeyAsync(IdxOdl);
|
||||
}
|
||||
|
||||
protected void saveBatch(string newBatch)
|
||||
{
|
||||
BatchSel = newBatch;
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private List<ODLModel>? elencoOdl;
|
||||
|
||||
private string giacenzeConf = "false";
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
private string BatchSel { get; set; } = "";
|
||||
private int IdxOdl { get; set; } = 0;
|
||||
|
||||
private string mainTabCss
|
||||
{
|
||||
get => !string.IsNullOrEmpty(BatchSel) ? "col-10" : "col-12";
|
||||
}
|
||||
|
||||
[Inject]
|
||||
private MpDataService MDService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private NavigationManager NavManager { get; set; } = null!;
|
||||
|
||||
private ODLExpModel? odlExp { get; set; } = null!;
|
||||
private string padCodXdl { get; set; } = "00000";
|
||||
private string giacenzeConf = "false";
|
||||
|
||||
[Inject]
|
||||
private ISessionStorageService sessionStorage { get; set; } = null!;
|
||||
|
||||
private string BatchSel { get; set; } = "";
|
||||
|
||||
private string mainTabCss
|
||||
{
|
||||
get => !string.IsNullOrEmpty(BatchSel) ? "col-10" : "col-12";
|
||||
}
|
||||
|
||||
protected void saveBatch(string newBatch)
|
||||
{
|
||||
BatchSel = newBatch;
|
||||
}
|
||||
|
||||
#endregion Private Properties
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
<div class="input-group input-group-sm d-flex justify-content-between">
|
||||
<div class="input-group-text" id="inputGroup-sizing-sm">
|
||||
<div class="pe-3" title="PODL Kit Child">
|
||||
Kit
|
||||
Kit C
|
||||
</div>
|
||||
<div class="form-check form-check-sm form-switch py-1" title="Mostra KIT">
|
||||
<input class="form-check-input" type="checkbox" @bind="@ShowKit">
|
||||
|
||||
@@ -192,21 +192,6 @@ builder.Services.TryAddScoped<MpDataService>();
|
||||
builder.Services.TryAddSingleton<IOApiService>();
|
||||
builder.Services.TryAddScoped<MsgServiceSpec>();
|
||||
|
||||
#if false
|
||||
builder.Services.AddSingleton<AppAuthService>();
|
||||
builder.Services.AddSingleton<ListSelectDataSrv>();
|
||||
builder.Services.AddSingleton<SharedMemService>();
|
||||
builder.Services.AddSingleton<TabDataService>();
|
||||
builder.Services.AddSingleton<TabDataFeeder>();
|
||||
#endif
|
||||
|
||||
#if false
|
||||
// aggiunta helper local/session storage service
|
||||
builder.Services.AddScoped<ISessionStorageService, SessionStorageService>();
|
||||
builder.Services.AddScoped<ILocalStorageService, LocalStorageService>();
|
||||
#endif
|
||||
|
||||
|
||||
builder.Services.AddHttpClient();
|
||||
|
||||
logger.Info("Aggiunti services");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo MAPOSPEC </i>
|
||||
<h4>Versione: 8.16.2606.1117</h4>
|
||||
<h4>Versione: 8.16.2606.1312</h4>
|
||||
<br /> Note di rilascio:
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.16.2606.1117
|
||||
8.16.2606.1312
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>8.16.2606.1117</version>
|
||||
<version>8.16.2606.1312</version>
|
||||
<url>https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/MP.SPEC.zip</url>
|
||||
<changelog>https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/ChangeLog.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>MP.Stats</RootNamespace>
|
||||
<UserSecretsId>826e877c-ba70-4253-84cb-d0b1cafd4440</UserSecretsId>
|
||||
<Version>8.16.2606.1117</Version>
|
||||
<Version>8.16.2606.1312</Version>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo statistiche MAPO</i>
|
||||
<h4>Versione: 8.16.2606.1117</h4>
|
||||
<h4>Versione: 8.16.2606.1312</h4>
|
||||
<br />
|
||||
Note di rilascio:
|
||||
<ul>
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.16.2606.1117
|
||||
8.16.2606.1312
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>8.16.2606.1117</version>
|
||||
<version>8.16.2606.1312</version>
|
||||
<url>https://nexus.steamware.net/repository/SWS/MP-STATS/stable/LAST/MP.Stats.zip</url>
|
||||
<changelog>https://nexus.steamware.net/repository/SWS/MP-STATS/stable/LAST/ChangeLog.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
Reference in New Issue
Block a user