// See https://aka.ms/new-console-template for more information using MachineSim; using Microsoft.Extensions.Configuration; using MP.MONO.Core; using MP.MONO.Core.CONF; 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"); string alarmSimMode = config.GetValue("AlarmSimMode"); 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 SIM", 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); //Create a connection ConnectionMultiplexer.SetFeatureFlag("preventthreadtheft", true); ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(redisConf); ISubscriber sub = redis.GetSubscriber(); IDatabase? redisDb = redis.GetDatabase(); // salvo configurazioni in redis setupConf(); 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(); /// /// 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() { // leggo e salvo conf stati string fullPath = Path.Combine(confPath, "ConfStatus.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, "ConfMode.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)); } } } /// /// 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}"); } } } void simAlarms() { int minPeriod = 5000; int maxPeriod = 10000; do { List currAlarmsVal = new List(); List activeAlarms = new List(); //int alarmCode = rand.Next(0, 255); int alarmCode = rand.Next(0, 160); // se >= 128 --> 0 (no alarm) alarmCode = alarmCode <= 127 ? alarmCode : 0; currAlarmsVal.Add(alarmCode); string rawDataVal = JsonConvert.SerializeObject(currAlarmsVal); saveAndSendMessage(Constants.ALARM_ACT_KEY, rawDataVal, Constants.ALARM_ACT_VAL, rawDataVal); if (alarmSimMode == "all") { for (int i = 0; i < 8; i++) { if ((alarmCode & (1 << i)) != 0) { activeAlarms.Add($"Alarm {i:000}"); } } string rawData = JsonConvert.SerializeObject(activeAlarms); // decodifico allarme e lo trasformo in List + List di allarmi attivi... saveAndSendMessage(Constants.ALARM_CURR_KEY, rawData, Constants.ALARM_M_QUEUE, rawData); } // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } void simParameters() { int minPeriod = 150; int maxPeriod = 500; do { var newParams = currSimGen.getParameters(); string rawData = JsonConvert.SerializeObject(newParams); saveAndSendMessage(Constants.PARAMS_CURR_KEY, rawData, Constants.PARAMS_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } void simStatus() { int minPeriod = 1000; int maxPeriod = 5000; do { // recupero uno stato simulato var newStatus = currSimGen.getStatus(); string rawData = JsonConvert.SerializeObject(newStatus); saveAndSendMessage(Constants.STATUS_CURR_KEY, rawData, Constants.STATUS_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } void simProd() { int minPeriod = 3000; int maxPeriod = 10000; do { // recupero uno stato simulato var newStatus = currSimGen.getProd(); string rawData = JsonConvert.SerializeObject(newStatus); saveAndSendMessage(Constants.PROD_CURR_KEY, rawData, Constants.PROD_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } void simMachStat() { int minPeriod = 5000; int maxPeriod = 15000; do { // recupero uno stato simulato var newVal = currSimGen.getProdStats(); string rawData = JsonConvert.SerializeObject(newVal); saveAndSendMessage(Constants.MACH_STATS_CURR_KEY, rawData, Constants.MACH_STATS_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } void simMaint() { int minPeriod = 10000; int maxPeriod = 20000; do { // recupero uno stato simulato var newVal = currSimGen.getMaint(); string rawData = JsonConvert.SerializeObject(newVal); saveAndSendMessage(Constants.MAINT_STATS_CURR_KEY, rawData, Constants.MAINT_STATS_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } void simTools() { int minPeriod = 3000; int maxPeriod = 10000; do { // recupero uno stato simulato var newVal = currSimGen.getTools(); string rawData = JsonConvert.SerializeObject(newVal); saveAndSendMessage(Constants.TOOLS_CURR_KEY, rawData, Constants.TOOLS_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } void simEvents() { int minPeriod = 200; int maxPeriod = 800; do { // recupero uno stato simulato var newVal = currSimGen.getEvents(); string rawData = JsonConvert.SerializeObject(newVal); saveAndSendMessage(Constants.EVENT_LOG_CURR_KEY, rawData, Constants.EVENT_LOG_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } void simActivityLog() { int minPeriod = 15000; int maxPeriod = 30000; do { // recupero uno stato simulato var newVal = currSimGen.getActLog(); string rawData = JsonConvert.SerializeObject(newVal); saveAndSendMessage(Constants.ACT_LOG_CURR_KEY, rawData, Constants.ACT_LOG_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); }