using Newtonsoft.Json; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Text; namespace IOB_UT_NEXT { public class RedisIobCache { #region Public Constructors /// /// init classe "implicita" /// /// /// public RedisIobCache() { initHashKeys(); } /// /// init classe gestione dati IOB su Redis /// /// IP/nome server /// Cod IOB /// Tipo di IOB /// Minima differenza in secondi x considerare variazione dati DataOra public RedisIobCache(string codServer, string codIob, string tipoIob, int minDeltaS) { // init dati di base... currCodIob = codIob; currIobType = tipoIob; initHashKeys(); // init oggetti gestiti ServerMpStatus newSrvStatus = new ServerMpStatus() { IP = codServer, online = false }; // init oggetto IOB IobWinStatus newIobStatus = new IobWinStatus() { CodIob = currCodIob, IobType = currIobType, online = false, minDeltaSec = minDeltaS }; // salvo in area REDIS servStatus = newSrvStatus; iobStatus = newIobStatus; } #endregion Public Constructors #region Public Enums /// /// Tipologia di ordinamento x liste KVP /// public enum kvpOrderBy { /// /// Ordinamento ASCending per KEY /// KeyAsc, /// /// Ordinamento DESCending per KEY /// KeyDesc, /// /// Ordinamento ASCending per VAL /// ValAsc, /// /// Ordinamento DESCending per VAL /// ValDesc } #endregion Public Enums #region Public Properties /// /// Oggetto DB REDIS corrente /// public IDatabase cache //currDB { get { IDatabase answ; // se già valorizzato uso oggetto private... if (_currDB != null) { answ = _currDB; } else { // init DB (sullo 0) answ = connRedis.GetDatabase(); // gestione override... if (baseUtils.CRI("redisDb") >= 0) { // in questo caso uso il DB configurato in app.config... answ = connRedis.GetDatabase(baseUtils.CRI("redisDb")); } _currDB = answ; } // restituisco oggetto DB return answ; } } /// /// Oggetto statico connessione redis /// public ConnectionMultiplexer connRedis { get { return lazyConnection.Value; } } /// /// Oggetto statico connessione redis /// public ConnectionMultiplexer connRedisAdmin { get { return lazyConnectionAdmin.Value; } } /// /// Accesso all'oggetto stato IOB da esterno /// public IobWinStatus iobStatus { get { IobWinStatus answ = null; // cerco in REDIS string rawData = getRSV(redIobKey); // se non ci fosse creo e salvo if (string.IsNullOrEmpty(rawData)) { answ = new IobWinStatus() { CodIob = currCodIob, IobType = currIobType, online = false }; // salvo serializzando... rawData = JsonConvert.SerializeObject(answ); setRSV(redIobKey, rawData); } else { // deserializzo answ = JsonConvert.DeserializeObject(rawData); } return answ; } set { string rawData = JsonConvert.SerializeObject(value); saveAndSendMessage(redIobKey, redIobChannel, rawData); } } /// /// MEssage Dispatcher: oggetto comunicazione pub/sub via REDIS channels corrente /// public ISubscriber messageDisp { get { ISubscriber answ; // se già valorizzato uso oggetto private... if (_currSub != null) { answ = _currSub; } else { // init DB (sullo 0) answ = connRedis.GetSubscriber(); _currSub = answ; } // restituisco oggetto DB return answ; } } /// /// Restituisce numero record in Redis DB /// public long numRecRedis { get { long answ = 0; try { foreach (var ep in connRedis.GetEndPoints()) { var server = connRedis.GetServer(ep); answ += server.DatabaseSize(); } } catch (Exception exc) { Logging.Instance.Error($"numRecRedis {exc}"); } return answ; } } /// /// Accesso all'oggetto stato server da esterno /// public ServerMpStatus servStatus { get { ServerMpStatus answ = null; // cerco in REDIS string rawData = getRSV(redServKey); // se non ci fosse creo e salvo if (string.IsNullOrEmpty(rawData)) { answ = new ServerMpStatus() { online = false }; // salvo serializzando... rawData = JsonConvert.SerializeObject(answ); setRSV(redServKey, rawData); } else { // deserializzo answ = JsonConvert.DeserializeObject(rawData); } return answ; } set { string rawData = JsonConvert.SerializeObject(value); setRSV(redServKey, rawData); } } #endregion Public Properties #region Public Methods /// /// Effettua comaprazione x VALORE in KVP ASC /// /// /// /// public int CompareVal(KeyValuePair x, KeyValuePair y) { return x.Value.CompareTo(y.Value); } /// /// Effettua comaprazione x VALORE in KVP DESC /// /// /// /// public int CompareValDesc(KeyValuePair x, KeyValuePair y) { return y.Value.CompareTo(x.Value); } /// /// Deserializzazione di un valore string in oggetto generico /// /// /// public object deserializeVal(string serVal) { object answ = ""; try { answ = JsonConvert.DeserializeObject(serVal); } catch (Exception exc) { Logging.Instance.Error($"deserializeVal {exc}"); } return answ; } public int getKReqCount(string keyReq) { string keyReqHash = redHash($"IOB:{currCodIob}:KRCOUNT:{keyReq}"); int answ = 0; string rawData = getRSV(keyReqHash); if (!string.IsNullOrEmpty(rawData)) { int.TryParse(rawData, out answ); } return answ; } /// /// Restituisce una chiave COUNTER in RedisCache /// /// /// public int getRCnt(string chiave) { int answInt = 0; string answ = ""; try { answ = cache.StringGet(chiave); answInt = Convert.ToInt32(answ); } catch (Exception exc) { Logging.Instance.Error($"getRCnt {exc}"); } return answInt; } /// /// Restituisce un pò di info sul server redis connesso /// /// public string getRedisInfoData() { string answ = ""; StringBuilder sb = new StringBuilder(); try { sb.AppendLine($"Configuration: {connRedis.Configuration}"); sb.AppendLine($"Connected: {connRedis.IsConnected}"); sb.AppendLine($"ClientName: {connRedis.ClientName}"); sb.AppendLine($"Total Ops: {connRedis.OperationCount}"); sb.AppendLine($"Status: {connRedis.GetStatus()}"); answ = sb.ToString(); } catch (Exception exc) { Logging.Instance.Error($"getRedisInfoData {exc}"); } return answ; } /// /// Restituisce un set KVP (Key Value Pair) salvati in RedisCache /// /// /// public RedisValue[] getRKeys(RedisKey[] chiavi) { RedisValue[] answ = null; try { answ = cache.StringGet(chiavi); } catch (Exception exc) { Logging.Instance.Error($"getRKeys {exc}"); } return answ; } /// /// Restituisce una chiave salvata in RedisCache /// /// /// public string getRSV(string chiave) { string answ = ""; try { answ = cache.StringGet(chiave); } catch (Exception exc) { Logging.Instance.Info($"getRSV {exc}"); } return answ; } /// /// Conta num oggetti cache redis che rispondono a pattern /// /// ** = tutti /// public int redCountKey(string keyPattern) { int answ = 0; // cerco se ci sia valore in redis... se vuoto = ALL... keyPattern = string.IsNullOrEmpty(keyPattern) ? "**" : keyPattern; try { foreach (var ep in connRedis.GetEndPoints()) { var server = connRedis.GetServer(ep); foreach (var key in server.Keys(pattern: keyPattern)) { answ++; } } } catch (Exception exc) { Logging.Instance.Error($"redCountKey {exc}"); } return answ; } /// /// Elimina una key (hash, string) /// /// /// public bool redDelKey(string key) { bool answ = false; // cerco se ci sia valore in redis... try { RedisKey chiave = key; cache.KeyDelete(chiave); answ = true; } catch (Exception exc) { Logging.Instance.Error($"redDelKey {exc}"); } return answ; } /// /// Flush completo cache redis /// /// ** = tutti /// public bool redFlushKey(string keyPattern) { bool answ = false; // cerco se ci sia valore in redis... se vuoto = ALL... keyPattern = string.IsNullOrEmpty(keyPattern) ? "**" : keyPattern; try { foreach (var ep in connRedis.GetEndPoints()) { var server = connRedis.GetServer(ep); var keys = server.Keys(database: baseUtils.CRI("redisDb"), pattern: $"{keyPattern}*"); foreach (var key in keys) { cache.KeyDelete(key); } } answ = true; } catch (Exception exc) { Logging.Instance.Error($"redFlushKey {exc}"); } return answ; } /// /// Restituisce oggetti cache redis che rispondono a pattern /// /// ** = tutti /// Tipo di ordinamento per kvp /// public List> redGetCounterByKey(string keyPattern, kvpOrderBy orderBy) { int numAnsw = redCountKey(keyPattern); RedisKey[] chiavi = new RedisKey[numAnsw]; List> answ = new List>(); // se vuoto = ALL... keyPattern = string.IsNullOrEmpty(keyPattern) ? "**" : keyPattern; // recupero in primis elenco chiavi try { int i = 0; foreach (var ep in connRedis.GetEndPoints()) { var server = connRedis.GetServer(ep); foreach (var key in server.Keys(pattern: keyPattern)) { chiavi[i] = key; i++; } } } catch (Exception exc) { Logging.Instance.Error($"redGetCounterByKey 01 {exc}"); } // ora recupero valori! var valori = getRKeys(chiavi); int currVal = 0; // popolo rispsota try { for (int i = 0; i < numAnsw; i++) { Int32.TryParse(valori[i], out currVal); answ.Add(new KeyValuePair(chiavi[i], currVal)); } } catch (Exception exc) { Logging.Instance.Error($"redGetCounterByKey 02 {exc}"); } // se richiesto riordino... switch (orderBy) { case kvpOrderBy.KeyAsc: answ.Sort(CompareKey); break; case kvpOrderBy.KeyDesc: answ.Sort(CompareKeyDesc); break; case kvpOrderBy.ValAsc: answ.Sort(CompareVal); break; case kvpOrderBy.ValDesc: answ.Sort(CompareValDesc); break; default: break; } return answ; } /// /// Recupera tutti i valori dalla hash /// /// /// public KeyValuePair[] redGetHash(string hashKey) { KeyValuePair[] answ = new KeyValuePair[1]; // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; HashEntry[] valori = cache.HashGetAll(chiave); answ = new KeyValuePair[valori.Length]; int i = 0; foreach (HashEntry item in valori) { answ[i] = new KeyValuePair(item.Name, item.Value); i++; } } catch (Exception exc) { Logging.Instance.Error($"redGetHash {exc}"); } return answ; } /// /// Recupera tutti i valori dalla hash in formato Dictionary /// /// /// public Dictionary redGetHashDict(string hashKey) { Dictionary answ = new Dictionary(); // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; HashEntry[] valori = cache.HashGetAll(chiave); foreach (HashEntry item in valori) { answ.Add(item.Name, item.Value); } } catch (Exception exc) { Logging.Instance.Error($"redGetHashDict {exc}"); } return answ; } /// /// Recupera UN SINGOLO VALORE dalla hash per un dato field /// /// /// /// public string redGetHashField(string hashKey, string hashField) { string answ = ""; // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; RedisValue campo = hashField; RedisValue valOut = cache.HashGet(chiave, campo); answ = valOut.ToString(); } catch (Exception exc) { Logging.Instance.Error($"redGetHashField {exc}"); } return answ; } /// /// Nome della variabile HASH da utilizzare (dato CodModulo / Server / DB impiegato da /// funzionalita' DbConfig) + keyName richiesto... /// public string redHash(string keyName) { keyName = keyName.Replace("\\", "_"); string answ = keyName; try { answ = string.Format($"{baseUtils.CRS("appName")}:{keyName}"); } catch (Exception exc) { Logging.Instance.Error($"redHash {exc}"); } return answ; } /// /// Verifica se ci siano valori nella hash indicata... /// /// /// public bool redHashPresent(RedisKey key) { bool answ = false; // cerco se ci sia valore in redis... try { answ = cache.HashGetAll(key).Length > 0; } catch (Exception exc) { Logging.Instance.Error($"redHashPresent {exc}"); } return answ; } /// /// Verifica se ci siano valori nella hash indicata (string) /// /// /// public bool redHashPresentSz(string key) { bool answ = false; try { RedisKey chiave = key; answ = redHashPresent(chiave); } catch (Exception exc) { Logging.Instance.Error($"redHashPresentSz {exc}"); } return answ; } /// /// Verifica se ci siano valori nella KEY indicata... /// /// /// public bool redKeyPresent(RedisKey key) { bool answ = false; // cerco se ci sia valore in redis... try { answ = cache.KeyExists(key); } catch (Exception exc) { Logging.Instance.Error($"redKeyPresent {exc}"); } return answ; } /// /// Verifica se ci siano valori nella KEY indicata (string) /// /// /// public bool redKeyPresentSz(string key) { bool answ = false; try { RedisKey chiave = key; answ = redKeyPresent(chiave); } catch (Exception exc) { Logging.Instance.Error($"redKeyPresentSz {exc}"); } return answ; } /// /// Salvataggio di una hash di valori /// /// chiave /// valori /// public bool redSaveHash(string hashKey, KeyValuePair[] hashFields) { bool answ = false; // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; HashEntry[] valori = new HashEntry[hashFields.Length]; int i = 0; foreach (KeyValuePair kvp in hashFields) { valori[i] = new HashEntry(kvp.Key, kvp.Value); i++; } cache.HashSet(chiave, valori); answ = true; } catch (Exception exc) { Logging.Instance.Error($"redSaveHash {exc}"); } return answ; } /// /// Salvataggio di una hash di valori /// /// chiave /// valori /// /// scadenza preimpostata hash (secondi) | defaoult = -1 (non scade) /// /// public bool redSaveHash(string hashKey, KeyValuePair[] hashFields, double expireSeconds = -1) { bool answ = false; // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; answ = redSaveHash(hashKey, hashFields); if (expireSeconds > 0) { cache.KeyExpire(chiave, DateTime.Now.AddSeconds(expireSeconds)); } } catch (Exception exc) { Logging.Instance.Error($"redSaveHash {exc}"); } return answ; } /// /// Salvataggio di una hash di valori in formato Dictionary /// /// chiave /// valori /// public bool redSaveHashDict(string hashKey, Dictionary hashFields) { bool answ = false; // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; HashEntry[] valori = new HashEntry[hashFields.Count]; int i = 0; foreach (KeyValuePair kvp in hashFields) { valori[i] = new HashEntry(kvp.Key, kvp.Value); i++; } cache.HashSet(chiave, valori); answ = true; } catch (Exception exc) { Logging.Instance.Error($"redSaveHashDict {exc}"); } return answ; } /// /// Salvataggio di una hash di valori in formato Dictionary /// /// chiave /// valori /// /// scadenza preimpostata hash (secondi) | defaoult = -1 (non scade) /// /// public bool redSaveHashDict(string hashKey, Dictionary hashFields, double expireSeconds = -1) { bool answ = false; // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; answ = redSaveHashDict(hashKey, hashFields); if (expireSeconds > 0) { cache.KeyExpire(chiave, DateTime.Now.AddSeconds(expireSeconds)); } //answ = true; } catch (Exception exc) { Logging.Instance.Error($"redSaveHashDict {exc}"); } return answ; } /// /// Salvataggio di una hash di valori /// /// chiave /// valori come lista KVP /// public bool redSaveHashList(string hashKey, List> hashListKVP) { bool answ = false; if (connRedis.IsConnected) { // cerco se ci sia valore in redis... IDatabase cache = connRedis.GetDatabase(); try { RedisKey chiave = hashKey; HashEntry[] valori = new HashEntry[hashListKVP.Count]; int i = 0; foreach (KeyValuePair kvp in hashListKVP) { valori[i] = new HashEntry(kvp.Key, kvp.Value); i++; } cache.HashSet(chiave, valori); answ = true; } catch (Exception exc) { Logging.Instance.Error($"redSaveHashList {exc}"); } } return answ; } /// /// Restituisce info dei server connessi... /// /// public IServer[] redServInfo() { IServer[] answ = new IServer[1]; try { answ = new IServer[connRedisAdmin.GetEndPoints().Length]; int i = 0; foreach (var ep in connRedisAdmin.GetEndPoints()) { var server = connRedisAdmin.GetServer(ep); answ[i] = server; i++; } } catch (Exception exc) { Logging.Instance.Error($"redServInfo {exc}"); } return answ; } /// /// Resetta (elimina) un contatore in Redis /// /// /// public bool resetRCnt(string chiave) { bool answ = false; try { answ = cache.KeyDelete(chiave); } catch (Exception exc) { Logging.Instance.Error($"resetRCnt {exc}"); } return answ; } /// /// Invia un messaggio tramite notify channel + salva in cache (se scaduto periodo veto) /// /// /// /// /// public bool saveAndSendMessage(string memKey, string notifyChannel, string message) { bool fatto = false; // effettuo la scrittura nell'area di memoria indicata SE passato intervallo minimo bool doSave = true; if (LastKeySave.ContainsKey(memKey)) { if (DateTime.Now.Subtract(LastKeySave[memKey]).TotalSeconds < 1) { doSave = false; } } else { LastKeySave.Add(memKey, DateTime.Now); } if (doSave) { cache.StringSet(memKey, message); LastKeySave[memKey] = DateTime.Now; Logging.Instance.Trace($"Redis Cache Key: {memKey}"); } // invio notifica tramite il canale richiesto messageDisp.Publish(notifyChannel, message); fatto = true; // restituisco! return fatto; } /// /// Serializzazione di un oggetto generico /// /// /// public string serializeVal(object origVal) { string answ = ""; try { answ = JsonConvert.SerializeObject(origVal); } catch (Exception exc) { Logging.Instance.Error($"serializeVal {exc}"); } return answ; } public bool setKReqCount(string keyReq, int val) { string keyReqHash = redHash($"IOB:{currCodIob}:KRCOUNT:{keyReq}"); // salvo ogni chiave x 25 h (in sec 60*60*25) bool done = setRSV(keyReqHash, $"{val}", 60 * 60 * 25); return done; } /// /// Decrementa un contatore in Redis /// /// /// public long setRCntD(string chiave) { long answ = 0; try { answ = cache.StringDecrement(chiave, 1); } catch (Exception exc) { Logging.Instance.Error($"setRCntD {exc}"); } return answ; } /// /// Incrementa un contatore in Redis /// /// /// public long setRCntI(string chiave) { long answ = 0; try { answ = cache.StringIncrement(chiave, 1); } catch (Exception exc) { Logging.Instance.Error($"setRCntI {exc}"); } return answ; } /// /// Salva un set KVP (Key Value Pair) in RedisCache /// /// Set KVP chiave-valore da salvare /// public bool setRKeys(KeyValuePair[] valori) { bool answ = false; try { cache.StringSet(valori); answ = true; } catch (Exception exc) { Logging.Instance.Error($"setRKeys {exc}"); } return answ; } /// /// Salva una chiave in RedisCache /// /// /// /// public bool setRSV(string chiave, string valore) { bool answ = false; try { cache.StringSet(chiave, valore); answ = true; } catch (Exception exc) { Logging.Instance.Error($"setRSV {exc}"); } return answ; } /// /// Salva una chiave in RedisCache /// /// /// /// in secondi /// public bool setRSV(string chiave, string valore, int TTL_sec) { bool answ = false; try { TimeSpan expT = new TimeSpan(0, 0, TTL_sec); // salvo con expyry... cache.StringSet(chiave, valore, expT); answ = true; } catch (Exception exc) { Logging.Instance.Error($"setRSV {exc}"); } return answ; } #endregion Public Methods #region Protected Fields /// /// Cod IOB /// protected string currCodIob = "000"; /// /// Tipo IOB /// protected string currIobType = "ND"; /// /// Hash REDIS x dati IOB /// protected string redIobKey = ""; /// /// Nome del channel REDIS x dati IOB /// protected string redIobChannel = "IobChannel"; /// /// Hash REDIS x dati server /// protected string redServKey = ""; #endregion Protected Fields #region Private Fields /// /// Dizionario delle ultime scritture in cache redis dei messaggi /// private Dictionary LastKeySave = new Dictionary(); /// /// Connessione lazy a redis... /// private Lazy lazyConnection = new Lazy(() => { string RedisConn = baseUtils.CRS("RedisConn"); if (string.IsNullOrEmpty(RedisConn)) { RedisConn = "localhost,abortConnect=false,ssl=false"; } return ConnectionMultiplexer.Connect(RedisConn); }); /// /// Connessione lazy a redis... /// private Lazy lazyConnectionAdmin = new Lazy(() => { string RedisConnAdmin = baseUtils.CRS("RedisConnAdmin"); if (string.IsNullOrEmpty(RedisConnAdmin)) { RedisConnAdmin = "localhost,abortConnect=false,ssl=false,allowAdmin=true"; } return ConnectionMultiplexer.Connect(RedisConnAdmin); }); #endregion Private Fields #region Private Properties /// /// Oggetto currentDb REDIS locale /// private IDatabase _currDB { get; set; } /// /// Oggetto subscriber x pubblicazione/sottoscrizione canali REDIS /// private ISubscriber _currSub { get; set; } #endregion Private Properties #region Private Methods /// /// Effettua comaprazione x CHIAVE in KVP ASC /// /// /// /// private int CompareKey(KeyValuePair x, KeyValuePair y) { return x.Key.CompareTo(y.Key); } /// /// Effettua comaprazione x CHIAVE in KVP DESC /// /// /// /// private int CompareKeyDesc(KeyValuePair x, KeyValuePair y) { return y.Key.CompareTo(x.Key); } private void initHashKeys() { // init dati di base... redServKey = redHash($"MP"); redIobKey = redHash($"IOB:{currCodIob}"); redIobChannel = $"IobChannel_{currCodIob}"; } #endregion Private Methods } }