using Microsoft.Extensions.Configuration; using MP.MONO.ADAPTER; using MP.MONO.Core; using MP.MONO.Core.CONF; using MP.MONO.Core.DTO; using Newtonsoft.Json; using NLog; using StackExchange.Redis; // init parte config, vedere https://blog.hildenco.com/2020/05/configuration-in-net-core-console.html var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); var builder = new ConfigurationBuilder() .AddJsonFile($"appsettings.json", true, true) .AddJsonFile($"appsettings.{env}.json", true, true) .AddEnvironmentVariables(); var config = builder.Build(); // imposto variabili di base string lineSep = "---------------------------------------------"; string redisConf = config.GetConnectionString("Redis"); string confPath = Path.Combine(Directory.GetCurrentDirectory(), "conf"); #if false string alarmSimMode = config.GetValue("AlarmSimMode"); #endif Logger Log = LogManager.GetCurrentClassLogger(); Random rand = new Random(); List? statusList = new List(); List? modeList = new List(); // fix numero minimo dei thread pool x evitare collasso chiamate redis ThreadPool.SetMinThreads(10, 10); Dictionary LogSimulator = new Dictionary(); Dictionary LastSend = new Dictionary(); DateTime lastLog = DateTime.Now.AddMinutes(-1); bool verboseLog = false; bool logWriting = false; logInfo(lineSep, true, true); logInfo($"Starting Machine ADAPTER", true, true); logInfo($"Redis server param: {redisConf.Substring(0, 20)}...", false, true); logInfo(lineSep, true, true); logInfo("", true, true); logInfo("Running - press CTRL-C to stop SIM", false, true); logInfo("", false, true); // Setup REDIS ConnectionMultiplexer.SetFeatureFlag("preventthreadtheft", true); ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(redisConf); ISubscriber sub = redis.GetSubscriber(); IDatabase? redisDb = redis.GetDatabase(); // salvo configurazioni in redis setupConf(); // avvio il vero e proprio programma di comunicaizone OPC-UA var currIob = new IobOpcUa(confPath, config); #if false var currSimGen = new Simulator(confPath, modeList.Count, statusList.Count); // preparo la lista dei contatori invio... LogSimulator.Add(Constants.ACT_LOG_M_QUEUE, 0); LogSimulator.Add(Constants.ALARM_M_QUEUE, 0); LogSimulator.Add(Constants.EVENT_LOG_M_QUEUE, 0); LogSimulator.Add(Constants.PARAMS_M_QUEUE, 0); LogSimulator.Add(Constants.PROD_M_QUEUE, 0); LogSimulator.Add(Constants.MACH_STATS_M_QUEUE, 0); LogSimulator.Add(Constants.MAINT_STATS_M_QUEUE, 0); LogSimulator.Add(Constants.TOOLS_M_QUEUE, 0); // avvio tutti i thread... Thread threadStatus = new Thread(simStatus); Thread threadAlarms = new Thread(simAlarms); Thread threadParams = new Thread(simParameters); Thread threadProd = new Thread(simProd); Thread threadMachStat = new Thread(simMachStat); Thread threadMaint = new Thread(simMaint); Thread threadTools = new Thread(simTools); Thread threadEvHistory = new Thread(simEvents); Thread threadActLog = new Thread(simActivityLog); threadStatus.Start(); threadAlarms.Start(); threadParams.Start(); threadProd.Start(); threadMachStat.Start(); threadMaint.Start(); threadTools.Start(); threadEvHistory.Start(); threadActLog.Start(); #endif // Ciclo infinito x attesa chiusura con CTRL-C do { // se non fosse connesso... riprovo la connessione... if (!currIob.connectionOk) { currIob.tryConnect(); } // attesa... Thread.Sleep(100); } while (true); /// /// verifica esistenza file oppure lo crea... /// void checkFilePresent(string filePath) { // verific presenza file log... if (!File.Exists(filePath)) { File.WriteAllText(filePath, $"{filePath} created!"); } } /// /// Setup e salvataggio redis delle conf (es modi/stati) /// void setupConf() { #if false // leggo e salvo conf stati string fullPath = Path.Combine(confPath, "StatusList.json"); if (File.Exists(fullPath)) { var rawData = File.ReadAllText(fullPath); if (!string.IsNullOrEmpty(rawData)) { List? statusList = JsonConvert.DeserializeObject>(rawData); // salvo in redis! redisDb.StringSetAsync(Constants.STATUS_CONF_KEY, JsonConvert.SerializeObject(statusList)); } } // leggo e salvo conf modi fullPath = Path.Combine(confPath, "ModeList.json"); if (File.Exists(fullPath)) { var rawData = File.ReadAllText(fullPath); if (!string.IsNullOrEmpty(rawData)) { var localObj = JsonConvert.DeserializeObject>(rawData); // salvo in redis! redisDb.StringSetAsync(Constants.MODE_CONF_KEY, JsonConvert.SerializeObject(localObj)); } } // leggo e salvo conf allarmi fullPath = Path.Combine(confPath, "AlarmList.json"); if (File.Exists(fullPath)) { var rawData = File.ReadAllText(fullPath); if (!string.IsNullOrEmpty(rawData)) { var localObj = JsonConvert.DeserializeObject>(rawData); if (localObj != null) { // sistemo allarmi foreach (var item in localObj) { item.setupData(); // loggo logInfo($"Decodifica aree alarmMap: {item.description} | {item.memAddr} x {item.size} byte | {item.messages.Count} messaggi allarme", true, true); } } // salvo in redis! redisDb.StringSetAsync(Constants.ALARMS_CONF_KEY, JsonConvert.SerializeObject(localObj)); } } // leggo e salvo conf parametri fullPath = Path.Combine(confPath, "ParamList.json"); if (File.Exists(fullPath)) { var rawData = File.ReadAllText(fullPath); if (!string.IsNullOrEmpty(rawData)) { var localObj = JsonConvert.DeserializeObject>(rawData); // salvo in redis! redisDb.StringSetAsync(Constants.PARAMS_CONF_KEY, JsonConvert.SerializeObject(localObj)); } } #endif ConfigManager configManager = new ConfigManager(redisConf, confPath); _ = configManager.getAlarmsConf(); _ = configManager.getMachineModeConf(); _ = configManager.getMachineStatusConf(); _ = configManager.getParamsConf(); } /// /// Effettua log INFO su file e se richiesto su console /// void logInfo(string msg, bool log2file = true, bool log2console = false) { if (log2console) { Console.WriteLine(msg); } if (log2file) { Log.Info(msg); } } /// /// Effettua log ERROR su file e se richiesto su console /// void logError(string msg, bool log2file = true, bool log2console = false) { if (log2console) { Console.WriteLine(msg); } if (log2file) { Log.Error(msg); } } void saveAndSendMessage(string memKey, string value, string notifyChannel, string message) { // effettuo la scrittura nell'area di memoria indicata SE passato intervallo minimo bool doSend = true; if (LastSend.ContainsKey(memKey)) { if (DateTime.Now.Subtract(LastSend[memKey]).TotalSeconds < 60) { doSend = false; } } else { LastSend.Add(memKey, DateTime.Now); } if (doSend) { redisDb.StringSetAsync(memKey, value); LastSend[memKey] = DateTime.Now; logInfo($"Redis Cache Key: {memKey}"); } //redisDb.SetAdd(memKey, value); // invio notifica tramite il canale richiesto sub.Publish(notifyChannel, message); if (verboseLog) { logInfo($"[{notifyChannel}] key: {memKey} | val: {value} | message: {message}"); } else { try { if (!logWriting) { if (LogSimulator.ContainsKey(notifyChannel)) { LogSimulator[notifyChannel]++; } else { LogSimulator.Add(notifyChannel, 1); } logWriting = true; // vedo se loggare... DateTime adesso = DateTime.Now; if (adesso.Subtract(lastLog).TotalSeconds > 15) { lastLog = adesso; logInfo(lineSep); // lavoro su copia... var LogSimulatorCopy = new Dictionary(LogSimulator); foreach (var item in LogSimulatorCopy) { logInfo($"Redis mQueue {item.Key,-20}{item.Value,12}"); } logInfo(lineSep); } logWriting = false; } } catch (Exception ex) { logError($"ERROR{Environment.NewLine}{ex}"); } } }