// 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 MP.MONO.Core.DTO; using Newtonsoft.Json; using NLog; using StackExchange.Redis; using System.Reflection; using static MP.MONO.Core.Enums; // 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(); IConfigurationRoot? config = builder.Build(); // imposto variabili di base string lineSep = "---------------------------------------------"; string redisConf = config.GetConnectionString("Redis"); string confPath = Path.Combine(Directory.GetCurrentDirectory(), "conf"); 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($"vers.{Assembly.GetExecutingAssembly().GetName().Version}", true, true); logInfo("", 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(); // preparo oggetti da configurare AlarmReportingMode alarmMode = AlarmReportingMode.ND; List? alarmsBankBitConf = new List(); List? alarmsRawConf = new List(); List? machineModeConf = new List(); List? machineStatusConf = new List(); // 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() { alarmMode = config.GetValue("SimPar:AlarmMode"); redisDb.StringSetAsync(Constants.ALARMS_MODE_KEY, JsonConvert.SerializeObject(alarmMode)); ConfigManager configManager = new ConfigManager(redisConf, confPath); if (alarmMode == AlarmReportingMode.RawList) { alarmsRawConf = configManager.getAlarmsRawConf(); } else { alarmsBankBitConf = configManager.getAlarmsBankConf(); } machineModeConf = configManager.getMachineModeConf(); machineStatusConf = 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 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, message); LastSend[memKey] = DateTime.Now; logInfo($"Redis Cache Key: {memKey}"); } // invio notifica tramite il canale richiesto sub.Publish(notifyChannel, message); if (verboseLog) { logInfo($"[{notifyChannel}] key: {memKey} | val/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 = 2000; int maxPeriod = 8000; int percAllarmi = config.GetValue("SimPar:PercAllarmi"); int MaxAddAllarmi = config.GetValue("SimPar:MaxAddAllarmi"); double MaxDurationAllarmi = config.GetValue("SimPar:MaxDurationAllarmi"); // in base alla modalità configurata effettuo la simulazione... if (alarmMode == AlarmReportingMode.RawList) { // controllo se devo fare (se ho valori da simulare...) if (alarmsRawConf.Count > 0 && alarmsRawConf[0].messages.Count > 0) { DateTime lastOk = DateTime.Now; // Dict allarmi attivi (e da quando) Dictionary CurrActiveAlarm = new Dictionary(); do { bool newAlarm = false; DateTime adesso = DateTime.Now; Dictionary NewActiveAlarm = new Dictionary(); // se fosse oltre MaxDurationAllarmi * 3 sec senza all ok --> riporta ad all Ok if (lastOk.Subtract(adesso).TotalSeconds > MaxDurationAllarmi * 3 && CurrActiveAlarm.Count > 0) { lastOk = adesso; } else { // verifico situazione allarmi già presenti foreach (var singleAlarm in CurrActiveAlarm) { // 1: seleziono allarmi se ce ne fossero e la loro durata è <= limite * rand (50-150) x rimetterli in elenco if (adesso.Subtract(singleAlarm.Value).TotalSeconds < MaxDurationAllarmi * rand.Next(50, 150) / 100) { NewActiveAlarm.Add(singleAlarm.Key, singleAlarm.Value); } } // ciclo x numero banchi da config... foreach (var alarmGroup in alarmsRawConf) { // simulo e limito insorgenza NUOVI allarmi al percAllarmi/100 dei casi newAlarm = rand.Next(0, 100) <= percAllarmi; if (newAlarm) { int numNew = rand.Next(1, MaxAddAllarmi); // ciclo x aggiungere il numero di allarmi indicato for (int i = 0; i < numNew; i++) { // seleziono uno degli allarmi del banco int alarmIndex = rand.Next(0, alarmGroup.messages.Count); string rndNewAlarm = alarmGroup.messages[alarmIndex]; if (!NewActiveAlarm.ContainsKey(rndNewAlarm)) { NewActiveAlarm.Add(rndNewAlarm, DateTime.Now); } } } } } // copio i nuovi allarmi nella memoria principale... CurrActiveAlarm = NewActiveAlarm; // serializzo ed invio l'elenco dei soli allarmi attivi List actAlarmList = CurrActiveAlarm.Select(x => x.Key).OrderBy(x => x).ToList(); string rawDataVal = JsonConvert.SerializeObject(actAlarmList); saveAndSendMessage(Constants.ALARM_ACT_KEY, Constants.ALARM_RAW_QUEUE, rawDataVal); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } } else if (alarmMode == AlarmReportingMode.BankBit) { // controllo se devo fare (se ho valori da simulare...) if (alarmsBankBitConf.Count > 0) { do { Dictionary currAlarmsVal = new Dictionary(); uint alarmCode = 0; bool hasAlarm; // ciclo x numero banchi da config... foreach (var item in alarmsBankBitConf) { // limito allarmi al percAllarmi/100 dei casi hasAlarm = rand.Next(0, 100) <= percAllarmi; // allarme libero SE in condizione allarme alarmCode = hasAlarm ? (uint)rand.Next(0, 65535) : 0; currAlarmsVal.Add(item.memAddr, alarmCode); } string rawDataVal = JsonConvert.SerializeObject(currAlarmsVal); saveAndSendMessage(Constants.ALARM_ACT_KEY, Constants.ALARM_RAW_QUEUE, rawDataVal); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } } } void simParameters() { int minPeriod = 150; int maxPeriod = 500; // controllo se devo fare (se ho valori da simulare...) if (currSimGen.currSimPar.Count > 0) { do { // gestione SIM dictionary var paramDict = currSimGen.getParamsVal(); string rawData = JsonConvert.SerializeObject(paramDict); saveAndSendMessage(Constants.PARAMS_ACT_KEY, Constants.PARAMS_RAW_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } } void simStatus() { int minPeriod = 1000; int maxPeriod = 5000; // controllo se devo fare (se ho valori da simulare...) if (currSimGen.currSimStatus.Count > 0) { do { // recupero uno stato simulato var newStatus = currSimGen.getStatus(); string rawData = JsonConvert.SerializeObject(newStatus); saveAndSendMessage(Constants.STATUS_CURR_KEY, Constants.STATUS_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } } void simProd() { int minPeriod = 3000; int maxPeriod = 10000; // controllo se devo fare (se ho valori da simulare...) if (currSimGen.currSimProd.Count > 0) { do { // recupero uno stato simulato var newStatus = currSimGen.getProd(); string rawData = JsonConvert.SerializeObject(newStatus); saveAndSendMessage(Constants.PROD_CURR_KEY, Constants.PROD_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } } void simMachStat() { int minPeriod = 5000; int maxPeriod = 15000; // controllo se devo fare (se ho valori da simulare...) if (currSimGen.currSimMachStat.Count > 0) { do { // recupero uno stato simulato var newVal = currSimGen.getProdStats(); string rawData = JsonConvert.SerializeObject(newVal); saveAndSendMessage(Constants.MACH_STATS_CURR_KEY, Constants.MACH_STATS_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } } void simMaint() { int minPeriod = 10000; int maxPeriod = 20000; // controllo se devo fare (se ho valori da simulare...) if (currSimGen.currSimMaint.Count > 0) { do { // recupero uno stato simulato var newVal = currSimGen.getMaint(); string rawData = JsonConvert.SerializeObject(newVal); saveAndSendMessage(Constants.MAINT_STATS_CURR_KEY, Constants.MAINT_STATS_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } } void simTools() { int minPeriod = 3000; int maxPeriod = 10000; // controllo se devo fare (se ho valori da simulare...) if (currSimGen.currSimTools.Count > 0) { do { // recupero uno stato simulato var newVal = currSimGen.getTools(); string rawData = JsonConvert.SerializeObject(newVal); saveAndSendMessage(Constants.TOOLS_CURR_KEY, Constants.TOOLS_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } } void simEvents() { int minPeriod = 200; int maxPeriod = 800; // controllo se devo fare (se ho valori da simulare...) if (currSimGen.currSimEvHist.Count > 0) { do { // recupero uno stato simulato var newVal = currSimGen.getEvents(); string rawData = JsonConvert.SerializeObject(newVal); saveAndSendMessage(Constants.EVENT_LOG_CURR_KEY, Constants.EVENT_LOG_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } } void simActivityLog() { int minPeriod = 15000; int maxPeriod = 30000; // controllo se devo fare (se ho valori da simulare...) if (currSimGen.currSimActLog.Count > 0) { do { // recupero uno stato simulato var newVal = currSimGen.getActLog(); string rawData = JsonConvert.SerializeObject(newVal); saveAndSendMessage(Constants.ACT_LOG_CURR_KEY, Constants.ACT_LOG_M_QUEUE, rawData); // attesa random Thread.Sleep(rand.Next(minPeriod, maxPeriod)); } while (true); } }