using NLog; using RestSharp; using System; using System.Collections; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.NetworkInformation; using System.Security.Cryptography; using System.Security.Policy; using System.Text; using System.Threading; using System.Threading.Tasks; using YamlDotNet.Core.Tokens; namespace IOB_UT_NEXT { public class baseUtils { #region Public Fields /// /// Indicazione VETO PING a server sino alla data-ora indicata /// public static DateTime dtVetoPing = DateTime.Now; /// /// Indicazione VETO accodamento INGRESSI/EVENTI sino alla data-ora indicata /// public static DateTime dtVetoQueueIN = DateTime.Now; /// /// Indicazione VETO invio a server sino alla data-ora indicata /// public static DateTime dtVetoSend = DateTime.Now; /// /// Status IOB /// public static bool IOB_Online = false; /// /// Status MP_IO /// public static bool MPIO_Online = false; #endregion Public Fields #region Public Properties /// /// Calcola una pausa con errore casuale x il prossimo send programmato verso SERVER /// public static int nextPauseSendMSec { get { // parto dal dato std di veto per pauseSendMSec int value = CRI("pauseSendMSec"); Random rnd = new Random(); // NextDouble() ∈ [0.0, 1.0) → fattore ∈ [-1.0, 1.0) double factor = rnd.NextDouble() * 2.0 - 1.0; // ±10% return (int)(value * (1.0 + 0.20 * factor)); } } #endregion Public Properties #region Public Methods /// /// formatta un numero in forma binaria 0/1 /// /// Il valore numerico (int, uint, short, byte, ecc.) /// La stringa binaria formattata public static string binaryForm(T valore) where T : struct { try { // Il compilatore passerà automaticamente il tipo corretto (int, uint, ecc.) // alla tua classe BinaryUtils, che lo gestirà nel suo blocco switch. return string.Format(new BinaryUtils(), "{0:B}", valore); } catch { return string.Empty; } } /// /// Test esistenza/creazione directory /// /// public static void checkDir(string dirPath) { if (!Directory.Exists(dirPath)) { lg.Info($"Dir not found: {dirPath}"); Directory.CreateDirectory(dirPath); lg.Info($"reated local dir {dirPath}"); } } /// /// Conteggio valori true in un array di boolean /// /// /// public static int CountTrue(params bool[] args) { return args.Count(t => t); } /// /// legge conf in formato BOOLean /// /// /// public static bool CRB(string key) { bool answ = false; try { answ = Convert.ToBoolean(CRS(key)); } catch { } return answ; } /// /// legge conf in formato char /// /// /// public static char CRC(string key) { char answ = '-'; try { answ = ConfigurationManager.AppSettings[key].ToCharArray()[0]; } catch { } return answ; } /// /// legge conf in formato INT /// /// /// public static Int32 CRI(string key) { int answ = 0; try { answ = Convert.ToInt32(CRS(key)); } catch { } return answ; } /// /// legge conf in formato stringa /// /// /// public static string CRS(string key) { return ConfigurationManager.AppSettings[key] ?? string.Empty; } /// /// Calcolo MD5 del fine indicato /// /// /// public static string GetFileMd5(string fileName) { string answ = ""; using (var md5 = MD5.Create()) { using (var stream = File.OpenRead(fileName)) { var hash = md5.ComputeHash(stream); answ = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); } } return answ; } /// /// Restituisce la prima cifra (a sx) da un numero (es x decodere quale memoria modbus sia) /// /// Numero di cui trovare la priam cifra /// public static int getFirstInt(int num) { if (num >= 100000000) num /= 100000000; if (num >= 10000) num /= 10000; if (num >= 100) num /= 100; if (num >= 10) num /= 10; return num; } /// /// Restituisce una stringa di soli caratteri numerici (stripe caratteri alfabetici) /// /// /// public static string GetNumbers(string input) { return new string(input.Where(c => char.IsDigit(c)).ToArray()); } /// /// Method to convert an integer to a string containing the number in binary. A negative /// number will be formatted as a 32-character binary number in two's compliment. /// /// self-explanatory /// /// if binary number contains fewer characters leading zeros are added /// /// string as described above public static string IntToBinStr(int theNumber, int minimumDigits) { return Convert.ToString(theNumber, 2).PadLeft(minimumDigits, '0'); } /// /// verifica se un dato bit sia alzato (come flag di strobe) /// /// valore da testare /// /// valore cercato, può essere un singolo valore o un insieme in modalità AND /// /// public static bool IsSetAll(StFlag32 value, StFlag32 flag) { return ((value & flag) == flag); } /// /// verifica se un dato bit sia alzato (come flag di strobe) /// /// valore da testare /// /// valore cercato, può essere un singolo valore o un insieme in modalità OR /// /// public static bool IsSetAny(StFlag32 value, StFlag32 flag) { return ((value & flag) != 0); } /// /// Test ping x indirizzo indicato /// /// /// public static bool pingAddress(IPAddress address) { bool answ = false; try { Ping pingSender = new Ping(); PingReply reply = pingSender.Send(address, 100); // se passa il ping do OK... if (reply.Status == IPStatus.Success) { answ = true; } } catch (Exception exc) { // controllo log permesso... if (logValuePermit("pingAddr")) { lg.Error(exc); } } return answ; } /// /// Lettura dictionary da file /// /// /// public static Dictionary ReadBin(string file) { var result = new Dictionary(); // verifico file esista... if (!File.Exists(file)) { FileStream fs = File.Create(file); fs.Close(); } using (FileStream fs = File.OpenRead(file)) using (BinaryReader reader = new BinaryReader(fs)) { // Get count. int count = 0; try { count = reader.ReadInt32(); } catch { } // Read in all pairs. for (int i = 0; i < count; i++) { string key = reader.ReadString(); string value = reader.ReadString(); result[key] = value; } } return result; } /// /// Lettura dictionary da file /// /// /// public static Dictionary ReadPlain(string file) { // inizializzo num righe lette... int numRow = 0; var result = new Dictionary(); // verifico file esista... if (!File.Exists(file)) { FileStream fs = File.Create(file); fs.Close(); } try { string[] lines = File.ReadAllLines(file); result = lines.Select(l => l.Split(':')).ToDictionary(a => a[0], a => a[1]); numRow = result.Count; } catch { } // se leggesse un valore NON coerente (senza righe) restituisce un file vuoto... if (numRow == 0) { result = new Dictionary(); } return result; } /// /// Effettua reverse della stringa /// /// /// public static string reverseStr(string s) { char[] arr = s.ToCharArray(); Array.Reverse(arr); return new string(arr); } /// /// imposta un bit al valore richiesto duplicando il valore IN come OUT /// /// valore originale da aggiornare /// valore richiesto x il bit (0/1) /// indice bit, 0 based (es: 0..31 per 32bit) /// public static byte[] setBitOnStFlag(byte[] original, bool bitBool, int bitIndex) { int bitVal = 0; if (bitBool) { bitVal = 1; } // risposta è identica ad originale... byte[] answ = original; // verifico se il bit è 0/1b if (bitVal <= 1 && bitVal >= 0) { // verifico se si possa aggiornare il bit richiesto (<= al totale dei bit...) if (bitIndex <= original.Length * 8 - 1) { // calcolo byte int byteIndex = bitIndex / 8; // bit nel byte int bitInByteIndex = bitIndex % 8; // bit richiesto byte mask = (byte)(bitVal << bitInByteIndex); // imposto! answ[byteIndex] |= mask; } } return answ; } /// /// provvede a verificare la dim della cartella dei log e cancella i + vecchi fino a restare /// a dim inferiori a _logMaxMb /// public static void shrinkDir(string dirPath) { // obj filemover... fileMover.obj.setDirectory(dirPath); float dirSizeMb = fileMover.obj.totalMb(); lg.Info("Inizio shrinkDir LOG folder: {0} Mb", dirSizeMb); // ottengo elenco files *.txt FileInfo[] _fis = fileMover.obj.elencoFiles_FI("*.log"); int numDdMax = 2; try { numDdMax = CRI("zipLogOldDay"); } catch { } foreach (FileInfo _file in _fis) { if (_file.LastWriteTime < DateTime.Now.AddDays(-1)) // zippo files + vecchi di 2 gg... { fileMover.obj.zippaSingoloFile(_file); // cancello l'originale... fileMover.obj.eliminaFile(_file); } } // inizio con eliminare file + vecchi della data indicata... int maxLogDays = CRI("maxLogDays"); fileMover.obj.deleteOlderThan(maxLogDays); // ora controllo SE sia superata la dim max della directory --> in tal caso cancello dal // + vecchio... dirSizeMb = fileMover.obj.totalMb(); int maxLogDirSize = CRI("maxLogDirSize"); int maxTry = 100; // controllo se serva eliminare... if (dirSizeMb > maxLogDirSize) { lg.Info("Continuo shrinkDir LOG folder: {0} Mb --> ELIMINAZIONE FILES", dirSizeMb); while (dirSizeMb > maxLogDirSize) { fileMover.obj.deleteOldest(); maxTry--; if (maxTry > 0) { dirSizeMb = fileMover.obj.totalMb(); } else { // per uscire fingo di aver ridotto... dirSizeMb = maxLogDirSize - 1; } } dirSizeMb = fileMover.obj.totalMb(); lg.Info("Completata shrinkDir LOG folder: {0} Mb", dirSizeMb); } } /// /// Restitusice la stringa prima dell'ultima occorrenza del char richiesto /// /// string originale /// char separatore da cercare /// public static string StringBeforeLastChar(string origString, char sepChar) { int index = origString.LastIndexOf(sepChar); string answ = (index == -1) ? origString : origString.Substring(0, index); return answ; } /// /// Rimuove query string e fragment in modo ottimizzato. /// Compatibile .NET 4.7 / C# 7.3 /// public static string StripQueryAndFragment(string url) { if (string.IsNullOrEmpty(url)) return url; // ? è il separator standard per le query string int qIndex = url.IndexOf('?'); if (qIndex >= 0) return url.Substring(0, qIndex); // Gestione opzionale di fragment # (es. #section) int hIndex = url.IndexOf('#'); return hIndex >= 0 ? url.Substring(0, hIndex) : url; } /// /// Converte un bitarray a byte[] /// /// /// public static byte[] ToByteArray(BitArray bits) { int numBytes = bits.Count / 8; if (bits.Count % 8 != 0) { numBytes++; } byte[] bytes = new byte[numBytes]; int byteIndex = 0, bitIndex = 0; for (int i = 0; i < bits.Count; i++) { if (bits[i]) { bytes[byteIndex] |= (byte)(1 << (7 - bitIndex)); } bitIndex++; if (bitIndex == 8) { bitIndex = 0; byteIndex++; } } return bytes; } /// /// Scrittura dictionary su file /// /// /// public static void WriteBin(Dictionary dictionary, string file) { using (FileStream fs = File.OpenWrite(file)) using (BinaryWriter writer = new BinaryWriter(fs)) { // Put count. writer.Write(dictionary.Count); // Write pairs. foreach (var pair in dictionary) { writer.Write(pair.Key); writer.Write(pair.Value); } } } /// /// Scrittura dictionary su file /// /// /// public static void WritePlain(Dictionary dictionary, string file) { string dirPath = file.Substring(0, file.LastIndexOf('\\')); // verifico directory checkDir(dirPath); string[] lines = dictionary.OrderBy(i => i.Key).Select(kvp => kvp.Key + ":" + kvp.Value).ToArray(); //string[] lines = dictionary.OrderBy(i => i.Key).Select(kvp => kvp.Key + ":" + kvp.Value).ToArray(); try { File.WriteAllLines(file, lines); } catch (Exception exc) { lg.Info(exc, string.Format("Errore in scrittura file {0}", file)); } } #endregion Public Methods #region Internal Fields /// /// Classe logger /// internal static Logger lg = LogManager.GetCurrentClassLogger(); #endregion Internal Fields #region Internal Methods /// /// Verifica se il log di un dato errore sia permesso /// /// ID del valore log da loggare/verificare /// internal static bool logValuePermit(string logKey) { bool doLog = false; if (vetoLogError.ContainsKey(logKey)) { // verifico se veto scaduto... if (DateTime.Now > vetoLogError[logKey]) { doLog = true; vetoLogError[logKey] = DateTime.Now.AddMinutes(vetoPeriodMin); } } else { doLog = true; vetoLogError.Add(logKey, DateTime.Now.AddMinutes(vetoPeriodMin)); } return doLog; } /// /// Traccia le chiamate URL (in ram) dall'ultimo riavvio, ogni ora scrive log /// /// URL chiamato /// durata chiamata internal static void TrackUrlCall(string rawUrl, TimeSpan elapsed) { // per l'URL tengo solo fino a prima dei parametri (prima di ?) string url = StripQueryAndFragment(rawUrl); // attenzione: x ora cablato 1 h per log pareto DateTime adesso = DateTime.Now; string HourCurr = $"{adesso:ddHH}"; // se cambiata ora --> forzo scrittura senza aggiunte if (!LastHourCurr.Equals(HourCurr)) { // recupero pareto chiamate... var callList = CallMetricsCollector.LogPareto(); if (callList != null) { StringBuilder sb = new StringBuilder(); sb.AppendLine(); sb.AppendLine("------------------- CALL STATS ---------------------"); foreach (var item in callList) { sb.AppendLine($"num: {item.Count} | CumSum: {item.CumulativeParetoPct:P2} | avg: {item.AvgElapsed.TotalMilliseconds:N1} ms | {item.Key}"); } sb.AppendLine("------------------- CALL STATS ---------------------"); lg.Info(sb.ToString()); } LastHourCurr = HourCurr; } // accumulo stat oraria CallMetricsCollector.AddCall(url, elapsed); } #endregion Internal Methods #region Private Fields /// /// Ultima ora registrata x statistiche track urlCall /// private static string LastHourCurr = ""; /// /// Dizionario dei valori bloccati x evitare log eccessivo /// private static Dictionary vetoLogError = new Dictionary(); /// /// Periodo di veto log in minuti /// private static int vetoPeriodMin = 30; #endregion Private Fields } }