using EgwCoreLib.Utils; using Microsoft.EntityFrameworkCore; using MP.Core.Conf; using MP.Core.DTO; using MP.Core.Objects; using MP.Data; using MP.Data.Controllers; using MP.Data.DbModels; using MP.Data.MgModels; using MP.Data.Services; using Newtonsoft.Json; using NLog; using StackExchange.Redis; using System.Data; using System.Diagnostics; using ZiggyCreatures.Caching.Fusion; namespace MP.SPEC.Data { public class MpDataService : IDisposable { #region Public Constructors public MpDataService(IConfiguration configuration, IFusionCache cache) { // salvataggio oggetti _configuration = configuration; _cache = cache; // Verifica conf trace... traceEnabled = _configuration.GetValue("Otel:EnableTracing", false); Log.Info($"MpDataService | INIT | Trace enabled: {traceEnabled}"); // setup compoenti REDIS redisConn = ConnectionMultiplexer.Connect(_configuration.GetConnectionString("Redis") ?? "localhost:6379"); redisConnAdmin = ConnectionMultiplexer.Connect(_configuration.GetConnectionString("RedisAdmin") ?? "localhost:6379"); redisDb = redisConn.GetDatabase(); // leggo cache lungo periodo int.TryParse(_configuration.GetValue("ServerConf:redisLongTimeCache"), out redisLongTimeCache); // setup MsgPipe BroadastMsgPipe = new MessagePipe(redisConn, Constants.BROADCAST_M_PIPE); Log.Info("MpDataService | Redis OK"); // conf DB string connStr = _configuration.GetConnectionString("MP.Data") ?? ""; if (string.IsNullOrEmpty(connStr)) { Log.Error("DbController: ConnString empty!"); } else { dbController = new MpSpecController(configuration); Log.Info("DbController OK"); } // conf x lettura dati da area REDIS di MP-IO MpIoNS = _configuration.GetValue("ServerConf:MpIoNS") ?? ""; // conf mongo... connStr = _configuration.GetConnectionString("mdbConnString") ?? ""; if (string.IsNullOrEmpty(connStr)) { Log.Error("MongoController: ConnString empty!"); } else { mongoController = new MpMongoController(configuration); Log.Info("MongoController OK"); } Log.Info("MpDataService | INIT completed"); } #endregion Public Constructors #region Public Events /// /// Evento richiesta rilettura dati pagina (x refresh pagine aperte) /// public event EventHandler ReloadRequest = delegate { }; #endregion Public Events #region Public Properties public static MpSpecController dbController { get; set; } = null!; public static MpMongoController mongoController { get; set; } = null!; public MessagePipe BroadastMsgPipe { get; set; } = null!; public Dictionary> currTagConf { get; set; } = new Dictionary>(); // Cache per controllo eliminazione articoli (Smart HashSet approach) private HashSet _usedArtIdsCache = new(); private HashSet _unusedArtIdsCache = new(); private DateTime _artCacheExpiry = DateTime.MinValue; #endregion Public Properties #region Public Methods /// /// Recupera eventuali azioni richieste /// /// public async Task ActionGetReq() { using var activity = ActivitySource.StartActivity("ActionGetReq"); string source = "REDIS"; DisplayAction? result = null; // cerco in redis... RedisValue rawData = await redisDb.StringGetAsync(Utils.redisActionReq); if (!string.IsNullOrEmpty($"{rawData}")) { result = JsonConvert.DeserializeObject($"{rawData}"); } if (result == null) { result = new DisplayAction(); } activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ActionGetReq Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Salva richiesta azione /// /// /// public bool ActionSetReq(DisplayAction? act2save) { using var activity = ActivitySource.StartActivity("ActionSetReq"); string source = "REDIS"; bool fatto = false; // cerco in redis... string rawData = JsonConvert.SerializeObject(act2save); // invio broadcast + salvo in redis BroadastMsgPipe.saveAndSendMessage(Utils.redisActionReq, rawData); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ActionSetReq {source} send to broadcast + Write cache: {activity?.Duration.TotalMilliseconds}ms"); return fatto; } /// /// Stacca un nuovo counter x il tipo richiesto /// /// public AnagCountersModel AnagCountersGetNext(string cntType) { using var activity = ActivitySource.StartActivity("AnagCountersGetNext"); AnagCountersModel result = new AnagCountersModel(); string source = "DB"; result = dbController.AnagCountersGetNext(cntType); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"AnagCountersGetNext | {source} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elenco EVENTI validi x ogni macchina secondo conf standard macchina /// /// public List AnagEventiGeneral() { using var activity = ActivitySource.StartActivity("AnagEventiGeneral"); string source = "DB"; List? result = new List(); // cerco in redisConn... string currKey = $"{Utils.redisEventList}:VSEB:GENERAL"; RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = dbController.AnagEventiGeneral(); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"AnagEventiGeneral | {source} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elenco EVENTI validi x macchina /// /// public List AnagEventiGetByMacch(string IdxMacch) { using var activity = ActivitySource.StartActivity("AnagEventiGetByMacch"); string source = "DB"; List? result = new List(); // cerco in redisConn... string currKey = $"{Utils.redisEventList}:VSEB:{IdxMacch}"; RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = dbController.AnagEventiGetByMacc(IdxMacch); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"AnagEventiGetByMacch | {source} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Delete record AnagraficaGruppi /// /// public bool AnagGruppiDelete(AnagGruppiModel updRec) { using var activity = ActivitySource.StartActivity("AnagGruppiDelete"); bool result = false; result = dbController.AnagGruppiDelete(updRec); // elimino cache redis... string pattern = $"{Utils.redisAnagGruppi}:*"; bool answ = ExecFlushRedisPattern(pattern); activity?.SetTag("data.source", "DB+REDIS"); activity?.Stop(); LogTrace($"AnagGruppiDelete | CodGruppo {updRec.CodGruppo} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Upsert record AnagraficaGruppi /// /// /// public bool AnagGruppiUpsert(AnagGruppiModel UpdRec) { using var activity = ActivitySource.StartActivity("AnagGruppiUpsert"); bool result = false; result = dbController.AnagGruppiUpsert(UpdRec); // elimino cache redis... string pattern = $"{Utils.redisAnagGruppi}:*"; bool answ = ExecFlushRedisPattern(pattern); activity?.SetTag("data.source", "DB+REDIS"); activity?.Stop(); LogTrace($"AnagGruppiUpsert | CodGruppo {UpdRec.CodGruppo} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elenco Gruppi /// /// public async Task> AnagKeyValGetAll() { // nuovo oggetto span activity using var activity = ActivitySource.StartActivity("AnagKeyValGetAll"); string source = "DB"; List? result = new List(); // cerco in redis... RedisValue rawData = await redisDb.StringGetAsync(Utils.redisAKVKey); if (!string.IsNullOrEmpty($"{rawData}")) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await Task.FromResult(dbController.AnagKeyValGetAll()); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); await redisDb.StringSetAsync(Utils.redisConfKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"AnagKeyValGetAll Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } public async Task> AnagStatiComm() { return await GetOrFetchAsync( operationName: "AnagStatiCommAsync", cacheKey: Utils.redisStatoCom, expiration: TimeSpan.FromMinutes(redisLongTimeCache), fetchFunc: async () => await dbController.AnagStatiCommAsync() ?? new List() ); } public async Task> AnagTipoArtLvAsync() { using var activity = ActivitySource.StartActivity("AnagTipoArtLvAsync"); string source = "DB"; List? result = new List(); // cerco in redis... RedisValue rawData = await redisDb.StringGetAsync(Utils.redisTipoArt); if (!string.IsNullOrEmpty($"{rawData}")) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await dbController.AnagTipoArtLvAsync(); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); await redisDb.StringSetAsync(Utils.redisTipoArt, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"AnagTipoArtLvAsync Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elenco Codice articolo con dati dossier gestiti /// /// public async Task> ArticleWithDossier() { using var activity = ActivitySource.StartActivity("ArticleWithDossier"); List? result = new List(); string source = "DB"; string currKey = Utils.redisArtByDossier; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await Task.FromResult(dbController.ArticleWithDossier()); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"ArticleWithDossier | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Eliminazione record selezionato /// /// /// public async Task ArticoliDeleteRecord(AnagArticoliModel currRec) { using var activity = ActivitySource.StartActivity("ArticoliDeleteRecord"); string source = "DB+REDIS"; bool fatto = await dbController.ArticoliDeleteRecord(currRec); await resetCacheArticoli(); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ArticoliDeleteRecord | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return fatto; } /// /// Restitusice elenco articoli dato tipo (es KIT) /// /// /// /// public async Task> ArticoliGetByTipoAsync(string tipo, string azienda = "*") { string sKey = string.IsNullOrWhiteSpace(tipo) ? "ALL" : tipo.Trim(); string redisKey = $"{Utils.redisArtList}:{azienda}:Tipo:{sKey}"; return await GetOrFetchAsync( operationName: "ArticoliGetByTipoAsync", cacheKey: redisKey, expiration: TimeSpan.FromMinutes(2), fetchFunc: async () => await dbController.ArticoliGetByTipoAsync(tipo, azienda) ?? new List() ); } /// /// Restitusice elenco articoli cercati /// /// /// /// public async Task> ArticoliGetSearchAsync(int numRecord, string azienda, string searchVal) { string sKey = string.IsNullOrWhiteSpace(searchVal) ? "***" : searchVal.Trim(); string memKey = $"ART_SEARCH_MEM:{azienda}:{sKey}:{numRecord}"; string redisKey = $"{Utils.redisArtList}:{azienda}:{sKey}:{numRecord}"; return await GetOrFetchAsync( operationName: "ArticoliGetSearchAsync", cacheKey: redisKey, expiration: TimeSpan.FromMinutes(2), fetchFunc: async () => await dbController.ArticoliGetSearchAsync(numRecord, azienda, searchVal) ?? new List() ); } /// /// Implementa gestione recupero cache da memoria o da obj esterno + cache memoria + tracking attività /// /// /// /// /// /// private async Task GetOrFetchAsync(string operationName, string cacheKey, Func> fetchFunc, TimeSpan expiration, params string[] tags) { using var activity = ActivitySource.StartActivity(operationName); string source; var tryGet = await _cache.TryGetAsync(cacheKey); if (tryGet.HasValue) { source = "MEMORY"; var result = tryGet.Value!; activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"{operationName} | {source} | {activity?.Duration.TotalMilliseconds:F4} ms"); return result; } bool fromDb = false; var final = await _cache.GetOrSetAsync( cacheKey, async _ => { fromDb = true; return await fetchFunc(); }, opt => { opt.SetDuration(expiration) .SetFailSafe(true); //if (tags != null && tags.Length > 0) // opt.SetTags(tags); }); source = fromDb ? "DB" : "REDIS"; activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"{operationName} | {source} | {activity?.Duration.TotalMilliseconds:F4} ms"); return final!; } /// /// Cancellazione FusionCache (totale) /// /// public async Task FlushCacheAsync() { bool fatto = false; await _cache.ClearAsync(); _configData.Clear(); fatto = true; return fatto; } /// /// Cancellazione FusionCache dato elenco tags /// /// public async Task FlushCacheByTagsAsync(List listTags) { bool fatto = false; foreach (var item in listTags) { await _cache.RemoveByTagAsync(item); } _configData.Clear(); fatto = true; return fatto; } /// /// Cancellazione FusionCache dato singolo tag /// /// public async Task FlushCacheByTagAsync(string tag) { bool fatto = false; await _cache.RemoveByTagAsync(tag); _configData.Clear(); fatto = true; return fatto; } /// /// Aggiornamento record selezionato /// /// /// public async Task ArticoliUpdateRecord(AnagArticoliModel currRec) { using var activity = ActivitySource.StartActivity("ArticoliUpdateRecord"); string source = "DB+REDIS"; bool fatto = await dbController.ArticoliUpdateRecord(currRec); await resetCacheArticoli(); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ArticoliUpdateRecord | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return fatto; } /// /// Verifica se sia possiubile cancellare articolo dato suo CodArt cercando su redis o su /// tab veto da DB /// /// /// public bool ArticoloDelEnabled(object CodArt) { using var activity = ActivitySource.StartActivity("ArticoloDelEnabled"); string codArticolo = $"{CodArt}"; int numUsed = _usedArtIdsCache.Count; int numUnused = _unusedArtIdsCache.Count; bool usato = true; string source = "MEMORY"; // 1. Controllo immediato sulla cache locale (HashSet) x eventuale refresh if (DateTime.Now >= _artCacheExpiry || (numUsed + numUnused) <= 0) { source = "DB/REDIS"; // Fallback sincrono minimo per non rompere il componente Blazor // Nota: Questo è un workaround per la firma sincrona. var task = EnsureArtCacheLoadedAsync(false); task.Wait(); // rileggo numUsed = _usedArtIdsCache.Count; numUnused = _unusedArtIdsCache.Count; } // verifico quale sia l'elenco if (numUsed > 0) { usato = _usedArtIdsCache.Contains(codArticolo); } else { usato = !_unusedArtIdsCache.Contains(codArticolo); } activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ArticoloDelEnabled | Cod: {codArticolo} | Source: {source}"); return !usato; } /// /// Caricamento asincrono della cache degli articoli (Used/Unused) /// public async Task EnsureArtCacheLoadedAsync(bool forceReload) { if (!forceReload && (DateTime.Now < _artCacheExpiry && (_usedArtIdsCache.Count > 0 || _unusedArtIdsCache.Count > 0))) return; try { // verifico quale sia il set + piccolo int totalCount = await dbController.ArticoliCountAsync(); int usedCount = await dbController.ArticoliCountUsedAsync(); if (usedCount <= (totalCount - usedCount)) { var usedList = await dbController.ArticoliGetUsedAsync(); _usedArtIdsCache = new HashSet(usedList.Select(x => x.CodArticolo)); _unusedArtIdsCache.Clear(); } else { var unusedList = await dbController.ArticoliGetUnusedAsync(); _unusedArtIdsCache = new HashSet(unusedList.Select(x => x.CodArticolo)); _usedArtIdsCache.Clear(); } _artCacheExpiry = DateTime.Now.AddMinutes(15); // TTL ragionevole per la cache locale } catch (Exception ex) { Log.Error($"Errore nel caricamento cache articoli: {ex.Message}"); _artCacheExpiry = DateTime.Now.AddSeconds(1); // Retry breve in caso di errore } } #if false /// /// Verifica se sia possiubile cancellare articolo dato suo CodArt cercando su redis o su /// tab veto da DB /// /// /// public async Task ArticoloDelEnabledAsync(object CodArt) { using var activity = ActivitySource.StartActivity("ArticoloDelEnabledAsync"); string codArticolo = $"{CodArt}"; int cacheCheckArtUsato = 1; int.TryParse(_configuration.GetValue("ServerConf:cacheCheckArtUsato"), out cacheCheckArtUsato); TimeSpan ttl = getRandTOut(cacheCheckArtUsato); // 1. Controllo cache locale (Smart HashSet) // Se siamo nel periodo di validità della cache locale, facciamo il controllo istantaneo if (DateTime.Now < _artCacheExpiry) { // Se la nostra cache corrente contiene l'ID (o se stiamo usando la lista degli unused) // Nota: la logica dipende da quale lista è stata caricata. // Per semplicità, se abbiamo caricato gli "usati", cerchiamo tra quelli. // Se abbiamo caricato gli "unused", l'articolo è eliminabile se è nel set. // Ma per evitare confusione, gestiamo il refresh globale. } // 2. Fallback su GetOrFetchAsync per garantire la sincronizzazione e l'uso di FusionCache (L1/L2) // Usiamo un approccio che carichi la lista più piccola in memoria. // Per evitare complessità di switching lato client, usiamo la lista degli "usati" come riferimento // principale nella cache distribuita, ma ottimizziamo il caricamento. bool usato = false; string source = "DB"; // Controllo se l'ID è già presente nella nostra cache locale degli "usati" if (DateTime.Now < _artCacheExpiry && _usedArtIdsCache.Contains(codArticolo)) { usato = true; source = "MEMORY(USED)"; } else if (DateTime.Now < _artCacheExpiry && _unusedArtIdsCache.Contains(codArticolo)) { usato = false; source = "MEMORY(UNUSED)"; } else { // Cache scaduta o non presente: ricalcoliamo la strategia int totalCount = await dbController.ArticoliCountAsync(); int usedCount = await dbController.ArticoliCountUsedAsync(); if (usedCount <= (totalCount - usedCount)) { // Gli usati sono meno o uguali agli unused: carichiamo gli usati var usedList = await dbController.ArticoliGetUsedAsync(); _usedArtIdsCache = new HashSet(usedList.Select(x => x.CodArticolo)); _unusedArtIdsCache.Clear(); usato = _usedArtIdsCache.Contains(codArticolo); source = "DB+MEMORY(USED)"; } else { // Gli unused sono meno: carichiamo gli unused var unusedList = await dbController.ArticoliGetUnusedAsync(); _unusedArtIdsCache = new HashSet(unusedList.Select(x => x.CodArticolo)); _usedArtIdsCache.Clear(); usato = !_unusedArtIdsCache.Contains(codArticolo); source = "DB+MEMORY(UNUSED)"; } _artCacheExpiry = DateTime.Now.AddMinutes(cacheCheckArtUsato); } bool answ = !usato; activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ArticoloDelEnabledAsync | Cod: {codArticolo} | Usato: {usato} | {activity?.Duration.TotalMilliseconds}ms"); return answ; } #endif public string CalcRecipe(RecipeModel currRecipe) { using var activity = ActivitySource.StartActivity("CalcRecipe"); var result = mongoController.CalcRecipe(currRecipe); activity?.SetTag("data.source", "MONGO"); return result; } /// /// Recupero tab config in modalità Sincrona /// /// public List ConfigGetAll() { using var activity = ActivitySource.StartActivity("ConfigGetAll"); string source = "REDIS"; List? result = new List(); // cerco in redis... RedisValue rawData = redisDb.StringGet(Utils.redisConfKey); if (!string.IsNullOrEmpty($"{rawData}")) { result = JsonConvert.DeserializeObject>($"{rawData}"); } else { source = "DB"; result = dbController.ConfigGetAll(); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(Utils.redisConfKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"ConfigGetAll Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Recupero tab config in modalità Asincrona /// /// public async Task> ConfigGetAllAsync() { using var activity = ActivitySource.StartActivity("ConfigGetAllAsync"); string source = "REDIS"; List? result = new List(); // cerco in redis... RedisValue rawData = await redisDb.StringGetAsync(Utils.redisConfKey); if (!string.IsNullOrEmpty($"{rawData}")) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await dbController.ConfigGetAllAsync(); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); await redisDb.StringSetAsync(Utils.redisConfKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"ConfigGetAllAsync Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Reset dati cache config /// /// public async Task ConfigResetCache() { using var activity = ActivitySource.StartActivity("ConfigResetCache"); string source = "REDIS"; await redisDb.StringSetAsync(Utils.redisConfKey, ""); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ConfigResetCache Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); } /// /// Restituisce valore della stringa (SE disponibile) /// /// /// public string ConfigTryGet(string keyName) { using var activity = ActivitySource.StartActivity("ConfigTryGet"); string source = "MEMORY"; EnsureConfigLoaded(); _configData.TryGetValue(keyName, out var value); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ConfigTryGet Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return value ?? ""; } /// /// Restituisce valore della stringa (SE disponibile) - modalità async /// /// /// public async Task ConfigTryGetAsync(string keyName) { using var activity = ActivitySource.StartActivity("ConfigTryGetAsync"); string source = "MEMORY"; await EnsureConfigLoadedAsync(); _configData.TryGetValue(keyName, out var value); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ConfigTryGetAsync | {keyName} | {source} | {activity?.Duration.TotalMilliseconds}ms"); return value ?? ""; } /// /// Update chiave config /// /// public bool ConfigUpdate(ConfigModel updRec) { using var activity = ActivitySource.StartActivity("ConfigUpdate"); string source = "DB"; var updRes = dbController.ConfigUpdate(updRec); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ConfigUpdate Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return updRes; } /// /// Restituisce le statistiche di DB maintenance eseguite /// /// public Dictionary DbDedupStats() { using var activity = ActivitySource.StartActivity("DbDedupStats"); string source = "REDIS"; Dictionary actStats = new Dictionary(); string currKey = $"{Utils.redisStatsDbMaint}"; // recupero i record statistiche correnti RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { var rawStats = JsonConvert.DeserializeObject>($"{rawData}"); if (rawStats != null) { actStats = rawStats; } } activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"DbDedupStats Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return actStats; } /// /// Dispose del connettore ai dati /// public void Dispose() { // Clear database controller dbController.Dispose(); mongoController.Dispose(); redisConn.Dispose(); } /// /// Eliminazione di un dossier /// /// record dossier da eliminare /// public async Task DossiersDeleteRecord(DossierModel selRecord) { using var activity = ActivitySource.StartActivity("DossiersDeleteRecord"); bool result = false; result = await dbController.DossiersDeleteRecord(selRecord); // elimino cache redis... RedisValue pattern = new RedisValue($"{Utils.redisDossByMac}:*"); bool answ = await ExecFlushRedisPatternAsync(pattern); activity?.SetTag("data.source", "DB+REDIS"); activity?.Stop(); LogTrace($"DossiersDeleteRecord | IdxMacchina {selRecord.IdxMacchina} | DtRif {selRecord.DtRif} | IdxODL {selRecord.IdxODL} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elenco ultimi n record DOssiers (che contengono ad esempio "salvataggi" di FLuxLog) dato /// idxMaccSel (ordinato x data registrazione) /// /// * = tutte, altrimenti solo x una data idxMaccSel /// Data minima per estrazione records /// Data Massima per estrazione records /// Num Max records da recuperare /// public async Task> DossiersGetLastFilt(string IdxMacchina, string CodArticolo, DateTime DtStart, DateTime DtEnd, int MaxRec) { using var activity = ActivitySource.StartActivity("DossiersGetLastFiltAsync"); List? result = new List(); string source = "DB"; string currKey = $"{Utils.redisDossByMac}:{IdxMacchina}:{CodArticolo}:{DtStart:yyyyMMddHHmm}:{DtEnd:yyyyMMddHHmm}:{MaxRec}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = await redisDb.StringGetAsync(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await dbController.DossiersGetLastFiltAsync(IdxMacchina, CodArticolo, DtStart, DtEnd, MaxRec); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); await redisDb.StringSetAsync(currKey, rawData, getRandTOut(redisLongTimeCache / 5)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"DossiersGetLastFiltAsync | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Inserimento nuovo record dossier /// /// /// public async Task DossiersInsert(DossierModel currDoss) { using var activity = ActivitySource.StartActivity("DossiersInsert"); string source = "DB"; // aggiorno record sul DB bool answ = await dbController.DossiersInsert(currDoss); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"DossiersInsert | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return answ; } /// /// Effettua salvataggio snapshot parametri (con stored) + svuota eventuale cache redis /// /// idxMaccSel /// NUm massimo secondi per recuperare dati correnti /// DataOra riferimento x cui prendere valori antecedenti /// public async Task DossiersTakeParamsSnapshotLast(string IdxMacchina, DateTime dtMin, DateTime dtMax) { using var activity = ActivitySource.StartActivity("DossiersUpdateValore"); string source = "DB+REDIS"; bool answ = false; Log.Info($"Richiesta snapshot per idxMaccSel {IdxMacchina} | periodo {dtMin} --> {dtMax}"); // chiamo stored x salvare parametri dbController.DossiersTakeParamsSnapshotLast(IdxMacchina, dtMin, dtMax); // elimino cache redis... RedisValue pattern = new RedisValue($"{Utils.redisDossByMac}:*"); answ = await ExecFlushRedisPatternAsync(pattern); activity?.SetTag("data.source", "DB+REDIS"); activity?.Stop(); LogTrace($"DossiersTakeParamsSnapshotLast | Svuotata cache dossier | {pattern} | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return answ; } /// /// Update valore dossier /// /// /// public async Task DossiersUpdateValore(DossierModel currDoss) { using var activity = ActivitySource.StartActivity("DossiersUpdateValore"); string source = "DB"; // aggiorno record sul DB bool answ = await dbController.DossiersUpdateValore(currDoss); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"DossiersUpdateValore | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return answ; } /// /// Restituisce elenco aziende /// /// public async Task> ElencoAziendeAsync() { return await GetOrFetchAsync( operationName: "ElencoAziendeAsync", cacheKey: $"{Utils.redisAnagGruppi}:Aziende", expiration: TimeSpan.FromMinutes(redisLongTimeCache * 2), fetchFunc: async () => await dbController.AnagGruppiAziendeAsync() ?? new List() ); } /// /// Restitusice elenco Fasi /// /// public List ElencoGruppiFase() { using var activity = ActivitySource.StartActivity("ElencoGruppiFase"); List result = new List(); string source = "DB"; string currKey = $"{Utils.redisAnagGruppi}:FASE"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { var rawResult = JsonConvert.DeserializeObject>($"{rawData}"); if (rawResult != null) { result = rawResult; } source = "REDIS"; } else { result = dbController.AnagGruppiFase(); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache / 5)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"ElencoGruppiFase | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elenco link validi /// /// public List ElencoLink() { using var activity = ActivitySource.StartActivity("ElencoLink"); string source = "DB"; var linkList = dbController.ElencoLink(); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ElencoLink | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return linkList; } /// /// Restitusice elenco Reparti /// /// public List ElencoRepartiDTO() { using var activity = ActivitySource.StartActivity("ElencoRepartiDTO"); List result = new List(); string source = "DB"; string currKey = $"{Utils.redisAnagGruppi}:REPARTO"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { var rawResult = JsonConvert.DeserializeObject>($"{rawData}"); if (rawResult != null) { result = rawResult; } source = "REDIS"; } else { result = dbController.AnagGruppiRepartoDTO(); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"ElencoRepartiDTO | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Aggiunta record EventList /// /// /// public async Task EvListInsert(EventListModel newRec) { using var activity = ActivitySource.StartActivity("EvListInsert"); string source = "DB"; var result = await dbController.EvListInsert(newRec); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"EvListInsert | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Esegue flush memoria redis dato keyVal /// /// /// public bool ExecFlushRedisPattern(string pat2Flush) { using var activity = ActivitySource.StartActivity("ExecFlushRedisPattern"); string source = "REDIS"; bool answ = false; var masterEndpoint = redisConn.GetEndPoints() .Where(ep => redisConn.GetServer(ep).IsConnected && !redisConn.GetServer(ep).IsReplica) .FirstOrDefault(); // sepattern è "*" elimino intero DB... if (masterEndpoint != null && (pat2Flush.Equals(new RedisValue("*")) || pat2Flush == RedisValue.Null)) { redisConn.GetServer(masterEndpoint).FlushDatabase(database: redisDb.Database); } else { var server = redisConn.GetServer(masterEndpoint); var keys = server.Keys(database: redisDb.Database, pattern: pat2Flush, pageSize: 1000); var batch = new List(); foreach (var key in keys) { batch.Add(key); // Flush in batches of 1000 if (batch.Count >= 1000) { foreach (var item in batch) redisDb.KeyDelete(item); batch.Clear(); } } // Flush remaining keys foreach (var item in batch) redisDb.KeyDelete(item); } answ = true; activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ExecFlushRedisPattern | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return answ; } /// /// Esegue flush memoria redis dato keyVal, async /// /// /// public async Task ExecFlushRedisPatternAsync(RedisValue pat2Flush) { bool answ = false; using var activity = ActivitySource.StartActivity("ExecFlushRedisPatternAsync"); string source = "REDIS"; var masterEndpoint = redisConn.GetEndPoints() .Where(ep => redisConn.GetServer(ep).IsConnected && !redisConn.GetServer(ep).IsReplica) .FirstOrDefault(); // sepattern è "*" elimino intero DB... if (masterEndpoint != null && (pat2Flush.Equals(new RedisValue("*")) || pat2Flush == RedisValue.Null)) { redisConn.GetServer(masterEndpoint).FlushDatabase(database: redisDb.Database); } else { var server = redisConn.GetServer(masterEndpoint); var keys = server.Keys(database: redisDb.Database, pattern: pat2Flush, pageSize: 1000); var deleteTasks = new List(); foreach (var key in keys) { deleteTasks.Add(redisDb.KeyDeleteAsync(key)); if (deleteTasks.Count >= 1000) { await Task.WhenAll(deleteTasks); deleteTasks.Clear(); } } if (deleteTasks.Count > 0) { await Task.WhenAll(deleteTasks); } } answ = true; activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ExecFlushRedisPatternAsync | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return answ; } /// /// Imposta in redis la scadenza della pagina x il reload /// /// /// public DateTime ExpiryReloadParamGet() { using var activity = ActivitySource.StartActivity("ExpiryReloadParamGet"); string source = "REDIS"; DateTime dtRif = DateTime.Now; string currKey = $"{Utils.redisParamPageExp}"; RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { dtRif = JsonConvert.DeserializeObject($"{rawData}"); } activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ExpiryReloadParamGet | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return dtRif; } /// /// Imposta in redis la scadenza della pagina x il reload /// /// /// public bool ExpiryReloadParamSet(DateTime expTime) { using var activity = ActivitySource.StartActivity("ExpiryReloadParamSet"); string source = "REDIS"; bool fatto = false; string currKey = $"{Utils.redisParamPageExp}"; string rawData = JsonConvert.SerializeObject(expTime); fatto = redisDb.StringSet(currKey, rawData); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ExpiryReloadParamSet | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return fatto; } public async Task FlushCacheFluxLog() { using var activity = ActivitySource.StartActivity("FlushCacheFluxLog"); string source = "REDIS"; bool answ = false; RedisValue pattern = new RedisValue($"{Utils.redisParetoFLKey}:*"); answ = await ExecFlushRedisPatternAsync(pattern); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"FlushCacheFluxLog | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return answ; } /// /// Flush cache relativa a MP-IO x dati ODL /// /// public async Task FlushMpIoOdlCache() { using var activity = ActivitySource.StartActivity("FlushMpIoOdlCache"); string source = "REDIS"; // svuoto dalla cache REDIS del server IO... bool ok01 = await ResetIoCache("CurrODL"); bool ok02 = await ResetIoCache("CurrOdlRow"); bool ok03 = await ResetIoCache("CurrStatoMacc"); bool ok04 = await ResetIoCache("DtMac"); activity?.SetTag("data.source", "REDIS"); activity?.Stop(); LogTrace($"FlushMpIoOdlCache | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return ok01 && ok02 && ok03 && ok04; } public async Task FlushRedisCache() { using var activity = ActivitySource.StartActivity("FlushRedisCache"); string source = "REDIS"; RedisValue pattern = Utils.RedValue("*"); bool answ = await ExecFlushRedisPatternAsync(pattern); // rileggo vocabolario.,.. ObjVocabolario = VocabolarioGetAll(); activity?.Stop(); LogTrace($"FlushRedisCache | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return answ; } public async Task FlushRedisKey(string redKey) { using var activity = ActivitySource.StartActivity("FlushRedisKey"); string source = "REDIS"; RedisValue pattern = Utils.RedValue(redKey); bool answ = await ExecFlushRedisPatternAsync(pattern); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"FlushRedisKey | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return answ; } /// /// Funzione di Data Reduction x FluxLog /// /// Macchina /// Elenco FL da processare /// Periodo /// modalità sel valore /// intervallo di analisi /// max num per intervallo /// public async Task FluxLogDataRedux(string idxMaccSel, List fluxList, DtUtils.Periodo currPeriodo, Enums.ValSelection valMode, Enums.DataInterval intReq, int maxItem) { using var activity = ActivitySource.StartActivity("FluxLogDataRedux"); string source = "DB+REDIS"; List procStats = await dbController.FluxLogDataRedux(idxMaccSel, fluxList, currPeriodo, valMode, intReq, maxItem); // effettuo merge statistiche... ProcDedupStatMerge(procStats); // svuoto cache await FlushCacheFluxLog(); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"FluxLogDataRedux | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); } public List FluxLogDtoGetByFlux(string Valore) { List answ = new List(); DossierFluxLogDTO? result = JsonConvert.DeserializeObject(Valore); if (result != null) { if (result.ODL != null) { answ = result .ODL .OrderBy(x => x.CodFlux) .ToList(); // inizializzo SE necessario foreach (var item in answ) { item.ValoreEdit = String.IsNullOrEmpty(item.ValoreEdit) ? item.Valore : item.ValoreEdit; } } } return answ; } /// /// Elenco ultimi n record flux log dato idxMaccSel e flusso (ordinato x data registrazione) /// /// Data massima x eventi /// Data minima x eventi /// * = tutte, altrimenti solo x una data idxMaccSel /// *=tutti, altrimenti solo selezionato /// numero massimo record da restituire /// public async Task> FluxLogGetLastFilt(DateTime DtMax, DateTime DtMin, string IdxMacchina, string CodFlux, int MaxRec, double redisCacheSec) { using var activity = ActivitySource.StartActivity("FluxLogGetLastFilt"); List? result = new List(); string source = "DB"; string currKey = $"{Utils.redisFluxLogFilt}:{IdxMacchina}:{CodFlux}:{MaxRec}:{DtMax:yyyyMMddHHmm}:{DtMin:yyyyMMddHHmm}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await Task.FromResult(dbController.FluxLogGetLastFilt(DtMax, DtMin, IdxMacchina, CodFlux, MaxRec)); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); if (string.IsNullOrEmpty(canCacheParametri)) { canCacheParametri = await ConfigTryGetAsync("SPEC_ParametriEnableRedisCache"); } if (canCacheParametri != "false") { redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisCacheSec)); } } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"FluxLogGetLastFilt | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elenco Gruppi /// /// public async Task> FluxLogPareto(string idxMacchina, DateTime dtFrom, DateTime dtTo) { using var activity = ActivitySource.StartActivity("FluxLogPareto"); string source = "DB"; List? result = new List(); // cerco in redis... string redKey = $"{Utils.redisParetoFLKey}:{idxMacchina}:{dtFrom:yyyyMMdd}:{dtTo:yyyyMMdd}"; RedisValue rawData = await redisDb.StringGetAsync(redKey); if (!string.IsNullOrEmpty($"{rawData}")) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await Task.FromResult(dbController.FluxLogPareto(idxMacchina, dtFrom, dtTo)); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); await redisDb.StringSetAsync(redKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"FluxLogPareto | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Stored manutenzione del DB /// /// Esegue realmente il task /// Aggiornamento statistiche /// Salvataggio /// def: 1000 /// def: 10 /// def: 50 /// public async Task ForceDbMaint(bool doExec = true, bool doUpdStat = true, bool doSave = true, int minPgCnt = 1000, int minAvgFrag = 10, int maxAvgFragReb = 50) { using var activity = ActivitySource.StartActivity("ForceDbMaint"); string source = "DB+REDIS"; await dbController.ForceDbMaint(doExec, doUpdStat, doSave, minPgCnt, minAvgFrag, maxAvgFragReb); // svuoto cache await FlushCacheFluxLog(); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ForceDbMaint | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); // registro statistiche esecuzione RecDbMaintStat(activity?.Duration ?? TimeSpan.FromSeconds(1)); } /// /// Eliminazione di un record macchina dal gruppo /// /// /// public bool Grp2MaccDelete(Gruppi2MaccModel rec2del) { using var activity = ActivitySource.StartActivity("Grp2MaccDelete"); bool result = false; result = dbController.Grp2MaccDelete(rec2del); // elimino cache redis... ResetMacGrpCache(); activity?.SetTag("data.source", "DB+REDIS"); activity?.Stop(); LogTrace($"Grp2MaccDelete | CodGruppo {rec2del.CodGruppo} | IdxMacc {rec2del.IdxMacchina} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Insert di un record macchina /// /// /// public bool Grp2MaccInsert(Gruppi2MaccModel upsRec) { using var activity = ActivitySource.StartActivity("Grp2MaccInsert"); bool result = false; result = dbController.Grp2MaccInsert(upsRec); // elimino cache redis... ResetMacGrpCache(); activity?.SetTag("data.source", "DB+REDIS"); activity?.Stop(); LogTrace($"Grp2MaccInsert | CodGruppo {upsRec.CodGruppo} | IdxMacc {upsRec.IdxMacchina} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Eliminazione di un record operatore dal gruppo /// /// /// public bool Grp2OperDelete(Gruppi2OperModel rec2del) { using var activity = ActivitySource.StartActivity("Grp2OperDelete"); bool result = false; result = dbController.Grp2OperDelete(rec2del); // elimino cache redis... ResetOprGrpCache(); activity?.SetTag("data.source", "DB+REDIS"); activity?.Stop(); LogTrace($"Grp2OperDelete | CodGruppo {rec2del.CodGruppo} | MatrOpr {rec2del.MatrOpr} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Insert di un record operatore /// /// /// public bool Grp2OperInsert(Gruppi2OperModel upsRec) { using var activity = ActivitySource.StartActivity("Grp2OperInsert"); bool result = false; result = dbController.Grp2OperInsert(upsRec); // elimino cache redis... ResetOprGrpCache(); activity?.SetTag("data.source", "DB+REDIS"); activity?.Stop(); LogTrace($"Grp2OperInsert | CodGruppo {upsRec.CodGruppo} | MatrOpr {upsRec.MatrOpr} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Init ricetta /// /// /// /// /// public RecipeModel InitRecipe(string confPath, int idxPODL, Dictionary CalcArgs) { return mongoController.InitRecipe(confPath, idxPODL, CalcArgs); } /// /// Recupero info IOB x TAB (da info registrate IOB-WIN--> MP-IO) /// /// /// public async Task IobInfo(string IdxMacchina) { using var activity = ActivitySource.StartActivity("IobInfo"); string source = "DB"; IOB_data? result = new IOB_data(); // cerco in redis... string currKey = redHashMpIO($"hM2IOB:{IdxMacchina}"); RedisValue rawData = await redisDb.StringGetAsync(currKey); //if (!string.IsNullOrEmpty($"{rawData}")) if (rawData.HasValue) { result = JsonConvert.DeserializeObject($"{rawData}"); source = "REDIS"; } else { Log.Error($"Errore: non trovato valore valido in REDIS | key: {currKey}"); Log.Info($"REDIS | conf: {redisConn.Configuration}"); Log.Info($" --> Valore trovato:{Environment.NewLine}{rawData}"); } if (result == null) { result = new IOB_data(); LogTrace($"Init valore default | IdxMacchina: {IdxMacchina}"); } activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"IobInfo per {IdxMacchina} | {source} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elimina record + svuotamento cache /// /// public async Task IstKitDelete(IstanzeKitModel currRecord) { using var activity = ActivitySource.StartActivity("IstKitDelete"); string source = "DB+REDIS"; bool fatto = false; // salvo fatto = dbController.IstKitDelete(currRecord); // svuoto cache RedisValue pattern = $"{Utils.redisKitInst}:*"; await ExecFlushRedisPatternAsync(pattern); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"IstKitDelete | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return fatto; } /// /// Elenco Istanze KIT da ricerca /// /// /// /// public List IstKitFilt(string keyKit, string keyExtOrd) { using var activity = ActivitySource.StartActivity("IstKitFilt"); string source = "DB"; List? result = new List(); // cerco in redis... string currKey = $"{Utils.redisKitInst}:{keyKit}:{keyExtOrd}"; RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = dbController.IstKitFilt(keyKit, keyExtOrd); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, TimeSpan.FromMinutes(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"IstKitFilt | {source} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Effettua creazione istanza KIT /// /// Articolo KIT (fittizio) /// Chiave x filtro conf su tab WKS public bool IstKitInsertByWKS(string CodArtParent, string KeyFilt) { bool fatto = false; using var activity = ActivitySource.StartActivity("IstKitInsertByWKS"); string source = "DB+REDIS"; // salvo fatto = dbController.IstKitInsertByWKS(CodArtParent, KeyFilt); // svuoto cache ExecFlushRedisPattern($"{Utils.redisKit}:*"); //ExecFlushRedisPattern((RedisValue)$"{Utils.redisKitInst}:*"); //ExecFlushRedisPattern((RedisValue)$"{Utils.redisKitScore}:*"); //ExecFlushRedisPattern((RedisValue)$"{Utils.redisKitTempl}:*"); //ExecFlushRedisPattern((RedisValue)$"{Utils.redisKitWip}:*"); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"IstKitInsertByWKS | {source} | {activity?.Duration.TotalMilliseconds}ms"); return fatto; } /// /// Esegue salvataggio record + svuotamento cache /// /// public async Task IstKitUpsert(IstanzeKitModel currRecord) { using var activity = ActivitySource.StartActivity("IstKitUpsert"); string source = "DB+REDIS"; bool fatto = false; // salvo fatto = dbController.IstKitUpsert(currRecord); // svuoto cache RedisValue pattern = $"{Utils.redisKitInst}:*"; await ExecFlushRedisPatternAsync(pattern); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"IstKitUpsert | {source} | {activity?.Duration.TotalMilliseconds}ms"); return fatto; } /// /// /// id odl da cercare /// public async Task> ListGiacenze(int IdxOdl) { using var activity = ActivitySource.StartActivity("ListGiacenze"); List? result = new List(); string source = "DB"; string currKey = $"{Utils.redisGiacenzaList}:{IdxOdl}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await Task.FromResult(dbController.ListGiacenze(IdxOdl)); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisShortTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"ListGiacenze | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Recupero elenco PODL filtrati /// /// /// True = aperti (=senza ODL) /// public List ListPODL_ByCodArt(string CodArticolo, bool OnlyAvail) { List result = new List(); if (!string.IsNullOrEmpty(CodArticolo)) { using var activity = ActivitySource.StartActivity("ListPODL_ByCodArt"); string source = "DB"; string avType = OnlyAvail ? "Avail" : "ALL"; string currKey = $"{Utils.redisPOdlByCodArt}:{CodArticolo}:{avType}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue && rawData.Length() > 2) { var rawResult = JsonConvert.DeserializeObject>($"{rawData}"); if (rawResult != null) { result = rawResult; source = "REDIS"; } } else { result = dbController.ListPODL_ByCodArt(CodArticolo, OnlyAvail); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); Log.Trace($"ListPODL_ByCodArt | {source} | {activity?.Duration.TotalMilliseconds}ms"); } else { Log.Debug("Errore CodArt vuoto"); } return result; } /// /// Elenco di tutte le macchine filtrate x gruppo /// /// /// public List MacchineGetFilt(string codGruppo) { using var activity = ActivitySource.StartActivity("MacchineGetFilt"); List? result = new List(); string source = "DB"; string keyGrp = codGruppo != "*" ? codGruppo : "ALL"; string currKey = $"{Utils.redisMacList}:{keyGrp}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = dbController.MacchineGetFilt(codGruppo); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", 1); activity?.Stop(); LogTrace($"MacchineGetFilt | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } private readonly IFusionCache _cache; /// /// Elenco di tutte le macchine filtrate x gruppo /// /// /// public async Task> MacchineGetFiltAsync(string codGruppo) { string keyGrp = codGruppo != "*" ? codGruppo : "ALL"; string redisKey = $"{Utils.redisMacList}:{keyGrp}"; string memKey = $"MACCHINE_MEM:{keyGrp}"; return await GetOrFetchAsync( operationName: "MacchineGetFiltAsync", cacheKey: redisKey, expiration: TimeSpan.FromMinutes(5), fetchFunc: async () => await dbController.MacchineGetFiltAsync(codGruppo) ?? new List() ); #if false return await GetOrCreateCachedAsync( operationName: "MacchineGetFiltAsync", memKey: memKey, redisKey: redisKey, // ✅ TTL coerente con il tuo requisito (prima avevi 1 minuto) memoryTtl: TimeSpan.FromMinutes(1), dbFactory: async () => await dbController.MacchineGetFiltAsync(codGruppo) ?? new List() ); #endif } #if false public async Task> MacchineGetFiltAsync(string codGruppo) { using var activity = ActivitySource.StartActivity("MacchineGetFiltAsync"); string source = "DB"; string keyGrp = codGruppo != "*" ? codGruppo : "ALL"; string memKey = $"MACCHINE_MEM:{keyGrp}"; string redisKey = $"{Utils.redisMacList}:{keyGrp}"; // ✅ 1. MEMORY CACHE if (_memoryCache.TryGetValue(memKey, out List cached)) { source = "MEMORY"; activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"MacchineGetFiltAsync | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return cached; } List result; // ✅ 2. REDIS var rawData = await redisDb.StringGetAsync(redisKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>(rawData!) ?? new(); source = "REDIS"; } else { // ✅ 3. DB result = await dbController.MacchineGetFiltAsync(codGruppo); await redisDb.StringSetAsync( redisKey, JsonConvert.SerializeObject(result), getRandTOut(redisLongTimeCache) ); } // ✅ salva in RAM (IMPORTANTISSIMO), TTL 1 minuto _memoryCache.Set(memKey, result, TimeSpan.FromMinutes(1)); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"MacchineGetFiltAsync | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } #endif /// /// Verifica se la idxMaccSel abbia un codice PATH ricette associato /// /// /// public string MacchineRecipeArchive(string idxMacchina) { using var activity = ActivitySource.StartActivity("MacchineRecipeArchive"); string? result = ""; string source = "DB"; string currKey = $"{Utils.redisMacRecipePath}:{idxMacchina}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject($"{rawData}"); source = "REDIS"; } else { //recupero elenco macchine... var machineList = MacchineGetFilt("*"); var currMach = machineList.Where(x => x.IdxMacchina == idxMacchina).FirstOrDefault(); result = currMach != null ? currMach.RecipeArchivePath : null; // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", 1); activity?.Stop(); LogTrace($"MacchineRecipeArchive | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result ?? ""; } /// /// Verifica se la idxMaccSel abbia un codice CONF ricetta associato /// /// /// public string MacchineRecipeConf(string idxMacchina) { using var activity = ActivitySource.StartActivity("MacchineRecipeConf"); string? result = ""; string source = "DB"; string currKey = $"{Utils.redisMacRecipeConf}:{idxMacchina}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject($"{rawData}"); source = "REDIS"; } else { //recupero elenco macchine... var machineList = MacchineGetFilt("*"); var currMach = machineList.Where(x => x.IdxMacchina == idxMacchina).FirstOrDefault(); result = currMach != null ? currMach.RecipePath : null; // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", 1); activity?.Stop(); LogTrace($"MacchineRecipeConf | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result ?? ""; } /// /// Elenco id Macchine che abbiano dati FLuxLog, nel periodo indicato /// /// /// /// public async Task> MacchineWithFlux(DateTime dtStart, DateTime dtEnd) { using var activity = ActivitySource.StartActivity("MacchineWithFlux"); List? result = new List(); string source = "DB"; string currKey = $"{Utils.redisMacByFlux}:{dtStart:yyyyMMddHHmm}:{dtEnd:yyyyMMddHHmm}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await dbController.MacchineWithFlux(dtStart, dtEnd); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"MacchineWithFlux | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Recupero info Machine-IOB x TAB (da info registrate IOB-WIN --> MP-IO) /// /// /// public Dictionary MachIobConf(string IdxMacchina) { using var activity = ActivitySource.StartActivity("MachIobConf"); string source = "DB"; Dictionary result = new Dictionary(); // cerco in redis... string currKey = redHashMpIO($"IOB:{IdxMacchina}:MachIobConf"); try { result = redisDb .HashGetAll(currKey) .ToDictionary(x => $"{x.Name}", x => $"{x.Value}"); source = "REDIS"; } catch (Exception exc) { Log.Error($"Errore in MachIobConf{Environment.NewLine}{exc}"); } if (result == null) { result = new Dictionary(); LogTrace($"Init valore default MachIobConf | IdxMacchina: {IdxMacchina}"); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"MachIobConf per {IdxMacchina} | {source} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Recupero singolo recordo info Machine-IOB x TAB (da info registrate IOB-WIN --> MP-IO) /// /// /// public string MachIobConfVal(string IdxMacchina, string Key) { string answ = ""; var currList = MachIobConf(IdxMacchina); if (currList.ContainsKey(Key)) { answ = currList[Key]; } return answ; } /// /// Elenco MSE stato amcchine /// /// /// public async Task> MseGetAll(bool forceDb = false) { using var activity = ActivitySource.StartActivity("MseGetAllAsync"); string source = "DB"; List? result = new List(); // cerco in redisConn... RedisValue rawData = redisDb.StringGet(Constants.redisMseKey); if (rawData.HasValue && !forceDb) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await Task.FromResult(dbController.MseGetAll(2000)); // serializzp e salvo... rawData = JsonConvert.SerializeObject(result); await redisDb.StringSetAsync(Constants.redisMseKey, rawData, TimeSpan.FromSeconds(1)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"MseGetAllAsync | {source} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Invio notifica rilettura (con parametro) /// /// public void NotifyReloadRequest(string message) { if (ReloadRequest != null) { // messaggio ReloadEventArgs rea = new ReloadEventArgs(message); ReloadRequest.Invoke(this, rea); } } /// /// Elenco ODL dato batch selezionato /// /// Batch richiesto /// public async Task> OdlByBatch(string BatchSel) { using var activity = ActivitySource.StartActivity("OdlByBatch"); List? result = new List(); string source = "DB"; string currKey = Utils.redisOdlByBatch; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await Task.FromResult(dbController.OdlByBatch(BatchSel)); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"OdlByBatch | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// ODL da chiave /// /// /// public ODLExpModel OdlByKey(int IdxOdl) { using var activity = ActivitySource.StartActivity("OdlByKey"); ODLExpModel? result = new ODLExpModel(); string source = "DB"; result = dbController.OdlByKey(IdxOdl); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"OdlByKey | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Effettua chiusura dell'ODL indicato, andand /// /// idx odl da chiudere /// idx idxMaccSel /// matricola operatore /// indica se confermare i pezzi priam di chiudere ODL public async Task ODLClose(int idxOdl, string idxMacchina, int matrOpr, bool confPezzi) { using var activity = ActivitySource.StartActivity("ODLClose"); string source = "DB"; bool fatto = false; await EnsureConfigLoadedAsync(); bool confRett = false; _configData.TryGetValue("confRett", out var value); if (!string.IsNullOrEmpty(value)) { bool.TryParse(value, out confRett); } int modoConfProd = 0; _configData.TryGetValue("modoConfProd", out var vModo); if (!string.IsNullOrEmpty(value)) { int.TryParse(vModo, out modoConfProd); } // chiamo metodo conferma! fatto = await dbController.ODLClose(idxOdl, idxMacchina, matrOpr, confPezzi, confRett, modoConfProd); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ODLClose | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return fatto; } /// /// Record ODL da chaive /// /// public async Task OdlGetByKey(int IdxOdl) { using var activity = ActivitySource.StartActivity("OdlGetByKey"); string source = "DB"; var dbResult = await dbController.OdlGetByKey(IdxOdl); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"OdlGetByKey | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return dbResult; } /// /// ODL correnti (tutti) /// /// /// public async Task> OdlGetCurrentAsync() { string redisKey = Utils.redisOdlCurrByMac; string memKey = $"MEM:{redisKey}"; return await GetOrFetchAsync( operationName: "OdlGetCurrentAsync", cacheKey: redisKey, expiration: TimeSpan.FromSeconds(3), fetchFunc: async () => { var rawData = await dbController.OdlGetCurrentAsync(); var dbResult = rawData .Select(x => x.IdxMacchina) .Distinct() .ToList(); return dbResult ?? new List(); } ); #if false return await GetOrCreateCachedAsync( operationName: "OdlGetCurrentAsync", memKey: memKey, redisKey: redisKey, // ✅ TTL molto corto (come avevi: 3 secondi) memoryTtl: TimeSpan.FromSeconds(3), dbFactory: async () => { var rawData = await dbController.OdlGetCurrentAsync(); var dbResult = rawData .Select(x => x.IdxMacchina) .Distinct() .ToList(); return dbResult ?? new List(); } ); #endif } #if false public List OdlGetCurrent() { using var activity = ActivitySource.StartActivity("OdlGetCurrent"); List? dbResult = new List(); string source = "DB"; string currKey = $"{Utils.redisOdlCurrByMac}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { dbResult = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { dbResult = dbController.OdlGetCurrent().Select(x => x.IdxMacchina).Distinct().ToList(); rawData = JsonConvert.SerializeObject(dbResult); redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(3)); } if (dbResult == null) { dbResult = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", dbResult.Count); activity?.Stop(); LogTrace($"OdlGetCurrent | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return dbResult; } #endif /// /// elenco TUTTI gli ODL /// /// /// public List OdlListAll() { using var activity = ActivitySource.StartActivity("OdlListAll"); List? result = new List(); string source = "DB"; result = dbController.OdlListAll(); activity?.SetTag("data.source", "DB"); activity?.Stop(); LogTrace($"OdlListAll | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elenco ODL filtrati x stato, articolo, KeyRich (che contiene stato) /// /// Stato ODL: true=in corso/completato /// Cod articolo /// KeyRich (parziale) da cercare (es cod stato x yacht) /// Reparto selezionato /// Macchina selezionata /// Data inizio /// Data fine /// public async Task> OdlListGetFilt(bool inCorso, string codArt, string keyRichPart, string Reparto, string IdxMacchina, DateTime startDate, DateTime endDate) { using var activity = ActivitySource.StartActivity("OdlListGetFilt"); List? result = new List(); string source = "DB"; string currKey = $"{Utils.redisOdlList}:{inCorso}:{codArt}:{keyRichPart}:{Reparto}:{IdxMacchina}:{startDate:yyyyMMdd_HHmmss}:{endDate:yyyyMMdd_HHmmss}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await Task.FromResult(dbController.ListODLFilt(inCorso, codArt, keyRichPart, Reparto, IdxMacchina, startDate, endDate)); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisShortTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"OdlListGetFilt | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elenco operatori filtrati x gruppo /// /// /// public List OperatoriGetFilt(string codGruppo) { using var activity = ActivitySource.StartActivity("OperatoriGetFilt"); List? result = new List(); string source = "DB"; string keyGrp = codGruppo != "*" ? codGruppo : "ALL"; string currKey = $"{Utils.redisOprList}:{keyGrp}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = dbController.OperatoriGetFilt(codGruppo); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"OperatoriGetFilt | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); activity?.SetTag("data.source", source); return result; } /// /// Elenco di tutti i parametri filtrati x idxMaccSel /// /// * = tutte, altrimenti solo x una data idxMaccSel /// public async Task> ParametriGetFilt(string IdxMacchina) { using var activity = ActivitySource.StartActivity("ParametriGetFilt"); List? result = new List(); string source = "DB"; string currKey = $"{Utils.redisFluxByMac}:{IdxMacchina}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await Task.FromResult(dbController.ParametriGetFilt(IdxMacchina)); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"ParametriGetFilt | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Eliminazione record selezionato /// /// /// public async Task POdlDeleteRecord(PODLExpModel currRec) { using var activity = ActivitySource.StartActivity("POdlDeleteRecord"); string source = "DB+REDIS"; var dbResult = await dbController.PODLDeleteRecord(currRec); // elimino cache redis... await POdlFlushCache(); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"POdlDeleteRecord | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return dbResult; } /// /// Avvio fase setup per il record selezionato /// /// /// public async Task POdlDoSetup(PODLExpModel currRec) { using var activity = ActivitySource.StartActivity("POdlDoSetup"); string source = "DB+REDIS"; var dbResult = await dbController.PODL_startSetup(currRec, 0, 1, 1, "", DateTime.Now); // elimino cache redis... await POdlFlushCache(); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"POdlDoSetup | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return dbResult; } /// /// Recupero PODL da chiave /// /// /// public async Task POdlGetByKey(int idxPODL) { PODLModel result = new PODLModel(); if (idxPODL != 0) { using var activity = ActivitySource.StartActivity("POdlGetByKey"); string source = "DB"; string currKey = $"{Utils.redisPOdlByPOdl}:{idxPODL}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { var rawResult = JsonConvert.DeserializeObject($"{rawData}"); if (rawResult != null) { result = rawResult; source = "REDIS"; } } else { result = await dbController.PODL_getByKey(idxPODL); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new PODLModel(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", 1); activity?.Stop(); Log.Trace($"POdlGetByKey | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); } else { Log.Debug("Errore IdxPODL = 0"); } return result; } /// /// Recupero PODL da IdxODL /// /// /// public PODLModel POdlGetByOdl(int idxODL) { PODLModel result = new PODLModel(); if (idxODL != 0) { using var activity = ActivitySource.StartActivity("POdlGetByOdl"); string source = "DB"; string currKey = $"{Utils.redisPOdlByOdl}:{idxODL}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { var rawResult = JsonConvert.DeserializeObject($"{rawData}"); if (rawResult != null) { result = rawResult; } source = "REDIS"; } else { result = dbController.PODL_getByOdl(idxODL); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); } if (result == null) { result = new PODLModel(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", 1); activity?.Stop(); Log.Trace($"POdlGetByOdl | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); } else { Log.Debug("Errore IdxODL = 0"); } return result; } /// /// Effettua il task di eliminazione PODL KIT + istanze + riattivazione PODL originali disattivate tramite stored /// /// IdxPODL parent public bool PodlIstKitDelete(int IdxPODL) { using var activity = ActivitySource.StartActivity("PodlIstKitDelete"); bool fatto = false; // salvo fatto = dbController.PodlIstKitDelete(IdxPODL); // svuoto cache string pattern = $"{Utils.redisKit}:*"; if (!string.IsNullOrEmpty(pattern)) { ExecFlushRedisPattern(pattern); } activity?.SetTag("data.source", "DB+REDIS"); return fatto; } /// /// Elenco PODL in un istanza KIT dall'ID del parent /// /// IDX PODL parent /// public List POdlListByKitParent(int IdxPodlParent) { using var activity = ActivitySource.StartActivity("POdlListByKitParent"); List? result = new List(); string source = "DB"; string currKey = $"{Utils.redisPOdlList}_kit:ByParent:{IdxPodlParent}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = dbController.ListPODL_ByKitParent(IdxPodlParent); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisShortTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"POdlListByKitParent | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elenco PODL non avviati filtrati x articolo, KeyRich (che contiene stato) /// /// Solo lanciati (1) o ancora disponibili (0) /// KeyRich (parziale) da cercare (es cod stato x yacht) /// Macchina /// Gruppo /// Data inizio /// Data fine /// public List POdlListGetFilt(bool lanciato, string keyRichPart, string idxMacchina, string codGruppo, DateTime startDate, DateTime endDate) { using var activity = ActivitySource.StartActivity("POdlListGetFiltAsync"); List? result = new List(); string source = "DB"; string currKey = $"{Utils.redisPOdlList}:{codGruppo}:{idxMacchina}:{keyRichPart}:{lanciato}:{startDate:yyyyMMdd_HHmmss}:{endDate:yyyyMMdd_HHmmss}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = dbController.ListPODLFilt(lanciato, keyRichPart, idxMacchina, codGruppo, startDate, endDate); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisShortTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"POdlListGetFilt | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elenco PODL (Async) non avviati filtrati x articolo, KeyRich (che contiene stato) /// /// Solo lanciati (1) o ancora disponibili (0) /// KeyRich (parziale) da cercare (es cod stato x yacht) /// Macchina /// Gruppo /// Data inizio /// Data fine /// public async Task> POdlListGetFiltAsync(bool lanciato, string keyRichPart, string idxMacchina, string codGruppo, DateTime startDate, DateTime endDate) { using var activity = ActivitySource.StartActivity("POdlListGetFiltAsync"); List? result = new List(); string source = "DB"; string currKey = $"{Utils.redisPOdlList}:{codGruppo}:{idxMacchina}:{keyRichPart}:{lanciato}:{startDate:yyyyMMdd_HHmmss}:{endDate:yyyyMMdd_HHmmss}"; // cerco in redis dato valore sel idxMaccSel... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await dbController.ListPODLFiltAsync(lanciato, keyRichPart, idxMacchina, codGruppo, startDate, endDate); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisShortTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"POdlListGetFiltAsync | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elenco PODL per composizione KIT (Async) non avviati filtrati x articolo, KeyRich (che contiene stato) /// /// Solo lanciati (1) o ancora disponibili (0) /// KeyRich (parziale) da cercare (es cod stato x yacht) /// Macchina /// Gruppo /// Data inizio /// Data fine /// public async Task> POdlToKitListGetFiltAsync(bool lanciato, string keyRichPart, string idxMacchina, string codGruppo, DateTime startDate, DateTime endDate) { string redisKey = $"{Utils.redisPOdlList}_kit:{codGruppo}:{idxMacchina}:{keyRichPart}:{lanciato}:{startDate:yyyyMMdd_HHmmss}:{endDate:yyyyMMdd_HHmmss}"; return await GetOrFetchAsync( operationName: "POdlToKitListGetFiltAsync", cacheKey: redisKey, expiration: TimeSpan.FromSeconds(redisShortTimeCache), fetchFunc: async () => await dbController.ListPODL_KitFiltAsync( lanciato, keyRichPart, idxMacchina, codGruppo, startDate, endDate ) ?? new List() ); } /// /// Chiamata salvataggio ricetta + refresh REDIS /// /// /// /// public async Task POdlUpdateRecipe(int idxPODL, string recipeName) { using var activity = ActivitySource.StartActivity("POdlUpdateRecipe"); string source = "DB+REDIS"; bool answ = false; answ = await dbController.PODL_updateRecipe(idxPODL, recipeName); // reset redis... if (answ) { await POdlFlushCache(); } activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"POdlUpdateRecipe | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return answ; } /// /// Aggiornamento record selezionato /// /// /// public async Task POdlUpdateRecord(PODLModel currRec) { using var activity = ActivitySource.StartActivity("POdlUpdateRecord"); string source = "DB+REDIS"; var dbResult = await dbController.PODLUpdateRecord(currRec); // elimino cache redis... await POdlFlushCache(); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"POdlUpdateRecord | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return dbResult; } /// /// Restituisce le statistiche di processo correnti x depluplica FluxLog /// /// public List ProcFLStats() { using var activity = ActivitySource.StartActivity("ProcFLStats"); string source = "REDIS"; List actStats = new List(); string currKey = $"{Utils.redisStatsProcFL}"; // recupero i record statistiche correnti RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { var rawStats = JsonConvert.DeserializeObject>($"{rawData}"); if (rawStats != null) { actStats = rawStats; } } activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ProcFLStats | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return actStats; } /// /// Ricerca ricetta su MongoDB dato PODL /// /// /// public async Task RecipeGetByPODL(int idxPODL) { RecipeModel? result = null; using var activity = ActivitySource.StartActivity("RecipeGetByPODL"); string source = "MongoDB"; result = await mongoController.RecipeGetByPODL(idxPODL); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"RecipeGetByPODL | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Salva ricetta su MongoDB /// /// /// public async Task RecipeSetByPODL(RecipeModel currRecord) { using var activity = ActivitySource.StartActivity("RecipeSetByPODL"); string source = "DB+REDIS"; bool answ = false; answ = await mongoController.RecipeSetByPODL(currRecord); if (answ) { await POdlFlushCache(); } activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"RecipeSetByPODL | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return answ; } /// /// Effettua conteggio chaivi REDIS dato pat2Flush ricerca /// /// /// public int RedisCountKey(string keyPattern) { using var activity = ActivitySource.StartActivity("RedisCountKey"); string source = "REDIS"; int num = 0; keyPattern = (string.IsNullOrEmpty(keyPattern) ? "**" : keyPattern); try { var listEndpoints = redisConnAdmin.GetEndPoints(); foreach (var endPoint in listEndpoints) { var server = redisConnAdmin.GetServer(endPoint); foreach (RedisKey item in server.Keys(pattern: keyPattern, database: redisDb.Database, pageSize: 250, cursor: 0L)) { num++; } } } catch (Exception arg) { Log.Error($"Eccezione in RedisCountKey{Environment.NewLine}{arg}"); } activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"RedisCountKey | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return num; } /// /// Esegue eliminazione memoria redis keyVal /// /// /// public bool RedisDelKey(string keyVal) { using var activity = ActivitySource.StartActivity("RedisDelKey"); string source = "REDIS"; bool answ = false; var listEndpoints = redisConnAdmin.GetEndPoints(); foreach (var endPoint in listEndpoints) { var server = redisConnAdmin.GetServer(endPoint); if (server != null) { redisDb.KeyDelete((RedisKey)keyVal); answ = true; } } activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"RedisDelKey | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return answ; } /// /// Reset della cache IO post operazioni come setup ODL... /// /// Indirizzo base da cui rimuovere memoria cache /// public async Task ResetIoCache(string baseMem) { using var activity = ActivitySource.StartActivity("ResetIoCache"); string source = "REDIS"; // patterna a partire da cache IO... RedisValue pattern = new RedisValue($"{MpIoNS}:*"); if (!string.IsNullOrEmpty(baseMem)) { pattern = new RedisValue($"{MpIoNS}:{baseMem}:*"); } bool answ = await ExecFlushRedisPatternAsync(pattern); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ResetIoCache | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return answ; } /// /// Effettua reset microstato macchina /// /// public async Task ResetMicrostatoMacchina(string idxMacchina) { using var activity = ActivitySource.StartActivity("ResetMicrostatoMacchina"); string source = "DB"; // salvo microstato 0... MicroStatoMacchinaModel newRecMS = new MicroStatoMacchinaModel() { IdxMacchina = idxMacchina, InizioStato = DateTime.Now, IdxMicroStato = 0, Value = "FER" }; var result = await dbController.MicroStatoMacchinaUpsert(newRecMS); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"ResetMicrostatoMacchina | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); } /// /// Statistiche ODL calcolate (da stored stp_STAT_ODL) /// /// public Task> StatOdl(int IdxOdl) { using var activity = ActivitySource.StartActivity("StatOdl"); string source = "DB"; var result = dbController.OdlStart(IdxOdl); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"StatOdl | {source} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Stato macchina /// /// /// public StatoMacchineModel StatoMacchina(string idxMacchina) { using var activity = ActivitySource.StartActivity("StatoMacchina"); // setup parametri costanti string source = "DB"; StatoMacchineModel? result = new StatoMacchineModel(); // cerco in redisConn... string currKey = $"{Utils.redisStatoMacch}:{idxMacchina}"; RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject($"{rawData}"); source = "REDIS"; } else { result = dbController.StatoMacchina(idxMacchina); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(1)); } if (result == null) { result = new StatoMacchineModel(); } activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"StatoMacchina | {source} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Restituisce il valore da REDIS associato al tag richiesto /// /// Chiave in cui cercare il valore /// public string TagConfGetKey(string redKey) { string outVal = ""; using var activity = ActivitySource.StartActivity("TagConfGetKey"); string source = "REDIS"; // cerco in REDIS la conf x l'IOB var rawData = redisDb.StringGet(redKey); if (!string.IsNullOrEmpty(rawData)) { outVal = $"{rawData}"; } activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"TagConfGetKey | {source} | {activity?.Duration.TotalMilliseconds}ms"); return outVal; } /// /// Elenco setup dei tag conf correnti /// /// public Dictionary> TagsGetAll() { return currTagConf; } /// /// Elimina record + svuotamento cache /// /// public async Task TemplateKitDelete(TemplateKitModel currRecord) { using var activity = ActivitySource.StartActivity("TemplateKitDelete"); string source = "DB+REDIS"; bool fatto = false; // salvo fatto = dbController.TemplateKitDelete(currRecord); // svuoto cache RedisValue pattern = $"{Utils.redisKitTempl}:*"; await ExecFlushRedisPatternAsync(pattern); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"TemplateKitDelete | {source} | {activity?.Duration.TotalMilliseconds}ms"); return fatto; } /// /// Elenco Template KIT da ricerca /// /// /// /// public List TemplateKitFilt(string codParent, string codChild) { using var activity = ActivitySource.StartActivity("TemplateKitFilt"); string source = "DB"; List? result = new List(); // cerco in redis... string currKey = $"{Utils.redisKitTempl}:{codParent}:{codChild}"; RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = dbController.TemplateKitFilt(codParent, codChild); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, TimeSpan.FromMinutes(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"TemplateKitFilt | {source} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Esegue salvataggio record + svuotamento cache /// /// /// public async Task TemplateKitUpsert(TemplateKitModel currRecord, string codAzienda) { using var activity = ActivitySource.StartActivity("TemplateKitUpsert"); string source = "DB+REDIS"; bool fatto = false; // salvo fatto = dbController.TemplateKitUpsert(currRecord, codAzienda); // svuoto cache RedisValue pattern = $"{Utils.redisKitTempl}:*"; await ExecFlushRedisPatternAsync(pattern); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"TemplateKitUpsert | {source} | {activity?.Duration.TotalMilliseconds}ms"); return fatto; } /// /// Punteggio compatibilità KIT per KeyFilt indicato /// /// /// /// /// public List TksScore(string KeyFilt, int MaxResult, bool ForceDb) { using var activity = ActivitySource.StartActivity("TksScore"); string source = "DB"; List? result = new List(); // cerco in redis... string currKey = $"{Utils.redisKitScore}:{KeyFilt}:{MaxResult}"; RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue && !ForceDb) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = dbController.TksScore(KeyFilt, MaxResult); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, TimeSpan.FromMinutes(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"TksScore | {source} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Esegue traduzione dato vocabolario da Lingua + Lemma /// /// /// /// public string Traduci(string lemma, string lingua) { string answ = $"[{lemma}]"; // verifico se ho qualcosa nell'obj vocabolario... if (ObjVocabolario == null || ObjVocabolario.Count == 0) { // inizializzo il vocabolario... ObjVocabolario = VocabolarioGetAll(); } var record = ObjVocabolario.Where(x => x.Lingua == lingua && x.Lemma == lemma).FirstOrDefault(); if (record != null) { answ = record.Traduzione; } return answ; } /// /// Update valore Dossier /// /// /// /// public async Task updateDossierValue(DossierModel currDoss, FluxLogDTO editFL) { using var activity = ActivitySource.StartActivity("updateDossierValue"); string source = "DB"; bool answ = false; // recupero intero set valori dossier deserializzando... var fluxLogList = FluxLogDtoGetByFlux(currDoss.Valore); // se tutto ok if (fluxLogList != null) { // da provare...!!!! // elimino vecchio record var currRec = fluxLogList.FirstOrDefault(x => x.CodFlux == editFL.CodFlux && x.dtEvento == editFL.dtEvento); if (currRec != null) { fluxLogList.Remove(currRec); // aggiungo nuovo fluxLogList.Add(editFL); } // serializzo nuovamente valore DossierFluxLogDTO? result = new DossierFluxLogDTO(); var ODLflux = result.ODL.ToList(); foreach (var item in fluxLogList) { ODLflux.Add(item); } DossierFluxLogDTO updatedResult = new DossierFluxLogDTO() { ODL = ODLflux }; string rawVal = JsonConvert.SerializeObject(updatedResult); currDoss.Valore = rawVal; // aggiorno record sul DB await dbController.DossiersUpdateValore(currDoss); } activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"updateDossierValue | {source} | {activity?.Duration.TotalMilliseconds}ms"); return answ; } /// /// Elenco completo tabella Vocabolario /// /// public List VocabolarioGetAll() { List? result = new List(); using var activity = ActivitySource.StartActivity("VocabolarioGetAll"); string source = "REDIS"; // cerco in redis... RedisValue rawData = redisDb.StringGet(Utils.redisVocabolario); if (!string.IsNullOrEmpty($"{rawData}")) { result = JsonConvert.DeserializeObject>($"{rawData}"); } else { result = dbController.VocabolarioGetAll(); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(Utils.redisVocabolario, rawData, getRandTOut(redisLongTimeCache / 5)); source = "DB"; } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"VocabolarioGetAll Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Elimina record + svuotamento cache /// /// public bool WipKitDelete(WipSetupKitModel currRecord) { using var activity = ActivitySource.StartActivity("WipKitDelete"); string source = "DB"; bool fatto = false; // salvo fatto = dbController.WipKitDelete(currRecord); // svuoto cache EmptyWipCache(); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"WipKitDelete Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return fatto; } /// /// Elimina i record associati al keyFilt indicato /// /// public bool WipKitDeleteGroup(string KeyFilt) { using var activity = ActivitySource.StartActivity("WipKitDeleteGroup"); string source = "DB"; bool fatto = false; // salvo fatto = dbController.WipKitDeleteGroup(KeyFilt); // svuoto cache EmptyWipCache(); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"WipKitDeleteGroup Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return fatto; } /// /// Elimina i record più vecchi della data-ora indicata /// /// public bool WipKitDeleteOlder(DateTime DateLimit) { using var activity = ActivitySource.StartActivity("WipKitDeleteOlder"); string source = "DB"; bool fatto = false; // salvo fatto = dbController.WipKitDeleteOlder(DateLimit); // svuoto cache EmptyWipCache(); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"WipKitDeleteOlder Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return fatto; } /// /// Elenco Template KIT da ricerca /// /// /// public List WipKitFilt(string KeyFilt) { using var activity = ActivitySource.StartActivity("WipKitFilt"); string source = "DB"; List? result = new List(); // cerco in redis... string currKey = $"{Utils.redisKitWip}:{KeyFilt}"; RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = dbController.WipKitFilt(KeyFilt); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, TimeSpan.FromMinutes(redisLongTimeCache)); } if (result == null) { result = new List(); } activity?.SetTag("data.source", source); activity?.SetTag("result.count", result.Count); activity?.Stop(); LogTrace($"WipKitFilt | {source} | {activity?.Duration.TotalMilliseconds}ms"); return result; } /// /// Esegue salvataggio record + svuotamento cache /// /// public bool WipKitUpsert(WipSetupKitModel currRecord) { using var activity = ActivitySource.StartActivity("WipKitUpsert"); string source = "DB"; bool fatto = false; // salvo fatto = dbController.WipKitUpsert(currRecord); // svuoto cache KitWip EmptyWipCache(); activity?.SetTag("data.source", source); activity?.Stop(); LogTrace($"WipKitUpsert | {source} | {activity?.Duration.TotalMilliseconds}ms"); return fatto; } #endregion Public Methods #region Protected Fields protected Random rand = new Random(); #endregion Protected Fields #region Protected Properties protected string canCacheParametri { get; set; } = ""; #endregion Protected Properties #region Protected Methods /// /// Restituisce un timeout dai minuti richiesti + tempo random -15..+15 sec /// /// /// protected TimeSpan getRandTOut(double stdMinutes) { double rndValue = stdMinutes + (double)rand.Next(-15, 15) / 60; return TimeSpan.FromMinutes(rndValue); } /// /// Merge statistiche Dedup /// /// /// protected bool ProcDedupStatMerge(List procStats) { bool answ = false; List actStats = ProcFLStats(); // se fosse vuoto --> add diretto if (actStats.Count == 0) { actStats.AddRange(procStats); } else { // aggiorno su redis i record statistiche 1:1... foreach (var recStat in procStats) { // cerco se ci fosse x aggiornare var currRec = actStats.Where(x => x.IdxMacchina == recStat.IdxMacchina && x.CodFlux == recStat.CodFlux && x.Interval == recStat.Interval && x.Num4Int == recStat.Num4Int).FirstOrDefault(); // se trovato aggiorno if (currRec != null) { currRec.ProcTime += recStat.ProcTime; currRec.NumRec += recStat.NumRec; } // altrimenti aggiungo else { actStats.Add(recStat); } } } // salvo record statistiche var rawData = JsonConvert.SerializeObject(actStats); string currKey = $"{Utils.redisStatsProcFL}"; redisDb.StringSet(currKey, rawData); return answ; } /// /// Merge statistiche DB Maintenance /// /// /// protected bool RecDbMaintStat(TimeSpan duration) { bool answ = false; Dictionary actStats = DbDedupStats(); // aggiungo record! actStats.Add(DateTime.Now, duration.TotalSeconds); // salvo NUOVO record statistiche string currKey = $"{Utils.redisStatsDbMaint}"; var rawData = JsonConvert.SerializeObject(actStats); redisDb.StringSet(currKey, rawData); return answ; } #endregion Protected Methods #region Private Fields /// /// Oggetto per collezione dati Activity (span in Uptrace) /// private static readonly ActivitySource ActivitySource = new ActivitySource("MP.DATA.Tracer"); private static IConfiguration _configuration = null!; private static Logger Log = LogManager.GetCurrentClassLogger(); private Dictionary _configData = new(); private string MpIoNS = ""; /// /// Oggetto vocabolario x uso continuo traduzione /// private List ObjVocabolario = new List(); /// /// Oggetto per connessione a REDIS /// private ConnectionMultiplexer redisConn = null!; /// /// Oggetto per connessione a REDIS modalità admin (ex flux dati) /// private ConnectionMultiplexer redisConnAdmin = null!; /// /// Oggetto DB redis da impiegare x chiamate R/W /// private IDatabase redisDb = null!; private int redisLongTimeCache = 5; private int redisShortTimeCache = 2; private bool traceEnabled = false; #endregion Private Fields #region Private Methods /// /// Svuota cache creazione KIT /// private void EmptyWipCache() { string pattern = $"{Utils.redisKitWip}:*"; if (!string.IsNullOrEmpty(pattern)) { ExecFlushRedisPattern(pattern); } } private void EnsureConfigLoaded() { if (_configData.Count == 0) { var list = ConfigGetAll(); _configData = list .GroupBy(x => x.Chiave) .ToDictionary(g => g.Key, g => g.First().Valore); } } private async Task EnsureConfigLoadedAsync() { if (_configData.Count == 0) { var list = await ConfigGetAllAsync(); _configData = list .GroupBy(x => x.Chiave) .ToDictionary(g => g.Key, g => g.First().Valore); } } /// /// Helper trace messaggio log (SE abilitato) /// /// private void LogTrace(string traceMsg, NLog.LogLevel? reqLevel = null) { if (!traceEnabled) return; reqLevel ??= NLog.LogLevel.Trace; // Loggo! Log.Log(reqLevel, traceMsg); } private async Task POdlFlushCache() { using var activity = ActivitySource.StartActivity("POdlFlushCache"); bool answ = false; RedisValue pattern = new RedisValue($"{Utils.redisXdlData}:*"); answ = await ExecFlushRedisPatternAsync(pattern); pattern = new RedisValue($"{Utils.redisPOdlByOdl}:*"); answ = await ExecFlushRedisPatternAsync(pattern); pattern = new RedisValue($"{Utils.redisPOdlByPOdl}:*"); answ = await ExecFlushRedisPatternAsync(pattern); pattern = new RedisValue($"{Utils.redisPOdlList}:*"); answ = await ExecFlushRedisPatternAsync(pattern); activity?.SetTag("data.source", "REDIS"); return answ; } private string redHashMpIO(string keyName) { string result = keyName; try { result = $"{MpIoNS}:{keyName}".Replace("\\", "_"); } catch (Exception exc) { Log.Error($"Errore in redHashMpIO{Environment.NewLine}{exc}"); } return result; } private async Task resetCacheArticoli() { using var activity = ActivitySource.StartActivity("resetCacheArticoli"); RedisValue pattern = new RedisValue($"{Utils.redisArtByDossier}:*"); await ExecFlushRedisPatternAsync(pattern); pattern = new RedisValue($"{Utils.redisArtList}:*"); await ExecFlushRedisPatternAsync(pattern); activity?.SetTag("data.source", "REDIS"); } /// /// Reset macchine e gruppi /// private void ResetMacGrpCache() { ExecFlushRedisPattern($"{Utils.redisAnagGruppi}:*"); ExecFlushRedisPattern($"{Utils.redisMacList}:*"); } /// /// Reset cache operatori e gruppi /// private void ResetOprGrpCache() { ExecFlushRedisPattern($"{Utils.redisAnagGruppi}:*"); ExecFlushRedisPattern($"{Utils.redisOprList}:*"); } #endregion Private Methods } }