using Microsoft.Extensions.Configuration; using MP.Core.Conf; using MP.Data.Controllers; using MP.Data.DbModels; using Newtonsoft.Json; using NLog; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MP.Data.Services { public class StatusData : IDisposable { #region Public Constructors public StatusData(IConfiguration configuration, IConnectionMultiplexer redConn) { _configuration = configuration; // setup componenti REDIS this.redisConn = redConn; redisDb = redisConn.GetDatabase(); // conf DB string connStr = _configuration.GetConnectionString("MP.Mon"); // se non trovo cerco con MP.All... if (string.IsNullOrEmpty(connStr)) { connStr = _configuration.GetConnectionString("MP.All"); } if (string.IsNullOrEmpty(connStr)) { Log.Error("ConnString empty! [MP.Mon / MP.All]"); } else { dbController = new MpMonController(configuration); StringBuilder sb = new StringBuilder(); sb.AppendLine($"StatusData | MpMonController OK"); Log.Info(sb.ToString()); } // setup time refresh if (!string.IsNullOrEmpty(_configuration.GetValue("ServerConf:maxAge"))) { maxAge = _configuration.GetValue("ServerConf:maxAge"); } else if (!string.IsNullOrEmpty(_configuration.GetValue("OptConf:msRefresh"))) { maxAge = _configuration.GetValue("OptConf:msRefresh"); } // sistemo i timespan LongCache = TimeSpan.FromSeconds(300); FastCache = TimeSpan.FromMilliseconds(maxAge); // setup conf IOB da dizionario tryLoadIobTags(); } #endregion Public Constructors #region Public Properties public static MpMonController dbController { get; set; } = null!; /// /// Dizionario dei tag configurati per IOB /// public Dictionary> currTagConf { get; set; } = new Dictionary>(); #endregion Public Properties #region Public Methods /// /// Recupero elenco config /// /// public async Task> ConfigGetAll() { string source = "DB"; Stopwatch sw = new Stopwatch(); sw.Start(); List? result = new List(); // cerco in _redisConn... string currKey = $"{redisBaseKey}:Conf"; RedisValue rawData = await redisDb.StringGetAsync(currKey); //if (!string.IsNullOrEmpty($"{rawData}")) if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = dbController.ConfigGetAll(); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, LongCache); } if (result == null) { result = new List(); } sw.Stop(); Log.Debug($"ConfigGetAll | {source} | {sw.Elapsed.TotalMilliseconds}ms"); return result; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Pulizia cache Redis (tutta) /// /// public async Task FlushCache() { RedisValue pattern = new RedisValue($"{redisBaseKey}:*"); bool answ = await ExecFlushRedisPattern(pattern); return answ; } /// /// Pulizia cache Redis per chiave specifica (da redisBaseKey...) /// /// public async Task FlushCache(string KeyReq) { RedisValue pattern = new RedisValue($"{redisBaseKey}:{KeyReq}:*"); bool answ = await ExecFlushRedisPattern(pattern); return answ; } /// /// Elenco setup dei tag conf correnti /// /// public Task>> getAllTags() { return Task.FromResult(currTagConf); } /// /// restituisce il valore da REDIS associato al tag richeisto /// /// Chiave in cui cercare il valore /// public string getTagConf(string redKey) { string outVal = ""; // cerco in REDIS la conf x l'IOB var rawData = redisDb.StringGet(redKey); if (!string.IsNullOrEmpty(rawData)) { outVal = $"{rawData}"; } return outVal; } public async Task> MacchineGetAll() { Stopwatch sw = new Stopwatch(); string source = "DB"; sw.Start(); List? result = new List(); // cerco in _redisConn... string currKey = $"{Constants.redisMacchine}:ALL"; RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await Task.FromResult(dbController.MacchineGetAll()); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); await redisDb.StringSetAsync(currKey, rawData, LongCache); } if (result == null) { result = new List(); } sw.Stop(); Log.Debug($"MacchineGetAll | {source} | {sw.Elapsed.TotalMilliseconds}ms"); return result; //return Task.FromResult(dbController.MacchineGetAll()); } public async Task> MacchineGetByGruppo(string CodGruppo) { Stopwatch sw = new Stopwatch(); string source = "DB"; sw.Start(); List? result = new List(); // cerco in _redisConn... string currKey = $"{Constants.redisMacchine}:{CodGruppo}"; RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) { result = JsonConvert.DeserializeObject>($"{rawData}"); source = "REDIS"; } else { result = await Task.FromResult(dbController.MacchineGetFilt(CodGruppo)); //result = dbController.MacchineGetFilt(CodGruppo); // serializzo e salvo... rawData = JsonConvert.SerializeObject(result); await redisDb.StringSetAsync(currKey, rawData, LongCache); } if (result == null) { result = new List(); } sw.Stop(); Log.Debug($"MacchineGetByGruppo | {source} | {sw.Elapsed.TotalMilliseconds}ms"); return result; } /// /// Restituisce numPezzi (ultimo) x macchina /// /// /// public int MachNumPzGet(string idxMacchina) { int answ = 0; if (MachineNumPz.ContainsKey(idxMacchina)) { answ = MachineNumPz[idxMacchina]; } return answ; } /// /// Salva numPezzi x macchina /// /// /// /// public bool MachNumPzSet(string idxMacchina, int numPz) { bool answ = false; if (MachineNumPz.ContainsKey(idxMacchina)) { MachineNumPz[idxMacchina] = numPz; } else { MachineNumPz.Add(idxMacchina, numPz); } answ = true; return answ; } /// /// Restituisce stato prod x NumPezzi (ultimo) x macchina /// /// /// public StatoProdModel MachProdStGet(string idxMacchina) { StatoProdModel answ = new StatoProdModel(); if (MachineProdStatus.ContainsKey(idxMacchina)) { answ = MachineProdStatus[idxMacchina]; } return answ; } /// /// Elimina info numPezzi x macchina /// /// /// public bool MachProdStRem(string idxMacchina) { bool answ = false; if (MachineProdStatus.ContainsKey(idxMacchina)) { MachineProdStatus.Remove(idxMacchina); } answ = true; return answ; } /// /// Salva numPezzi x macchina /// /// /// /// public bool MachProdStSet(string idxMacchina, StatoProdModel currStatus) { bool answ = false; if (MachineProdStatus.ContainsKey(idxMacchina)) { MachineProdStatus[idxMacchina] = currStatus; } else { MachineProdStatus.Add(idxMacchina, currStatus); } answ = true; return answ; } /// /// Elenco da tabella MappaStatoExplModel /// /// public async Task> MseGetAll(bool forceDb = false) { Stopwatch sw = new Stopwatch(); string source = "DB"; sw.Start(); List? result = new List(); // cerco in _redisConn... RedisValue rawData = await redisDb.StringGetAsync(Constants.redisMseKey); if (rawData.HasValue && !forceDb) { result = JsonConvert.DeserializeObject>($"{rawData}") ?? new(); source = "REDIS"; } else { result = await dbController.MseGetAllAsync(maxAge); // serializzp e salvo... rawData = JsonConvert.SerializeObject(result); await redisDb.StringSetAsync(Constants.redisMseKey, rawData, UltraFastCache); } if (result == null) { result = new List(); } sw.Stop(); Log.Debug($"MseGetAllAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms"); return result; } #endregion Public Methods #region Protected Fields /// /// Oggetto per connessione a REDIS /// protected IConnectionMultiplexer redisConn = null!; /// /// Oggetto DB redis da impiegare x chiamate R/W /// protected IDatabase redisDb = null!; #endregion Protected Fields #region Protected Methods protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { // Free managed resources here currTagConf.Clear(); MachineNumPz.Clear(); MachineProdStatus.Clear(); // REDIS dispose redisDb = null; // Clear database controller dbController.Dispose(); } // Free unmanaged resources here _disposed = true; } } #endregion Protected Methods #region Private Fields private static IConfiguration _configuration = null!; private static Logger Log = LogManager.GetCurrentClassLogger(); private bool _disposed = false; /// /// Cache breve durata /// private TimeSpan FastCache = TimeSpan.FromSeconds(5); /// /// Cache lunga durata /// private TimeSpan LongCache = TimeSpan.FromSeconds(60); private int maxAge = 2000; private string redisBaseKey = "MP:ALL:Cache"; /// /// Cache brevissima durata /// private TimeSpan UltraFastCache = TimeSpan.FromMilliseconds(1500); #endregion Private Fields #region Private Properties private Dictionary MachineNumPz { get; set; } = new Dictionary(); private Dictionary MachineProdStatus { get; set; } = new Dictionary(); #endregion Private Properties #region Private Methods /// /// Esegue flush memoria _redisConn dato pat2Flush /// /// /// private async Task ExecFlushRedisPattern(RedisValue pat2Flush) { 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 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; #if false var listEndpoints = redisConn.GetEndPoints(); foreach (var endPoint in listEndpoints) { //var server = redisConnAdmin.GetServer(listEndpoints[0]); var server = redisConn.GetServer(endPoint); if (server != null) { var keyList = server.Keys(redisDb.Database, pattern); foreach (var item in keyList) { await redisDb.KeyDeleteAsync(item); } answ = true; } } #endif return answ; } /// /// Prova a caricare da file la conf degli IOB se presente /// private void tryLoadIobTags() { Dictionary> currConf = new Dictionary>(); string strExeFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location; if (!string.IsNullOrEmpty(strExeFilePath)) { string? strWorkPath = Path.GetDirectoryName(strExeFilePath); if (!string.IsNullOrEmpty(strWorkPath)) { string filePath = $"{strWorkPath}/Conf/iobTagsConf.json"; if (File.Exists(filePath)) { string rawData = File.ReadAllText(filePath); if (!string.IsNullOrEmpty(rawData)) { var fileConfData = JsonConvert.DeserializeObject(rawData); if (fileConfData != null) { // effettuo esplosione conf SE contenesse il valore "***" = tutti // gli IOB if (fileConfData.IobSetup.ContainsKey("***")) { // recupero elenco macchine... var elencoMacc = dbController.MacchineGetAll(); // x ogni macchina creo le righe standard da conf... var baseConf = fileConfData.IobSetup.Where(x => x.Key == "***").FirstOrDefault(); foreach (var item in elencoMacc) { if (!string.IsNullOrEmpty(item.IdxMacchina)) { // converto i valori x la macchina corrente... clono in // nuovo oggetto var specVal = baseConf.Value.Select(i => i.Clone()).ToList(); // sostituisco segnaposto foreach (var singleVal in specVal) { singleVal.TagLocation = singleVal.TagLocation.Replace("***", item.IdxMacchina); } // ora aggiungo eventuali valori in override... if (fileConfData.IobSetup.ContainsKey(item.IdxMacchina)) { var otConf = fileConfData.IobSetup.Where(x => x.Key == item.IdxMacchina).FirstOrDefault(); //verifico x ogni valore other... foreach (var otTag in otConf.Value) { var ovrTag = specVal.Where(x => x.ColNum == otTag.ColNum && x.RowNum == otTag.RowNum).FirstOrDefault(); // se contiene --> sovrascrivo if (ovrTag != null) { //ovrTag = otTag.Clone(); specVal.Remove(ovrTag); specVal.Add(otTag.Clone()); } // se non contiene --> aggiungo else { specVal.Add(otTag); } } } currConf.Add(item.IdxMacchina, specVal); } } } // altrimenti copio ed ho finito else { currConf = fileConfData.IobSetup; } } } } if (currConf != null) { currTagConf = currConf; } } } } #endregion Private Methods } }