using Microsoft.Extensions.Configuration; using MP.MONO.Core; using MP.MONO.Core.CONF; using MP.MONO.Core.DTO; using MP.MONO.Data; using MP.MONO.Data.Controllers; using MP.MONO.Data.DbModels; using MP.MONO.DECODER; using Newtonsoft.Json; using NLog; using NLua; using StackExchange.Redis; using System.Diagnostics; using System.Reflection; using static MP.MONO.Core.Enums; // See https://aka.ms/new-console-template for more information // 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(); // init LOG Logger Log = LogManager.GetCurrentClassLogger(); // initi va di base string lineSep = "---------------------------------------------"; Console.WriteLine(lineSep); Console.WriteLine("Start DECODER with LUA integration!"); Console.WriteLine($"vers.{Assembly.GetExecutingAssembly().GetName().Version}"); Console.WriteLine(lineSep); Console.WriteLine(""); Console.WriteLine("Running - press CTRL-C to stop"); Console.WriteLine(lineSep); Console.WriteLine(""); // init DB // init info x DB string dbServerAddr = config["DbConfig:Server"]; string nKey = config["DbConfig:nKey"]; string sKey = config["DbConfig:sKey"]; DbConfig.InitDb(dbServerAddr, nKey, sKey); // inizializzo il DB e creo (se necessario) l'utente DbConfig.CheckUser(nKey, sKey); // verifico se serve applicazione migrazioni DbConfig.ExecMigrationMain(); //DbConfig.ExecMigrationIdentity(); // altri parametri per check vari string connStringDB = DbConfig.CONNECTION_STRING; MpDbController dbController = null!; if (string.IsNullOrEmpty(connStringDB)) { Log.Error("ConnString empty!"); } else { dbController = new MpDbController(); Log.Info("DbController OK"); Console.WriteLine("DbController OK"); } // init info di base int MachineId = 1; int.TryParse(config.GetValue("MachineId"), out MachineId); // init oggetti REDIS + messagePipe... ConnectionMultiplexer.SetFeatureFlag("preventthreadtheft", true); ConnectionMultiplexer redisConn = ConnectionMultiplexer.Connect(config.GetConnectionString("Redis")); IDatabase? redisDb = redisConn.GetDatabase(); // preparo oggetti da configurare AlarmReportingMode alarmMode = AlarmReportingMode.ND; List? alarmsBankBitConf = new List(); List? alarmsRListConf = new List(); List? activeAlarmList = new List(); List? alarmsRecorded = new List(); List? machineModeConf = new List(); List? machineStatusConf = new List(); MachineDTO machinePlate = new MachineDTO(); // gestione configurazioni da redis setupConf(); /* -------------------------------- * Setup Gestione ALLARMI * --------------------------------*/ // init classe gestione allarmi con LUA AlarmsManager alarmsMan = new AlarmsManager(); // inizializzo gestione messagePipe da Redis x allarmi MessagePipe alarmsSendPipe = new MessagePipe(redisConn, Constants.ALARM_M_QUEUE); MessagePipe alarmsRecvPipe = new MessagePipe(redisConn, Constants.ALARM_RAW_QUEUE); // datetime dell'inizio esecuzione task... x usare un semaforo di veto doppia esecuzione entro 30 sec DateTime lastExecAlarms = DateTime.Now.AddHours(-1); // registro gestione eventi alarmsRecvPipe.EA_NewMessage += AlarmsValPipe_EA_NewMessage; /* -------------------------------- * Setup Gestione Parametri * --------------------------------*/ ParamsManager paramMan = new ParamsManager(); // inizializzo gestione messagePipe da Redis x allarmi MessagePipe paramsSendPipe = new MessagePipe(redisConn, Constants.PARAMS_M_QUEUE); MessagePipe paramsRecvPipe = new MessagePipe(redisConn, Constants.PARAMS_RAW_QUEUE); // datetime dell'inizio esecuzione task... x usare un semaforo di veto doppia esecuzione entro 30 sec DateTime lastExecParams = DateTime.Now.AddHours(-1); // registro gestione eventi paramsRecvPipe.EA_NewMessage += ParamsValPipe_EA_NewMessage; /* -------------------------------- * Setup Gestione MachineStatus * --------------------------------*/ MPStatusManager mpStatusMan = new MPStatusManager(); // inizializzo gestione messagePipe da Redis x allarmi MessagePipe mpStatusSendPipe = new MessagePipe(redisConn, Constants.STATUS_M_QUEUE); MessagePipe mpStatusRecvPipe = new MessagePipe(redisConn, Constants.STATUS_RAW_QUEUE); // datetime dell'inizio esecuzione task... x usare un semaforo di veto doppia esecuzione entro 30 sec DateTime lastExecStatus = DateTime.Now.AddHours(-1); // registro gestione eventi mpStatusRecvPipe.EA_NewMessage += MpStatusRecvPipe_EA_NewMessage; /* -------------------------------- * Setup Gestione Counters * --------------------------------*/ CounterManager counterMan = new CounterManager(); // inizializzo gestione messagePipe da Redis x allarmi MessagePipe counterSendPipe = new MessagePipe(redisConn, Constants.COUNT_M_QUEUE); MessagePipe counterRecvPipe = new MessagePipe(redisConn, Constants.COUNT_RAW_QUEUE); // datetime dell'inizio esecuzione task... x usare un semaforo di veto doppia esecuzione entro 30 sec DateTime lastExecCounters = DateTime.Now.AddHours(-1); // registro gestione eventi counterRecvPipe.EA_NewMessage += CounterRecvPipe_EA_NewMessage; /* -------------------------------- * Funzioni / metodi accessori * --------------------------------*/ /// /// Recupero da redis le conf (es modi,stati,allarmi) /// void setupConf() { // recupero valori e deserializzo string rawData = ""; // machine status (display) conf rawData = redisDb.StringGet(Constants.MACHINE_CONF_PLATE); if (!string.IsNullOrEmpty(rawData)) { var currData = JsonConvert.DeserializeObject(rawData); machinePlate = currData != null ? currData : new MachineDTO(); } // machine status (display) conf rawData = redisDb.StringGet(Constants.DISPLSTATUS_CONF_KEY); if (!string.IsNullOrEmpty(rawData)) { machineStatusConf = JsonConvert.DeserializeObject>(rawData); } // machine mode rawData = redisDb.StringGet(Constants.MODE_CONF_KEY); if (!string.IsNullOrEmpty(rawData)) { machineModeConf = JsonConvert.DeserializeObject>(rawData); } // allarmi.. rawData = redisDb.StringGet(Constants.ALARMS_MODE_KEY); alarmMode = JsonConvert.DeserializeObject(rawData); // elenco allarmi già registrati (cache DB) rawData = redisDb.StringGet(Constants.ALARMS_SETT_RLIST_KEY); if (!string.IsNullOrEmpty(rawData)) { alarmsRecorded = JsonConvert.DeserializeObject>(rawData); } // ora la conf specifica rawData = redisDb.StringGet(Constants.ALARMS_CONF_KEY); if (!string.IsNullOrEmpty(rawData)) { if (alarmMode == AlarmReportingMode.RawList) { alarmsRListConf = JsonConvert.DeserializeObject>(rawData); } else { alarmsBankBitConf = JsonConvert.DeserializeObject>(rawData); } } } /// /// Gestione evento ricezione messaggi allarmi secondo tipologia attiva... /// void AlarmsValPipe_EA_NewMessage(object? sender, EventArgs e) { PubSubEventArgs currArgs = (PubSubEventArgs)e; // conversione on-the-fly List --> allarmi if (!string.IsNullOrEmpty(currArgs.newMessage)) { DateTime adesso = DateTime.Now; if (alarmMode == AlarmReportingMode.RawList) { try { // decodifico allarmi ricevuti var alarmList = JsonConvert.DeserializeObject>(currArgs.newMessage); List alarmRecList = new List(); List alarmLogList = new List(); List? mutedAlarmsList = new List(); string rawData = redisDb.StringGet(Constants.ALARMS_SETT_RLIST_KEY); if (!string.IsNullOrEmpty(rawData)) { mutedAlarmsList = JsonConvert.DeserializeObject>(rawData); } if (mutedAlarmsList == null) { mutedAlarmsList = dbController.AlarmListGetAll(); } // rileggo da REDIS la conf attuale allarmi già attivi da area ALARM_CURR_KEY List? lastAlarms = new List(); rawData = redisDb.StringGet(Constants.ALARM_CURR_KEY); // se trovati uso questi... if (!string.IsNullOrEmpty(rawData)) { lastAlarms = JsonConvert.DeserializeObject>(rawData); } // altrimenti parto da vuoto else { lastAlarms = new List(); } // ciclo x ogni allarme ricevuto x vedere se sia INIZIATO if (alarmList != null) { // ciclo x ogni allarme attivo x verificare se si sia CHIUSO foreach (var newAlarm in alarmList) { // in primis... se è muted --> lo escludo... bool isMuted = false; var maybeMuted = mutedAlarmsList.Where(x => x.FullValue == newAlarm).FirstOrDefault(); if (maybeMuted != null) { isMuted = maybeMuted.Muted; } // cerco l'allarme nell'elenco degli allarmi dal DB var foundAlarm = alarmsRecorded .Where(x => x.FullValue == newAlarm) .FirstOrDefault(); // se non lo trovo --> chiamo procedura che verifica su DB, eventualmente inserisce, restituisce nuovo elenco if (foundAlarm == null) { // salvo sul DB foundAlarm = dbController.AlarmListInsert(newAlarm); // ora rileggo ed aggiorno redis e cerco di nuovo record... alarmsRecorded = dbController.AlarmListGetAll(); redisDb.StringSet(Constants.ALARMS_SETT_RLIST_KEY, JsonConvert.SerializeObject(alarmsRecorded)); } if (!isMuted && lastAlarms != null) { if (!lastAlarms.Contains(newAlarm)) { if (foundAlarm != null) { // segno come iniziato... AlarmRecModel newAlarmRec = new AlarmRecModel() { MachineId = MachineId, DtStart = adesso, DtEnd = adesso, AlarmId = foundAlarm.AlarmId }; alarmRecList.Add(newAlarmRec); } } } else { // rimuovo da alarmList alarmList.Remove(newAlarm); } } } // processo elenco allarmi già presenti if (lastAlarms != null) { // ciclo x ogni allarme attivo x verificare se si sia CHIUSO foreach (var oldData in lastAlarms) { bool trovato = false; if (alarmList != null) { trovato = alarmList.Contains(oldData); } if (!trovato) { // se non lo trovo var foundAlarm = alarmsRecorded .Where(x => x.FullValue == oldData) .FirstOrDefault(); if (foundAlarm != null) { // salvo sul DB _ = dbController.AlarmRecCloseActive(foundAlarm.AlarmId); } } } } // verifico se devo scrivere if (lastAlarms != null && alarmList != null) { bool wALog = lastAlarms.Count != alarmList.Count; if (!wALog) { // verifico se ci siano differenze... wALog = alarmList.Union(lastAlarms).Count() != lastAlarms.Count; } // salvo se c'è stata variazione... if (wALog) { // preparo dati x alarmLog AlarmLogModel newAlarmLog = new AlarmLogModel() { MachineId = MachineId, DtRif = adesso, MemAddress = "ND", Index = 0, Status = (uint)alarmList.Count, ValDecoded = alarmList.Count > 0 ? string.Join(", ", alarmList) : "All OK" }; alarmLogList.Add(newAlarmLog); } } // serializzo l'elenco allarmi... string serAlarms = "[]"; serAlarms = JsonConvert.SerializeObject(alarmList); // invio sulla message pipeline corretta TUTTI gli allarmi serializzati alarmsSendPipe.saveAndSendMessage(Constants.ALARM_CURR_KEY, serAlarms); // salvo sul DB _ = dbController.AlarmRecInsertMany(alarmRecList).Result; _ = dbController.AlarmLogInsertMany(alarmLogList).Result; // salvo nuovo elenco allarmi lastAlarms = alarmList; } catch { } } else { try { // verifico codici allarmi ricevuti var alarmList = JsonConvert.DeserializeObject>(currArgs.newMessage); if (alarmList != null) { // variabili accessorie List activeAlarmList = new List(); List alarmLogList = new List(); // rileggo da REDIS la conf attuale allarmi da area ALARMS_SETT_KEY, altrimenti uso quella letta inizialmente da conf base List? currAlarmsBankConf = new List(); string rawData = redisDb.StringGet(Constants.ALARMS_SETT_BBIT_KEY); // se trovati uso questi... if (!string.IsNullOrEmpty(rawData)) { currAlarmsBankConf = JsonConvert.DeserializeObject>(rawData); } // altrimenti uso quelli di default inizialmente letti else { currAlarmsBankConf = alarmsBankBitConf; } if (currAlarmsBankConf != null) { // ciclo x ogni bank di allarmi configurato foreach (var alarmData in currAlarmsBankConf) { // recupero valore allarme var bankVal = alarmList.FirstOrDefault(x => x.Key == alarmData.memAddr); uint actVal = !string.IsNullOrEmpty(bankVal.Key) ? bankVal.Value : 0; // valutare se dividere in 2 da 16... FIXME TODO var bankAlarmList = AlarmsManager.processData(alarmData.memAddr, alarmData.messages, alarmData.silenceMask[0], alarmData.disableMask[0], alarmData.alarmsState[0], ref actVal); // salvo nuovo valore in oggetto... if (alarmData.alarmsState[0] != actVal) { alarmData.alarmsState[0] = actVal; // aggiungo... activeAlarmList.AddRange(bankAlarmList); AlarmLogModel newAlarmLog = new AlarmLogModel() { MachineId = MachineId, DtRif = adesso, MemAddress = bankVal.Key, Index = alarmData != null ? alarmData.index : 0, Status = alarmData != null ? (uint)bankVal.Value & alarmData.silenceMask[0] & alarmData.disableMask[0] : (uint)bankVal.Value, ValDecoded = bankAlarmList.Count > 0 ? string.Join(", ", bankAlarmList) : "All OK" }; alarmLogList.Add(newAlarmLog); } } } // salvo status aggiornato allarme... redisDb.StringSet(Constants.ALARMS_SETT_BBIT_KEY, JsonConvert.SerializeObject(currAlarmsBankConf)); // serializzo l'elenco allarmi... string serAlarms = "[]"; serAlarms = JsonConvert.SerializeObject(activeAlarmList); // invio sulla message pipeline corretta TUTTI gli allarmi serializzati alarmsSendPipe.saveAndSendMessage(Constants.ALARM_CURR_KEY, serAlarms); // verifico NON ci sia veto di scrittura... 60 sec... if (adesso.Subtract(lastExecAlarms).TotalSeconds > 60) { lastExecAlarms = adesso; if (dbController != null) { // salvo sul DB _ = dbController.AlarmLogInsertMany(alarmLogList).Result; } } } } catch (Exception exc) { Log.Error($"Eccezione in AlarmsValPipe_EA_NewMessage, mode {alarmMode}:{Environment.NewLine}{exc}"); } } } } /// /// Gestione evento ricezione messaggi parametri /// void ParamsValPipe_EA_NewMessage(object? sender, EventArgs e) { PubSubEventArgs currArgs = (PubSubEventArgs)e; // conversione on-the-fly Dictionary --> parametri valorizzati (tra quelli configurati) if (!string.IsNullOrEmpty(currArgs.newMessage)) { DateTime adesso = DateTime.Now; try { // verifico codici ricevuti var paramsList = JsonConvert.DeserializeObject>(currArgs.newMessage); if (paramsList != null) { // recupero elenco parametri salvati in redis... List? redisParamConf = new List(); List updatedParams = new List(); string rawData = redisDb.StringGet(Constants.PARAMS_CONF_KEY); if (!string.IsNullOrEmpty(rawData)) { redisParamConf = JsonConvert.DeserializeObject>(rawData); // se ho i parametri... if (redisParamConf != null) { // ciclo x ogni valore ricevuto da message service... foreach (var item in paramsList) { // cerco il parametro corrispondente... var currParam = redisParamConf.FirstOrDefault(x => x.Title == item.Key); if (currParam != null) { currParam.ValueNum = item.Value; currParam.Value = $"{item.Value.ToString(currParam.DisplFormat)}"; // aggiungo a lista... updatedParams.Add(currParam); } } // verifico NON ci sia veto di scrittura... 60 sec... if (adesso.Subtract(lastExecParams).TotalSeconds > 60) { lastExecParams = adesso; if (dbController != null) { // verifico x ogni parametro se sia completato il periodo di campionamento... var params2save = paramMan.processData(paramsList, redisParamConf); if (params2save.Count > 0) { // salvo sul DB _ = dbController.DataLogInsertMany(params2save).Result; } } } } } // invio sulla message pipeline corretta TUTTI i parametri aggiornati serializzati string updRawVal = JsonConvert.SerializeObject(redisParamConf); paramsSendPipe.saveAndSendMessage(Constants.PARAMS_CURR_KEY, updRawVal); } } catch (Exception exc) { Log.Error($"Eccezione in ParamsValPipe_EA_NewMessage:{Environment.NewLine}{exc}"); } } } /// /// Gestione evento ricezione messaggi status /// void MpStatusRecvPipe_EA_NewMessage(object? sender, EventArgs e) { PubSubEventArgs currArgs = (PubSubEventArgs)e; // conversione on-the-fly Dictionary --> parametri valorizzati (tra quelli configurati) if (!string.IsNullOrEmpty(currArgs.newMessage)) { DateTime adesso = DateTime.Now; try { // verifico codici ricevuti var dataList = JsonConvert.DeserializeObject>(currArgs.newMessage); if (dataList != null) { /************************************************************************ * * * * * * * * * * * *************************************************************************/ // recupero elenco parametri salvati in redis... MachineDTO? machinePlate = new MachineDTO(); Dictionary parList = new Dictionary(); string rawData = redisDb.StringGet(Constants.MACHINE_CONF_PLATE); if (!string.IsNullOrEmpty(rawData)) { machinePlate = JsonConvert.DeserializeObject(rawData); } int machStatus = 0; int procStatus = 0; int procMode = 0; string currKey = ""; // ora processo il messaggio ricevuto x stati e modi vari currKey = "MACHINE STATUS"; if (dataList.ContainsKey(currKey)) { int.TryParse(dataList[currKey], out machStatus); parList.Add(currKey, machStatus); } currKey = "PROCESS STATUS"; if (dataList.ContainsKey(currKey)) { int.TryParse(dataList[currKey], out procStatus); parList.Add(currKey, procStatus); } currKey = "PROCESS MODE"; if (dataList.ContainsKey(currKey)) { int.TryParse(dataList[currKey], out procMode); parList.Add(currKey, procMode); } // fix me todo inserire gestione program name!!! Stopwatch sw = new Stopwatch(); sw.Start(); // calcolo status if (machStatus == 0) { machinePlate.StatusId = 7; } else { switch (procMode) { case 1: machinePlate.StatusId = 8; break; case 2: machinePlate.StatusId = 2; break; case 3: machinePlate.StatusId = 9; break; case 4: machinePlate.StatusId = 5; break; case 5: machinePlate.StatusId = 10; break; case 6: machinePlate.StatusId = 11; break; case 7: machinePlate.StatusId = 12; break; case 8: machinePlate.StatusId = 13; break; default: break; } } // eseguo cablata la gestione x ora... rispetto a MachineStatus + MachineMode invierò un machineDTO switch (procStatus) { case 1: machinePlate.ModeId = 2; break; case 2: machinePlate.ModeId = 1; break; case 3: machinePlate.ModeId = 3; break; case 6: machinePlate.ModeId = 8; break; case 8: machinePlate.ModeId = 9; break; case 11: machinePlate.ModeId = 10; break; default: break; } sw.Stop(); Log.Info($"Calcolo C# | {sw.ElapsedTicks} ticks | {JsonConvert.SerializeObject(machinePlate)}"); Log.Info("-------------"); // calcolo con LUA e vedo se cambia... sw.Restart(); bool done = mpStatusMan.processData(parList, ref machinePlate); sw.Stop(); Log.Info($"Calcolo LUA | {sw.ElapsedTicks} ticks | {JsonConvert.SerializeObject(machinePlate)}"); Log.Info("-------------"); // invio sulla message pipeline corretta TUTTI i parametri aggiornati serializzati string updRawVal = JsonConvert.SerializeObject(machinePlate); paramsSendPipe.saveAndSendMessage(Constants.STATUS_CURR_KEY, updRawVal); } } catch (Exception exc) { Log.Error($"Eccezione in MpStatusRecvPipe_EA_NewMessage:{Environment.NewLine}{exc}"); } } }; /// /// Gestione evento ricezione messaggi status /// void CounterRecvPipe_EA_NewMessage(object? sender, EventArgs e) { PubSubEventArgs currArgs = (PubSubEventArgs)e; // conversione on-the-fly Dictionary --> parametri valorizzati (tra quelli configurati) if (!string.IsNullOrEmpty(currArgs.newMessage)) { #if false DateTime adesso = DateTime.Now; try { // verifico codici ricevuti var dataList = JsonConvert.DeserializeObject>(currArgs.newMessage); if (dataList != null) { /************************************************************************ * * * * * * * * * * * *************************************************************************/ // recupero elenco parametri salvati in redis... MachineDTO? machinePlate = new MachineDTO(); string rawData = redisDb.StringGet(Constants.MACHINE_CONF_PLATE); if (!string.IsNullOrEmpty(rawData)) { machinePlate = JsonConvert.DeserializeObject(rawData); } int machStatus = 0; int procStatus = 0; int procMode = 0; string currKey = ""; // ora processo il messaggio ricevuto x stati e modi vari currKey = "MACHINE STATUS"; if (dataList.ContainsKey(currKey)) { int.TryParse(dataList[currKey], out machStatus); } currKey = "PROCESS STATUS"; if (dataList.ContainsKey(currKey)) { int.TryParse(dataList[currKey], out procStatus); } currKey = "PROCESS MODE"; if (dataList.ContainsKey(currKey)) { int.TryParse(dataList[currKey], out procMode); } // fix me todo inserire gestione program name!!! if (machStatus == 0) { machinePlate.StatusId = 7; } else { switch (procMode) { case 1: machinePlate.StatusId = 8; break; case 2: machinePlate.StatusId = 2; break; case 3: machinePlate.StatusId = 9; break; case 4: machinePlate.StatusId = 5; break; case 5: machinePlate.StatusId = 10; break; case 6: machinePlate.StatusId = 11; break; case 7: machinePlate.StatusId = 12; break; case 8: machinePlate.StatusId = 13; break; default: break; } } // eseguo cablata la gestione x ora... rispetto a MachineStatus + MachineMode invierò un machineDTO switch (procStatus) { case 1: machinePlate.ModeId = 2; break; case 2: machinePlate.ModeId = 1; break; case 3: machinePlate.ModeId = 3; break; case 6: machinePlate.ModeId = 8; break; case 8: machinePlate.ModeId = 9; break; case 11: machinePlate.ModeId = 10; break; default: break; } // invio sulla message pipeline corretta TUTTI i parametri aggiornati serializzati string updRawVal = JsonConvert.SerializeObject(machinePlate); paramsSendPipe.saveAndSendMessage(Constants.STATUS_CURR_KEY, updRawVal); } } catch (Exception exc) { Log.Error($"Eccezione in MpStatusRecvPipe_EA_NewMessage:{Environment.NewLine}{exc}"); } #endif } } /* -------------------------------- * MAIN * --------------------------------*/ // Ciclo infinito x attesa chiusura con CTRL-C do { Thread.Sleep(100); } while (true);