1677 lines
68 KiB
C#
1677 lines
68 KiB
C#
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
|
|
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.Data.DTO;
|
|
using MP.MONO.DECODER;
|
|
using Newtonsoft.Json;
|
|
using NLog;
|
|
using Org.BouncyCastle.Asn1.Pkcs;
|
|
using Pomelo.EntityFrameworkCore.MySql.Query.Internal;
|
|
using StackExchange.Redis;
|
|
using System.Collections.Specialized;
|
|
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("");
|
|
|
|
Log.Info(lineSep);
|
|
Log.Info("Start DECODER with LUA integration!");
|
|
Log.Info($"vers.{Assembly.GetExecutingAssembly().GetName().Version}");
|
|
Log.Info(lineSep);
|
|
|
|
// 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(config);
|
|
Log.Info("DbController OK");
|
|
Console.WriteLine("DbController OK");
|
|
}
|
|
|
|
// init info di base
|
|
int MachineId = 1;
|
|
int.TryParse(config.GetValue<string>("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<BaseAlarmBankConf>? alarmsBankBitConf = new List<BaseAlarmBankConf>();
|
|
List<BaseAlarmRawConf>? alarmsRListConf = new List<BaseAlarmRawConf>();
|
|
List<AlarmListModel>? alarmsRecorded = new List<AlarmListModel>();
|
|
List<MachineMode>? machineModeConf = new List<MachineMode>();
|
|
List<MachineStatus>? machineStatusConf = new List<MachineStatus>();
|
|
List<CounterModel>? countStatus = new List<CounterModel>();
|
|
MachineDTO machinePlate = new MachineDTO();
|
|
|
|
// gestione configurazioni da redis
|
|
int BlinkCount = 1;
|
|
bool AlarmIgnoreEmpty = true;
|
|
string AlarmCleanPre = "";
|
|
string AlarmCleanPost = "";
|
|
int DbSampleInt = 60;
|
|
int BlinkPeriodMSec = 1000;
|
|
bool AlarmLogActive = false;
|
|
int NumDay2Keep = 180;
|
|
string LogFolder = Path.Combine(Directory.GetCurrentDirectory(), config["OptPar:LogFolder"]);
|
|
int LogMaxAgeDays = config.GetValue<int>("OptPar:LogMaxAgeDays");
|
|
setupConf();
|
|
|
|
// datetime dell'inizio esecuzione task... x usare un semaforo di veto doppia esecuzione entro 30 sec
|
|
DateTime lastExecParams = DateTime.Now.AddHours(-1);
|
|
DateTime lastLogDetail = DateTime.Now.AddHours(-1);
|
|
int numSendParam = 1;
|
|
int numSendTools = 1;
|
|
int numSendMStatus = 1;
|
|
Random rand = new Random();
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Setup Thread periodici (es calcolo pareto stati)
|
|
* ----------------------------------------------------------------*/
|
|
MessagePipe mpStatusParetoSendPipe = new MessagePipe(redisConn, Constants.STATUS_PAR_QUEUE);
|
|
MessagePipe mpStatsSendPipe = new MessagePipe(redisConn, Constants.MACH_STATS_M_QUEUE);
|
|
MessagePipe mpDayDurSendPipe = new MessagePipe(redisConn, Constants.MACH_DAY_DUR_M_QUEUE);
|
|
Thread threadStatus = new Thread(calcParetoStatus);
|
|
threadStatus.Start();
|
|
Thread threadDbMaint = new Thread(dbMaintenance);
|
|
threadDbMaint.Start();
|
|
Thread threadPersistCount = new Thread(persistCount);
|
|
threadPersistCount.Start();
|
|
Thread cleanupLog = new Thread(cleanupOldLog);
|
|
cleanupLog.Start();
|
|
|
|
/* --------------------------------
|
|
* 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);
|
|
// registro gestione eventi
|
|
counterRecvPipe.EA_NewMessage += CounterRecvPipe_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 mpProdSendPipe = new MessagePipe(redisConn, Constants.PROD_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);
|
|
// ultimi parametri stato x check variazione..
|
|
Dictionary<string, int> parListLast = new Dictionary<string, int>();
|
|
// registro gestione eventi
|
|
mpStatusRecvPipe.EA_NewMessage += MpStatusRecvPipe_EA_NewMessage;
|
|
|
|
/* --------------------------------
|
|
* Setup Gestione Tools
|
|
* --------------------------------*/
|
|
ToolsManager toolsMan = new ToolsManager();
|
|
// inizializzo gestione messagePipe da Redis x allarmi
|
|
MessagePipe toolsSendPipe = new MessagePipe(redisConn, Constants.TOOLS_M_QUEUE);
|
|
MessagePipe toolsRecvPipe = new MessagePipe(redisConn, Constants.TOOLS_RAW_QUEUE);
|
|
// datetime dell'inizio esecuzione task... x usare un semaforo di veto doppia esecuzione entro 30 sec
|
|
DateTime lastExecTools = DateTime.Now.AddHours(-1);
|
|
// registro gestione eventi
|
|
toolsRecvPipe.EA_NewMessage += ToolsRecvPipe_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);
|
|
// registro gestione eventi
|
|
paramsRecvPipe.EA_NewMessage += ParamsValPipe_EA_NewMessage;
|
|
|
|
/* --------------------------------
|
|
* Setup Gestione ALLARMI
|
|
* --------------------------------*/
|
|
// init classe gestione allarmi con LUA
|
|
AlarmsManager alarmsMan = new AlarmsManager();
|
|
AlarmsBlinkManager alarmsBlinkMan = new AlarmsBlinkManager();
|
|
// 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;
|
|
|
|
/* --------------------------------
|
|
* Funzioni / metodi accessori
|
|
* --------------------------------*/
|
|
AlarmListModel? getAlarmModel(string alarmFullCode)
|
|
{
|
|
// cerco l'allarme nell'elenco degli allarmi dal DB
|
|
AlarmListModel? foundAlarm = null;
|
|
if (alarmsRecorded != null)
|
|
{
|
|
foundAlarm = alarmsRecorded
|
|
.Where(x => x.FullValue == alarmFullCode)
|
|
.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(alarmFullCode);
|
|
// ora rileggo ed aggiorno redis e cerco di nuovo record...
|
|
alarmsRecorded = dbController.AlarmListGetAll();
|
|
if (redisDb != null)
|
|
{
|
|
redisDb.StringSet(Constants.ALARMS_SETT_RLIST_KEY, JsonConvert.SerializeObject(alarmsRecorded));
|
|
}
|
|
}
|
|
|
|
return foundAlarm;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifica un allarme se sia x caso cessato dopo il periodo di pausa e quindi
|
|
/// </summary>
|
|
async Task<bool> checkCeasedAlarm(string alarmFullCode)
|
|
{
|
|
bool answ = false;
|
|
// verifico NON SIA già in elenco check...
|
|
List<string> listClose = AlarmsBlinkManager.alarmsPendingClose;
|
|
if (!listClose.Contains(alarmFullCode))
|
|
{
|
|
Log.Debug($"CCA 01 | Verifica scadenza allarmi | cod: {alarmFullCode}");
|
|
listClose.Add(alarmFullCode);
|
|
AlarmsBlinkManager.alarmsPendingClose = listClose;
|
|
|
|
// attendo periodo pausa...
|
|
await Task.Delay(BlinkPeriodMSec);
|
|
|
|
// cerco allarme copiando il set degli ultimi ricevuti
|
|
List<string> alarmListRecev = AlarmsBlinkManager.alarmsLastRec;
|
|
if (alarmListRecev != null)
|
|
{
|
|
// ...SE mancasse
|
|
if (!alarmListRecev.Contains(alarmFullCode))
|
|
{
|
|
// recupero allarme...
|
|
var foundAlarm = alarmsRecorded
|
|
.Where(x => x.FullValue == alarmFullCode)
|
|
.FirstOrDefault();
|
|
|
|
int alId = foundAlarm != null ? foundAlarm.AlarmId : 0;
|
|
Log.Debug($"CCA 02 | {alId} | Allarme ancora mancante | cod: {alarmFullCode}");
|
|
// --> chiudo
|
|
if (foundAlarm != null)
|
|
{
|
|
// salvo sul DB
|
|
_ = dbController.AlarmRecCloseActive(foundAlarm.AlarmId);
|
|
Log.Info($"Chiusura allarme | Id: {foundAlarm.AlarmId}");
|
|
}
|
|
// verifico libero canale di rimozione
|
|
int numTry = 0;
|
|
DateTime adesso = DateTime.Now;
|
|
Random rand = new Random();
|
|
while (AlarmsBlinkManager.isRemoving)
|
|
{
|
|
await Task.Delay(rand.Next(10, 30));
|
|
if (numTry % 2 == 0)
|
|
{
|
|
TimeSpan ts = DateTime.Now.Subtract(adesso);
|
|
Log.Info($"Wait for remove Alarm {alId} | total wait {ts.TotalMilliseconds:N3}ms");
|
|
}
|
|
numTry++;
|
|
}
|
|
AlarmsBlinkManager.isRemoving = true;
|
|
|
|
// --> rimuovo da elenco attivi!
|
|
var alarmListSent = new List<string>(AlarmsBlinkManager.alarmsActAck);
|
|
int numPre = alarmListSent.Count();
|
|
alarmListSent.Remove(alarmFullCode);
|
|
AlarmsBlinkManager.alarmsActAck = alarmListSent;
|
|
int numPost = alarmListSent.Count();
|
|
// invio attivi...
|
|
string serAlarms = JsonConvert.SerializeObject(alarmListSent);
|
|
// invio sulla message pipeline corretta TUTTI gli allarmi serializzati
|
|
alarmsSendPipe.saveAndSendMessage(Constants.ALARM_CURR_KEY, serAlarms);
|
|
Log.Debug($"CCA 03 | {alId} | Salvataggio Sent alarms | num: {numPre} --> {numPost}");
|
|
AlarmsBlinkManager.isRemoving = false;
|
|
}
|
|
answ = true;
|
|
}
|
|
// rimuovo da pending close
|
|
listClose = new List<string>(AlarmsBlinkManager.alarmsPendingClose);
|
|
listClose.Remove(alarmFullCode);
|
|
AlarmsBlinkManager.alarmsPendingClose = listClose;
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
#if false
|
|
/// <summary>
|
|
/// Verifica x TUTTI gli allarmi SE siano cessati topo 4 * periodo di controllo... zero ora e zero dopo...
|
|
/// </summary>
|
|
async Task<bool> checkCloseAllAlarm()
|
|
{
|
|
bool answ = false;
|
|
Log.Debug($"Verifica checkCloseAllAlarm");
|
|
// cerco allarme da valore REDIS ultimi ricevuti
|
|
List<string> alarmSent = new List<string>(AlarmsBlinkManager.alarmsActAck);
|
|
if (alarmSent != null)
|
|
{
|
|
bool isZero = alarmSent.Count() == 0;
|
|
// solo SE ora sono zero...
|
|
if (isZero)
|
|
{
|
|
// attendo periodo pausa...
|
|
await Task.Delay(BlinkPeriodMSec * 2);
|
|
// rileggo stato
|
|
alarmSent = new List<string>(AlarmsBlinkManager.alarmsActAck);
|
|
isZero = alarmSent.Count() == 0;
|
|
// ...SE fossero sempre zero
|
|
if (isZero)
|
|
{
|
|
Log.Debug($"Verifica zero allarmi confermata, chiamo chiusura");
|
|
// chiudo sul DB
|
|
_ = dbController.AlarmRecCloseStarving();
|
|
// invio resettati...
|
|
string serAlarms = JsonConvert.SerializeObject(alarmSent);
|
|
// invio sulla message pipeline corretta TUTTI gli allarmi serializzati
|
|
alarmsSendPipe.saveAndSendMessage(Constants.ALARM_CURR_KEY, serAlarms);
|
|
}
|
|
}
|
|
answ = true;
|
|
}
|
|
return answ;
|
|
}
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Recupero da redis le conf (es modi,stati,allarmi)
|
|
/// </summary>
|
|
void setupConf()
|
|
{
|
|
// imposto blinkCount da conf...
|
|
string rConf = config["OptPar:BlinkCount"];
|
|
int.TryParse(rConf, out BlinkCount);
|
|
// imposto intervallo salvataggio valori su db
|
|
rConf = config["OptPat:DbSampleInt"];
|
|
int.TryParse(rConf, out DbSampleInt);
|
|
// imposto parametri x Alarms
|
|
rConf = config["OptPar:AlarmIgnoreEmpty"];
|
|
bool.TryParse(rConf, out AlarmIgnoreEmpty);
|
|
AlarmCleanPre = config["OptPar:AlarmCleanPre"];
|
|
AlarmCleanPost = config["OptPar:AlarmCleanPost"];
|
|
// gestione blink allarmi
|
|
rConf = config["OptPar:BlinkPeriodMSec"];
|
|
int.TryParse(rConf, out BlinkPeriodMSec);
|
|
// recupero valori e deserializzo
|
|
string redisConf = config.GetConnectionString("Redis");
|
|
string confPath = Path.Combine(Directory.GetCurrentDirectory(), "conf");
|
|
ConfigManager confMan = new ConfigManager(redisConf, confPath);
|
|
// salvo conf allarmi in redis
|
|
rConf = config["OptPar:AlarmLogActive"];
|
|
bool.TryParse(rConf, out AlarmLogActive);
|
|
confMan.AlarmLogActive = AlarmLogActive;
|
|
|
|
// machine status (display) conf
|
|
var rawData = redisDb.StringGet(Constants.MACHINE_CONF_PLATE);
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
var currData = JsonConvert.DeserializeObject<MachineDTO>($"{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<List<MachineStatus>>($"{rawData}");
|
|
}
|
|
|
|
// machine mode
|
|
rawData = redisDb.StringGet(Constants.MODE_CONF_KEY);
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
machineModeConf = JsonConvert.DeserializeObject<List<MachineMode>>($"{rawData}");
|
|
}
|
|
|
|
// allarmi..
|
|
rawData = redisDb.StringGet(Constants.ALARMS_MODE_KEY);
|
|
alarmMode = JsonConvert.DeserializeObject<AlarmReportingMode>($"{rawData}");
|
|
|
|
// elenco allarmi già registrati (cache DB)
|
|
rawData = redisDb.StringGet(Constants.ALARMS_SETT_RLIST_KEY);
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
alarmsRecorded = JsonConvert.DeserializeObject<List<AlarmListModel>>($"{rawData}");
|
|
}
|
|
|
|
// ora la conf specifica
|
|
rawData = redisDb.StringGet(Constants.ALARMS_CONF_KEY);
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
if (alarmMode == AlarmReportingMode.RawList)
|
|
{
|
|
alarmsRListConf = JsonConvert.DeserializeObject<List<BaseAlarmRawConf>>($"{rawData}");
|
|
}
|
|
else if (alarmMode == AlarmReportingMode.RawListBlink)
|
|
{
|
|
alarmsRListConf = JsonConvert.DeserializeObject<List<BaseAlarmRawConf>>($"{rawData}");
|
|
}
|
|
else
|
|
{
|
|
alarmsBankBitConf = JsonConvert.DeserializeObject<List<BaseAlarmBankConf>>($"{rawData}");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Esecuzione pulizia cartella log
|
|
/// </summary>
|
|
void cleanupOldLog()
|
|
{
|
|
// esecuzione ogni ora +/- 1 min
|
|
int minPeriod = 1000 * 60 * 59;
|
|
int maxPeriod = 1000 * 60 * 61;
|
|
do
|
|
{
|
|
DateTime limit = DateTime.Now.AddDays(-LogMaxAgeDays);
|
|
// cerco file + vecchi di
|
|
var file2check = Directory
|
|
.EnumerateFiles(LogFolder, "*.log", SearchOption.TopDirectoryOnly)
|
|
.Select(file => new FileInfo(file))
|
|
.Where(fInfo => fInfo.LastWriteTime < limit && fInfo.Name.Length > 10)
|
|
.ToList();
|
|
// cerco e se NON si tratta dei 3 file standard --> elimino!
|
|
foreach (var item in file2check)
|
|
{
|
|
Log.Info($"Deleting old logfile: {item.Name}");
|
|
item.Delete();
|
|
}
|
|
// attesa random di circa 1 minuti x calcolare...
|
|
Thread.Sleep(rand.Next(minPeriod, maxPeriod));
|
|
} while (true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Test di uguaglianza tra 2 dizionari...
|
|
/// </summary>
|
|
bool testEqual(Dictionary<string, string> dict1, Dictionary<string, string> dict2)
|
|
{
|
|
// se nulli inizializzo...
|
|
if (dict1 == null)
|
|
{
|
|
dict1 = new Dictionary<string, string>();
|
|
}
|
|
if (dict2 == null)
|
|
{
|
|
dict2 = new Dictionary<string, string>();
|
|
}
|
|
// in primis testo num elementi
|
|
int num1 = dict1.Count;
|
|
int num2 = dict2.Count;
|
|
if (num1 != num2)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// comparazione sequenze ordinate...
|
|
return dict1.OrderBy(x => x.Key).SequenceEqual(dict2.OrderBy(x => x.Key));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ultimo Plate machine salvato
|
|
/// </summary>
|
|
MachineDTO getLastMachinePlate()
|
|
{
|
|
MachineDTO? currMPlate = new MachineDTO();
|
|
var rawData = redisDb.StringGet(Constants.STATUS_CURR_KEY);
|
|
if (string.IsNullOrEmpty(rawData))
|
|
{
|
|
rawData = redisDb.StringGet(Constants.MACHINE_CONF_PLATE);
|
|
}
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
currMPlate = JsonConvert.DeserializeObject<MachineDTO>($"{rawData}");
|
|
}
|
|
if (currMPlate == null)
|
|
{
|
|
currMPlate = new MachineDTO();
|
|
}
|
|
return currMPlate;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ultimo ProdDTO salvato
|
|
/// </summary>
|
|
ProductionDTO getCurrProdData()
|
|
{
|
|
ProductionDTO? currProdData = new ProductionDTO();
|
|
var rawData = redisDb.StringGet(Constants.PROD_CURR_KEY);
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
currProdData = JsonConvert.DeserializeObject<ProductionDTO>($"{rawData}");
|
|
}
|
|
if (currProdData == null)
|
|
{
|
|
currProdData = new ProductionDTO();
|
|
}
|
|
return currProdData;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stato precedentemente salvato su REDIS x calcolo variazioni
|
|
/// </summary>
|
|
Dictionary<string, string> getLastStatusVal()
|
|
{
|
|
Dictionary<string, string> answ = new Dictionary<string, string>();
|
|
if (redisDb != null)
|
|
{
|
|
var rawData = redisDb.StringGet(Constants.STATUS_LAST_KEY);
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
try
|
|
{
|
|
var rawDict = JsonConvert.DeserializeObject<Dictionary<string, string>>($"{rawData}");
|
|
answ = rawDict != null ? rawDict : new Dictionary<string, string>();
|
|
}
|
|
catch
|
|
{ }
|
|
}
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Salva su redis il set di valori ricevuti
|
|
/// </summary>
|
|
bool setLastStatusVal(Dictionary<string, string> dataList)
|
|
{
|
|
bool answ = false;
|
|
if (redisDb != null)
|
|
{
|
|
string rawData = JsonConvert.SerializeObject(dataList);
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
answ = redisDb.StringSet(Constants.STATUS_LAST_KEY, rawData);
|
|
}
|
|
}
|
|
return answ;
|
|
}
|
|
|
|
void calcParetoStatus()
|
|
{
|
|
// ricalcolo ogni 30" +/- 2sec
|
|
int minPeriod = 28000;
|
|
int maxPeriod = 32000;
|
|
|
|
do
|
|
{
|
|
// periodo riferimento sempre ricalcolato...
|
|
DateTime inizio = DateTime.Today;
|
|
DateTime fine = DateTime.Today.AddDays(1);
|
|
// recupero pareto da DB
|
|
var newStatus = dbController.ParetoStatusMach(MachineId, inizio, fine);
|
|
// riordino e aggiungo i dati di status...
|
|
var statusAllDTO = machineStatusConf
|
|
.Select(x => new ParetoStatusDTO()
|
|
{
|
|
CodStatus = $"MS_{x.MStatusID:000}",
|
|
CssClass = x.Css,
|
|
Description = x.Description,
|
|
Group = x.Group,
|
|
Prior = x.Priority
|
|
})
|
|
.ToList();
|
|
// ora ciclo tra i valori trovati...
|
|
foreach (var item in newStatus)
|
|
{
|
|
var recStatus = statusAllDTO.Where(x => x.CodStatus == item.CodStatus).FirstOrDefault();
|
|
if (recStatus != null)
|
|
{
|
|
recStatus.TotDuration = item.TotDuration;
|
|
}
|
|
}
|
|
|
|
// salvo i dati insieme alla traduzione degli stati relativa...
|
|
var statusActDTO = statusAllDTO
|
|
.Where(x => x.TotDuration > 0)
|
|
.OrderByDescending(x => x.TotDuration).ToList();
|
|
|
|
// serializzo e salvo i valori x pareto
|
|
string rawData = JsonConvert.SerializeObject(statusActDTO);
|
|
mpStatusParetoSendPipe.saveAndSendMessage(Constants.STATUS_PARETO_KEY, rawData);
|
|
|
|
// calcolo le daily duration e invio
|
|
List<DisplayDataDTO> currMDayDurDTO = getMachDayDurDTO(statusAllDTO);
|
|
if (currMDayDurDTO.Count > 0)
|
|
{
|
|
// serializzo e salvo
|
|
rawData = JsonConvert.SerializeObject(currMDayDurDTO);
|
|
mpDayDurSendPipe.saveAndSendMessage(Constants.MACH_DAY_DUR_CURR_KEY, rawData);
|
|
}
|
|
|
|
// calcolo Current:MachStats (pareto status macchina...)
|
|
List<DisplayDataDTO> currMStatDTO = getMachStatDTO(statusActDTO);
|
|
if (currMStatDTO.Count > 0)
|
|
{
|
|
// serializzo e salvo
|
|
rawData = JsonConvert.SerializeObject(currMStatDTO);
|
|
mpStatsSendPipe.saveAndSendMessage(Constants.MACH_STATS_CURR_KEY, rawData);
|
|
}
|
|
|
|
// attesa random di circa 1 minuti x calcolare...
|
|
Thread.Sleep(rand.Next(minPeriod, maxPeriod));
|
|
} while (true);
|
|
}
|
|
|
|
void dbMaintenance()
|
|
{
|
|
// ricalcolo ogni 120 min CIRCA +/- 2%
|
|
int msecOra = 1000 * 60 * 120;
|
|
int minPeriod = msecOra * 98 / 100;
|
|
int maxPeriod = msecOra * 102 / 100;
|
|
|
|
// leggo conf x giorni da tenere...
|
|
int.TryParse(config.GetValue<string>("OptPar:NumDay2Keep"), out NumDay2Keep);
|
|
|
|
do
|
|
{
|
|
// effettuo su DB chiamata stored x pulizia dati vecchi
|
|
var cleanupResult = dbController.RemoveOldData(NumDay2Keep);
|
|
|
|
// loggo esito cleanup eseguito
|
|
foreach (var item in cleanupResult)
|
|
{
|
|
Log.Info($"DB Cleanup: {item.CodTask} | {item.Result}");
|
|
}
|
|
|
|
// attesa random da periodo...
|
|
Thread.Sleep(rand.Next(minPeriod, maxPeriod));
|
|
} while (true);
|
|
}
|
|
|
|
|
|
void persistCount()
|
|
{
|
|
// eseguo ogni 1 min CIRCA +/- 2%
|
|
int msecOra = 1000 * 60 * 1;
|
|
int minPeriod = msecOra * 98 / 100;
|
|
int maxPeriod = msecOra * 102 / 100;
|
|
|
|
// per prima cosa leggo i counters attuali...
|
|
countStatus = dbController.CountersGetAll();
|
|
do
|
|
{
|
|
// salvo su DB valori counters
|
|
_ = dbController.CountersUpsertMany(countStatus);
|
|
Log.Debug($"Counters: {countStatus.Count} saved");
|
|
|
|
// attesa random da periodo...
|
|
Thread.Sleep(rand.Next(minPeriod, maxPeriod));
|
|
} while (true);
|
|
}
|
|
List<DisplayDataDTO> getMachDayDurDTO(List<ParetoStatusDTO> currStatus)
|
|
{
|
|
List<DisplayDataDTO> dayDurList = new List<DisplayDataDTO>();
|
|
// calcolo in minuti assoluti
|
|
int i = 0;
|
|
dayDurList = currStatus
|
|
.Select(x => new DisplayDataDTO()
|
|
{
|
|
Order = i++,
|
|
Type = "TIME",
|
|
Title = $"{x.Group} | {x.Description}",
|
|
ValueNum = x.TotDuration,
|
|
Value = $"{x.TotDuration:N2}",
|
|
DisplFormat = "N0",
|
|
MinVal = 0,
|
|
MaxVal = 1,
|
|
IsNumeric = true,
|
|
EnablePlot = true,
|
|
ShowBar = true,
|
|
HLShow = x.TotDuration > 5,
|
|
CssClass = x.CssClass
|
|
})
|
|
.ToList();
|
|
return dayDurList;
|
|
}
|
|
|
|
List<DisplayDataDTO> getMachStatDTO(List<ParetoStatusDTO> currStatus)
|
|
{
|
|
List<DisplayDataDTO> machStatList = new List<DisplayDataDTO>();
|
|
// calcolo come % tempo RUN sul totale...
|
|
float globDur = currStatus.Sum(x => x.TotDuration);
|
|
int i = 0;
|
|
machStatList = currStatus
|
|
.Select(x => new DisplayDataDTO()
|
|
{
|
|
Order = i++,
|
|
Type = "PERC",
|
|
Title = $"{x.Group} | {x.Description}",
|
|
ValueNum = x.TotDuration / globDur,
|
|
Value = $"{x.TotDuration / globDur:P1}",
|
|
DisplFormat = "P1",
|
|
MinVal = 0,
|
|
MaxVal = 1,
|
|
IsNumeric = true,
|
|
EnablePlot = true,
|
|
ShowBar = true,
|
|
HLShow = x.TotDuration > (globDur * 0.01),
|
|
CssClass = x.CssClass
|
|
})
|
|
.ToList();
|
|
return machStatList;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gestione evento ricezione messaggi allarmi secondo tipologia attiva...
|
|
/// </summary>
|
|
async void AlarmsValPipe_EA_NewMessage(object? sender, EventArgs e)
|
|
{
|
|
PubSubEventArgs currArgs = (PubSubEventArgs)e;
|
|
// conversione on-the-fly List<string> --> allarmi
|
|
if (!string.IsNullOrEmpty(currArgs.newMessage))
|
|
{
|
|
DateTime adesso = DateTime.Now;
|
|
// variabili accessorie x persistenza su DB
|
|
List<AlarmRecModel> alarmRecList = new List<AlarmRecModel>();
|
|
List<AlarmLogModel> alarmLogList = new List<AlarmLogModel>();
|
|
|
|
Random rand = new Random();
|
|
|
|
if (alarmMode == AlarmReportingMode.RawList)
|
|
{
|
|
try
|
|
{
|
|
// decodifico allarmi ricevuti
|
|
var alarmListRaw = JsonConvert.DeserializeObject<List<string>>(currArgs.newMessage);
|
|
|
|
// effettuo selezione dei SOLI allarmi distinct (in caso di cambio posizione...)
|
|
List<string> alarmList = new List<string>();
|
|
if (alarmListRaw != null && alarmListRaw.Count > 0)
|
|
{
|
|
alarmList = alarmListRaw
|
|
.GroupBy(x => x)
|
|
.Select(grp => grp.First())
|
|
.ToList();
|
|
}
|
|
// gestione allarmi muted
|
|
List<AlarmListModel>? mutedAlarmsList = new List<AlarmListModel>();
|
|
var rawData = redisDb.StringGet(Constants.ALARMS_SETT_RLIST_KEY);
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
mutedAlarmsList = JsonConvert.DeserializeObject<List<AlarmListModel>>($"{rawData}");
|
|
}
|
|
if (mutedAlarmsList == null)
|
|
{
|
|
mutedAlarmsList = dbController.AlarmListGetAll();
|
|
}
|
|
|
|
// rileggo da REDIS la conf attuale allarmi già attivi da area ALARM_CURR_KEY
|
|
List<string> lastAlarms = new List<string>();
|
|
rawData = redisDb.StringGet(Constants.ALARM_CURR_KEY);
|
|
// se trovati uso questi...
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
var rawLastAlarms = JsonConvert.DeserializeObject<List<string>>($"{rawData}");
|
|
lastAlarms = rawLastAlarms != null ? rawLastAlarms : new List<string>();
|
|
}
|
|
|
|
// recupero DICT dei blink allarmi (per filtrare fronte discesa in caso di allarmi
|
|
// che cessano e tornano attivi)
|
|
Dictionary<string, int> blinkStatus = new Dictionary<string, int>();
|
|
rawData = redisDb.StringGet(Constants.ALARM_BLINK_CURR_KEY);
|
|
// se trovati uso questi...
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
var rawBlink = JsonConvert.DeserializeObject<Dictionary<string, int>>($"{rawData}");
|
|
blinkStatus = rawBlink != null ? rawBlink : new Dictionary<string, int>();
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
var currNewAlarm = newAlarm;
|
|
// se ho valori trim pre/post li applica...
|
|
if (!string.IsNullOrEmpty(AlarmCleanPre))
|
|
{
|
|
currNewAlarm = currNewAlarm.Replace(AlarmCleanPre, "");
|
|
}
|
|
if (!string.IsNullOrEmpty(AlarmCleanPost))
|
|
{
|
|
currNewAlarm = currNewAlarm.Replace(AlarmCleanPost, "");
|
|
}
|
|
// se AlarmIgnoreEmpty=true + vuoto --> salto
|
|
if (AlarmIgnoreEmpty && string.IsNullOrWhiteSpace(currNewAlarm))
|
|
{
|
|
alarmList.Remove(currNewAlarm);
|
|
alarmList.Remove(newAlarm);
|
|
}
|
|
else
|
|
{
|
|
// in primis... se è muted --> lo escludo...
|
|
bool isMuted = false;
|
|
var maybeMuted = mutedAlarmsList.Where(x => x.FullValue == currNewAlarm).FirstOrDefault();
|
|
if (maybeMuted != null)
|
|
{
|
|
isMuted = maybeMuted.Muted;
|
|
}
|
|
// cerco l'allarme nell'elenco degli allarmi dal DB
|
|
AlarmListModel? foundAlarm = getAlarmModel(currNewAlarm);
|
|
#if false
|
|
var foundAlarm = alarmsRecorded
|
|
.Where(x => x.FullValue == currNewAlarm)
|
|
.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(currNewAlarm);
|
|
// ora rileggo ed aggiorno redis e cerco di nuovo record...
|
|
alarmsRecorded = dbController.AlarmListGetAll();
|
|
redisDb.StringSet(Constants.ALARMS_SETT_RLIST_KEY, JsonConvert.SerializeObject(alarmsRecorded));
|
|
}
|
|
#endif
|
|
if (!isMuted)
|
|
{
|
|
if (!lastAlarms.Contains(currNewAlarm))
|
|
{
|
|
if (foundAlarm != null)
|
|
{
|
|
// segno come iniziato...
|
|
AlarmRecModel newAlarmRec = new AlarmRecModel()
|
|
{
|
|
MachineId = MachineId,
|
|
DtStart = adesso,
|
|
DtEnd = adesso.AddMinutes(-1),
|
|
AlarmId = foundAlarm.AlarmId
|
|
};
|
|
alarmRecList.Add(newAlarmRec);
|
|
}
|
|
// gestione blink allarmi
|
|
if (blinkStatus.ContainsKey(currNewAlarm))
|
|
{
|
|
blinkStatus[currNewAlarm] = BlinkCount;
|
|
Log.Info($"Alarm: {currNewAlarm} | BlinkCount: {BlinkCount}");
|
|
}
|
|
else
|
|
{
|
|
blinkStatus.Add(currNewAlarm, BlinkCount);
|
|
Log.Info($"NEW Alarm: {currNewAlarm} | BlinkCount: {BlinkCount}");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// rimuovo da alarmList perché muted...
|
|
alarmList.Remove(currNewAlarm);
|
|
alarmList.Remove(newAlarm);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// recupero elenco valori blink
|
|
List<string> chiavi = blinkStatus.Select(x => x.Key).ToList();
|
|
// decremento i blink
|
|
foreach (var item in chiavi)
|
|
{
|
|
if (blinkStatus[item] > 0)
|
|
{
|
|
blinkStatus[item]--;
|
|
}
|
|
}
|
|
// salvo blink status counter...
|
|
redisDb.StringSet(Constants.ALARM_BLINK_CURR_KEY, JsonConvert.SerializeObject(blinkStatus));
|
|
|
|
// 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);
|
|
}
|
|
bool isBlinking = false;
|
|
if (blinkStatus.ContainsKey(oldData))
|
|
{
|
|
isBlinking = blinkStatus[oldData] > 0;
|
|
}
|
|
|
|
if (!trovato && !isBlinking)
|
|
{
|
|
// 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
|
|
if (alarmRecList.Count > 0)
|
|
{
|
|
foreach (var item in alarmRecList)
|
|
{
|
|
_ = dbController.AlarmRecInsert(item);
|
|
await Task.Delay(10);
|
|
}
|
|
#if false
|
|
_ = dbController.AlarmRecInsertMany(alarmRecList);
|
|
#endif
|
|
}
|
|
if (alarmLogList.Count > 0)
|
|
{
|
|
_ = dbController.AlarmLogInsertMany(alarmLogList).Result;
|
|
}
|
|
|
|
// salvo nuovo elenco allarmi
|
|
if (alarmList != null)
|
|
{
|
|
lastAlarms = alarmList;
|
|
}
|
|
}
|
|
catch
|
|
{ }
|
|
}
|
|
else if (alarmMode == AlarmReportingMode.RawListBlink)
|
|
{
|
|
int numTry = 0;
|
|
int myTicket = 0;
|
|
Stopwatch stopWatch = new Stopwatch();
|
|
try
|
|
{
|
|
myTicket = AlarmsBlinkManager.getTicket();
|
|
// attendo che NON ci siano altri processi in coda...
|
|
while (!AlarmsBlinkManager.checkTicket(myTicket))
|
|
{
|
|
await Task.Delay(rand.Next(100, 130));
|
|
if (numTry % 2 == 0)
|
|
{
|
|
TimeSpan ts = DateTime.Now.Subtract(adesso);
|
|
Log.Debug($"Ticket {myTicket} | total wait {ts.TotalMilliseconds:N3}ms");
|
|
}
|
|
numTry++;
|
|
}
|
|
|
|
stopWatch.Start();
|
|
Log.Debug($"---------- INIZIO ticket {myTicket} ---------- ");
|
|
|
|
// decodifico allarmi ricevuti
|
|
var alarmListRaw = JsonConvert.DeserializeObject<List<string>>(currArgs.newMessage);
|
|
var alarmListRcvd = alarmListRaw != null ? alarmListRaw : new List<string>();
|
|
// bonifico allarmi
|
|
var alarmiListClean = AlarmsBlinkManager.cleanAlarms(AlarmCleanPre, AlarmCleanPost, alarmListRcvd);
|
|
|
|
// allarmi correnti
|
|
List<string> alarmListCurrent = AlarmsBlinkManager.alarmsActAck;
|
|
|
|
// init classi helper
|
|
List<string> addedAlarms = new List<string>();
|
|
List<string> ceasedAlarms = new List<string>();
|
|
// chiamo procedura in blocco...
|
|
bool fatto = AlarmsBlinkManager.processData(alarmiListClean, ref alarmListCurrent, ref addedAlarms, ref ceasedAlarms);
|
|
|
|
// salvo ed invio subito i current...
|
|
string serAlarms = JsonConvert.SerializeObject(alarmListCurrent);
|
|
alarmsSendPipe.saveAndSendMessage(Constants.ALARM_CURR_KEY, serAlarms);
|
|
|
|
// solo se abilitata gestione AlarmLog...
|
|
if (AlarmLogActive)
|
|
{
|
|
// se cambiato --> ultimo ricevuto != current...
|
|
string lastLogList = AlarmsBlinkManager.alarmsLogSaved != null ? AlarmsBlinkManager.alarmsLogSaved : "";
|
|
// calcolo nuova stringa...
|
|
string newLogString = string.Join(", ", alarmiListClean);
|
|
bool logChanged = !lastLogList.Equals(newLogString);
|
|
AlarmsBlinkManager.alarmsLogSaved = newLogString;
|
|
|
|
if (logChanged)
|
|
{
|
|
// preparo dati x alarmLog
|
|
AlarmLogModel newAlarmLog = new AlarmLogModel()
|
|
{
|
|
MachineId = MachineId,
|
|
DtRif = adesso,
|
|
MemAddress = "ND",
|
|
Index = 0,
|
|
Status = (uint)alarmiListClean.Count,
|
|
ValDecoded = alarmiListClean.Count > 0 ? string.Join(", ", alarmiListClean) : "All OK"
|
|
};
|
|
alarmLogList.Add(newAlarmLog);
|
|
_ = dbController.AlarmLogInsertMany(alarmLogList).Result;
|
|
Log.Debug("Inserito Record AlarmLogModel");
|
|
}
|
|
}
|
|
|
|
if (addedAlarms.Count > 0)
|
|
{
|
|
// preparo lista NEW x inserimento su DB
|
|
foreach (var item in addedAlarms)
|
|
{
|
|
// cerco l'allarme nell'elenco degli allarmi dal DB
|
|
AlarmListModel? foundAlarm = getAlarmModel(item);
|
|
if (foundAlarm != null)
|
|
{
|
|
// segno come iniziato...
|
|
AlarmRecModel newAlarmRec = new AlarmRecModel()
|
|
{
|
|
MachineId = MachineId,
|
|
DtStart = adesso,
|
|
DtEnd = adesso.AddMinutes(-1),
|
|
AlarmId = foundAlarm.AlarmId
|
|
};
|
|
alarmRecList.Add(newAlarmRec);
|
|
}
|
|
}
|
|
// salvo sul DB
|
|
#if false
|
|
foreach (var item in alarmRecList)
|
|
{
|
|
_ = dbController.AlarmRecInsert(item);
|
|
}
|
|
#endif
|
|
_ = dbController.AlarmRecInsertMany(alarmRecList);
|
|
Log.Debug($"Inseriti {alarmRecList.Count} Records AlarmRecModel");
|
|
}
|
|
if (ceasedAlarms.Count > 0)
|
|
{
|
|
// chiamo task x verifica chiusura ritardata
|
|
foreach (var item in ceasedAlarms)
|
|
{
|
|
await Task.Run(() =>
|
|
{
|
|
_ = checkCeasedAlarm(item);
|
|
});
|
|
}
|
|
// Loggo quanto fatto
|
|
Log.Debug($"Chiamato controllo differito per {ceasedAlarms.Count} allarmi cessati");
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
Log.Error($"Eccezione:{Environment.NewLine}{exc}");
|
|
}
|
|
// tolgo semaforo processing
|
|
AlarmsBlinkManager.isProcessing = false;
|
|
|
|
stopWatch.Stop();
|
|
Log.Debug($"---------- Fine ticket {myTicket} | {stopWatch.ElapsedMilliseconds} ms ---------- ");
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
// verifico codici allarmi ricevuti
|
|
var alarmList = JsonConvert.DeserializeObject<Dictionary<string, uint>>(currArgs.newMessage);
|
|
if (alarmList != null)
|
|
{
|
|
// variabili accessorie
|
|
List<string> activeAlarmList = new List<string>();
|
|
alarmLogList = new List<AlarmLogModel>();
|
|
|
|
// rileggo da REDIS la conf attuale allarmi da area ALARMS_SETT_KEY, altrimenti
|
|
// uso quella letta inizialmente da conf base
|
|
List<BaseAlarmBankConf>? currAlarmsBankConf = new List<BaseAlarmBankConf>();
|
|
var rawData = redisDb.StringGet(Constants.ALARMS_SETT_BBIT_KEY);
|
|
// se trovati uso questi...
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
currAlarmsBankConf = JsonConvert.DeserializeObject<List<BaseAlarmBankConf>>($"{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... DbSampleInt (tipicamente 60 sec...)
|
|
if (adesso.Subtract(lastExecAlarms).TotalSeconds > DbSampleInt)
|
|
{
|
|
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}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gestione evento ricezione messaggi tools
|
|
/// </summary>
|
|
void ToolsRecvPipe_EA_NewMessage(object? sender, EventArgs e)
|
|
{
|
|
PubSubEventArgs currArgs = (PubSubEventArgs)e;
|
|
// conversione on-the-fly Dictionary<string,int> --> parametri valorizzati (tra quelli configurati)
|
|
if (!string.IsNullOrEmpty(currArgs.newMessage))
|
|
{
|
|
DateTime adesso = DateTime.Now;
|
|
List<DisplayDataDTO>? redisToolsConf = new List<DisplayDataDTO>();
|
|
try
|
|
{
|
|
// verifico codici ricevuti
|
|
var toolsList = JsonConvert.DeserializeObject<Dictionary<string, double>>(currArgs.newMessage);
|
|
if (toolsList != null)
|
|
{
|
|
// recupero elenco parametri salvati in redis...
|
|
var rawData = redisDb.StringGet(Constants.TOOLS_CONF_KEY);
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
redisToolsConf = JsonConvert.DeserializeObject<List<DisplayDataDTO>>($"{rawData}");
|
|
// se ho i parametri...
|
|
if (redisToolsConf != null)
|
|
{
|
|
// ciclo x ogni valore ricevuto da message service...
|
|
foreach (var item in toolsList)
|
|
{
|
|
// cerco il parametro corrispondente...
|
|
var currToolVal = redisToolsConf.FirstOrDefault(x => x.Title == item.Key);
|
|
if (currToolVal != null)
|
|
{
|
|
currToolVal.ValueNum = item.Value;
|
|
currToolVal.Value = $"{item.Value.ToString(currToolVal.DisplFormat)}";
|
|
}
|
|
}
|
|
// verifico NON ci sia veto di scrittura... DbSampleInt (60 sec...)
|
|
if (adesso.Subtract(lastExecTools).TotalSeconds > DbSampleInt)
|
|
{
|
|
lastExecTools = adesso;
|
|
if (dbController != null)
|
|
{
|
|
// verifico x ogni parametro se sia completato il periodo di campionamento...
|
|
var val2save = toolsMan.processData(toolsList, redisToolsConf);
|
|
if (val2save.Count > 0)
|
|
{
|
|
// salvo sul DB
|
|
_ = dbController.DataLogInsertMany(val2save).Result;
|
|
Log.Info($"Tools | DB | {val2save.Count} rec inserted");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// invio sulla message pipeline corretta TUTTI i parametri aggiornati serializzati
|
|
string updRawVal = JsonConvert.SerializeObject(redisToolsConf);
|
|
toolsSendPipe.saveAndSendMessage(Constants.TOOLS_CURR_KEY, updRawVal);
|
|
if (redisToolsConf != null)
|
|
{
|
|
if (adesso.Subtract(lastLogDetail).TotalSeconds > 5 || numSendTools > 10)
|
|
{
|
|
Log.Info($"TOOLS | {redisToolsConf.Count} data transmitted x {numSendTools}");
|
|
lastLogDetail = adesso;
|
|
numSendTools = 0;
|
|
}
|
|
else
|
|
{
|
|
numSendTools++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
Log.Error($"Eccezione in ToolsRecvPipe_EA_NewMessage:{Environment.NewLine}{exc}{Environment.NewLine}{currArgs.newMessage}");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gestione evento ricezione messaggi parametri
|
|
/// </summary>
|
|
void ParamsValPipe_EA_NewMessage(object? sender, EventArgs e)
|
|
{
|
|
PubSubEventArgs currArgs = (PubSubEventArgs)e;
|
|
// conversione on-the-fly Dictionary<string,int> --> parametri valorizzati (tra quelli configurati)
|
|
if (!string.IsNullOrEmpty(currArgs.newMessage))
|
|
{
|
|
DateTime adesso = DateTime.Now;
|
|
List<DisplayDataDTO>? redisParamConf = new List<DisplayDataDTO>();
|
|
try
|
|
{
|
|
// verifico codici ricevuti
|
|
var paramsList = JsonConvert.DeserializeObject<Dictionary<string, double>>(currArgs.newMessage);
|
|
if (paramsList != null)
|
|
{
|
|
// recupero elenco parametri salvati in redis...
|
|
var rawData = redisDb.StringGet(Constants.PARAMS_CONF_KEY);
|
|
if (!string.IsNullOrEmpty(rawData))
|
|
{
|
|
redisParamConf = JsonConvert.DeserializeObject<List<DisplayDataDTO>>($"{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)}";
|
|
}
|
|
}
|
|
// verifico NON ci sia veto di scrittura... DbSampleInt (60 sec...)
|
|
if (adesso.Subtract(lastExecParams).TotalSeconds > DbSampleInt)
|
|
{
|
|
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;
|
|
Log.Info($"Parametri | DB | {params2save.Count} rec inserted");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// invio sulla message pipeline corretta TUTTI i parametri aggiornati serializzati
|
|
string updRawVal = JsonConvert.SerializeObject(redisParamConf);
|
|
paramsSendPipe.saveAndSendMessage(Constants.PARAMS_CURR_KEY, updRawVal);
|
|
if (redisParamConf != null)
|
|
{
|
|
if (adesso.Subtract(lastLogDetail).TotalSeconds > 5 || numSendParam > 30)
|
|
{
|
|
Log.Info($"Parametri | {redisParamConf.Count} params trasmessi x {numSendParam}");
|
|
lastLogDetail = adesso;
|
|
numSendParam = 0;
|
|
}
|
|
else
|
|
{
|
|
numSendParam++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
Log.Error($"Eccezione in ParamsValPipe_EA_NewMessage:{Environment.NewLine}{exc}{Environment.NewLine}{currArgs.newMessage}");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gestione evento ricezione messaggi status
|
|
/// </summary>
|
|
void MpStatusRecvPipe_EA_NewMessage(object? sender, EventArgs e)
|
|
{
|
|
PubSubEventArgs currArgs = (PubSubEventArgs)e;
|
|
// conversione on-the-fly Dictionary<string,int> --> parametri valorizzati (tra quelli configurati)
|
|
if (!string.IsNullOrEmpty(currArgs.newMessage))
|
|
{
|
|
DateTime adesso = DateTime.Now;
|
|
try
|
|
{
|
|
// verifico codici ricevuti
|
|
var dataList = JsonConvert.DeserializeObject<Dictionary<string, string>>(currArgs.newMessage);
|
|
if (dataList != null)
|
|
{
|
|
/************************************************************************
|
|
*
|
|
* <BROADCAST_EVENT DESCRIPTION = "MACHINE STATUS" IDENTIFIER = "1" >
|
|
* <!--
|
|
* DESCRIPTION:
|
|
* 0: MACHINE OFF
|
|
* 1: MACHINE ON
|
|
* -->
|
|
*
|
|
* <BROADCAST_EVENT DESCRIPTION = "PROC 1 STATUS" IDENTIFIER = "500" >
|
|
* <!--
|
|
* DESCRIPTION:
|
|
* 1: IDLE
|
|
* 2: CYCLE (RUN)
|
|
* 3: HOLD (HOLD, RUNH, HRUN)
|
|
* 6: ERROR
|
|
* 8: RESET
|
|
* 11: PAUSE (WAIT, INPUT)
|
|
* -->
|
|
*
|
|
* <BROADCAST_EVENT DESCRIPTION = "PROC 1 MODE" IDENTIFIER = "501" >
|
|
* <!--
|
|
* DESCRIPTION:
|
|
* 1: MDI
|
|
* 2: AUTO
|
|
* 3: STEP (BLK-BLK)
|
|
* 4: MANU (MANJOG)
|
|
* 5: MANJ (INCJOG)
|
|
* 6: RETPROF (PROFILE)
|
|
* 7: HOME
|
|
* 8: HPG (HANDWHEEL)
|
|
* -->
|
|
*
|
|
*
|
|
*************************************************************************/
|
|
|
|
// recupero elenco parametri salvati in redis...
|
|
Dictionary<string, int> parList = new Dictionary<string, int>();
|
|
// recupero status precedente...
|
|
MachineDTO lastMachinePlate = getLastMachinePlate();
|
|
MachineDTO nextMachinePlate = new MachineDTO();
|
|
|
|
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);
|
|
}
|
|
|
|
// effettuo verifica valori production e relativo invio
|
|
checkProduction(dataList);
|
|
|
|
// fix me todo inserire gestione program name!!!
|
|
Stopwatch sw = new Stopwatch();
|
|
sw.Start();
|
|
|
|
// calcolo con LUA...
|
|
sw.Restart();
|
|
nextMachinePlate = mpStatusMan.processData(parList, lastMachinePlate);
|
|
sw.Stop();
|
|
|
|
if (adesso.Subtract(lastLogDetail).TotalSeconds > 5 || numSendMStatus > 10)
|
|
{
|
|
Log.Info($"Calcolo LUA | {sw.ElapsedTicks} ticks | {sw.ElapsedMilliseconds} ms | {JsonConvert.SerializeObject(nextMachinePlate)} | x {numSendMStatus}");
|
|
lastLogDetail = adesso;
|
|
numSendMStatus = 0;
|
|
}
|
|
else
|
|
{
|
|
numSendMStatus++;
|
|
}
|
|
#if false
|
|
else
|
|
{
|
|
// calcolo status
|
|
if (machStatus == 0)
|
|
{
|
|
nextMachinePlate.StatusId = 7;
|
|
}
|
|
else
|
|
{
|
|
switch (procMode)
|
|
{
|
|
case 1:
|
|
nextMachinePlate.StatusId = 8;
|
|
break;
|
|
|
|
case 2:
|
|
nextMachinePlate.StatusId = 2;
|
|
break;
|
|
|
|
case 3:
|
|
nextMachinePlate.StatusId = 9;
|
|
break;
|
|
|
|
case 4:
|
|
nextMachinePlate.StatusId = 5;
|
|
break;
|
|
|
|
case 5:
|
|
nextMachinePlate.StatusId = 10;
|
|
break;
|
|
|
|
case 6:
|
|
nextMachinePlate.StatusId = 11;
|
|
break;
|
|
|
|
case 7:
|
|
nextMachinePlate.StatusId = 12;
|
|
break;
|
|
|
|
case 8:
|
|
nextMachinePlate.StatusId = 13;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
// eseguo cablata la gestione x ora... rispetto a MachineStatus + MachineMode
|
|
// invierò un machineDTO
|
|
switch (procStatus)
|
|
{
|
|
case 1:
|
|
lastMachinePlate.ModeId = 2;
|
|
break;
|
|
|
|
case 2:
|
|
lastMachinePlate.ModeId = 1;
|
|
break;
|
|
|
|
case 3:
|
|
lastMachinePlate.ModeId = 3;
|
|
break;
|
|
|
|
case 6:
|
|
lastMachinePlate.ModeId = 8;
|
|
break;
|
|
|
|
case 8:
|
|
lastMachinePlate.ModeId = 9;
|
|
break;
|
|
|
|
case 11:
|
|
lastMachinePlate.ModeId = 10;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
sw.Stop();
|
|
Log.Info($"Calcolo C# | {sw.ElapsedTicks} ticks | {sw.ElapsedMilliseconds} ms | {JsonConvert.SerializeObject(nextMachinePlate)}");
|
|
Log.Info("-------------");
|
|
}
|
|
#endif
|
|
|
|
// recupero elenco valori...
|
|
var lastStatusParams = getLastStatusVal();
|
|
// verifico parametri state se modificati da ultimo invio --> salvo su DB
|
|
bool isEqual = testEqual(dataList, lastStatusParams);
|
|
if (!isEqual)
|
|
{
|
|
// calcolo differenze e salvo...
|
|
var diffVal = dataList.Where(entry => !lastStatusParams.ContainsKey(entry.Key) || lastStatusParams[entry.Key] != entry.Value)
|
|
.ToDictionary(entry => entry.Key, entry => entry.Value);
|
|
|
|
// salvo ultimo blocco valori last status...
|
|
setLastStatusVal(dataList);
|
|
|
|
// salvo i valori modificati...
|
|
List<ProdLogModel> newProdRecords = diffVal
|
|
.Select(x => new ProdLogModel()
|
|
{
|
|
DtRif = adesso,
|
|
EvType = x.Key,
|
|
ValStr = x.Value,
|
|
MachineId = MachineId
|
|
})
|
|
.ToList();
|
|
// per ogni valore cerca di effettuare conversione a double...
|
|
double testVal = 0;
|
|
foreach (var item in newProdRecords)
|
|
{
|
|
testVal = 0;
|
|
double.TryParse(item.ValStr, out testVal);
|
|
item.ValNum = testVal;
|
|
}
|
|
// salvo su DB x log produzione
|
|
_ = dbController.ProdLogInsertMany(newProdRecords);
|
|
}
|
|
|
|
// confronto variazioni x "MACHINE STATUS"
|
|
if (nextMachinePlate.StatusId != lastMachinePlate.StatusId)
|
|
{
|
|
//string codStatus = $"MS_{diffVal[keyStatus]:000}";
|
|
string codStatus = $"MS_{nextMachinePlate.StatusId:000}";
|
|
// chiamo registrazione sul DB dello stato...
|
|
var newRecStatus = new StatusLogModel()
|
|
{
|
|
MachineId = MachineId,
|
|
DtRif = adesso,
|
|
Duration = 0,
|
|
CodStatus = codStatus
|
|
};
|
|
_ = dbController.StatusLogInsert(newRecStatus);
|
|
}
|
|
|
|
// invio sulla message pipeline corretta TUTTI i parametri aggiornati serializzati
|
|
string updRawVal = JsonConvert.SerializeObject(nextMachinePlate);
|
|
mpStatusSendPipe.saveAndSendMessage(Constants.STATUS_CURR_KEY, updRawVal);
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
Log.Error($"Eccezione in MpStatusRecvPipe_EA_NewMessage:{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
};
|
|
|
|
void checkProduction(Dictionary<string, string> dataList)
|
|
{
|
|
if (dataList.Count > 0)
|
|
{
|
|
var currProdDTO = getCurrProdData();
|
|
string currKey = "RECIPE NAME";
|
|
if (dataList.ContainsKey(currKey))
|
|
{
|
|
currProdDTO.ProgName = dataList[currKey];
|
|
}
|
|
currKey = "PART COUNT";
|
|
if (dataList.ContainsKey(currKey))
|
|
{
|
|
int numPz = 0;
|
|
int.TryParse(dataList[currKey], out numPz);
|
|
currProdDTO.CurrQty = numPz;
|
|
}
|
|
currKey = "CURR ORDER";
|
|
if (dataList.ContainsKey(currKey))
|
|
{
|
|
currProdDTO.Order = dataList[currKey];
|
|
}
|
|
string rawData = JsonConvert.SerializeObject(currProdDTO);
|
|
mpProdSendPipe.saveAndSendMessage(Constants.PROD_CURR_KEY, rawData);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gestione evento ricezione messaggi status x conteggi...
|
|
/// </summary>
|
|
void CounterRecvPipe_EA_NewMessage(object? sender, EventArgs e)
|
|
{
|
|
PubSubEventArgs currArgs = (PubSubEventArgs)e;
|
|
// conversione on-the-fly Dictionary<string,int> --> parametri valorizzati (tra quelli configurati)
|
|
if (!string.IsNullOrEmpty(currArgs.newMessage))
|
|
{
|
|
try
|
|
{
|
|
// verifico codici ricevuti
|
|
var dataList = JsonConvert.DeserializeObject<Dictionary<string, string>>(currArgs.newMessage);
|
|
//int valInt = 0;
|
|
if (dataList != null)
|
|
{
|
|
// chiamo metodo CounterManager... incrementi in MINUTI!!!
|
|
var incrSet = counterMan.processData(dataList);
|
|
if (incrSet != null)
|
|
{
|
|
foreach (var item in incrSet)
|
|
{
|
|
// verifico i valori > soglia 5 minuti...
|
|
if (item.Value > 5)
|
|
{
|
|
// salvo
|
|
var countRec = countStatus.Where(x => x.CCode == item.Key || x.CodAlias == item.Key).FirstOrDefault();
|
|
if (countRec != null)
|
|
{
|
|
// attenzione valori in ore, incrementi in minuti!
|
|
countRec.ActualVal = Math.Round(countRec.ActualVal + item.Value / 60, 6);
|
|
// resetto
|
|
counterMan.resetAccum(item.Key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
Log.Error($"Eccezione in CounterRecvPipe_EA_NewMessage:{Environment.NewLine}{exc}");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* --------------------------------
|
|
* MAIN
|
|
* --------------------------------*/
|
|
|
|
// Ciclo infinito x attesa chiusura con CTRL-C
|
|
do
|
|
{
|
|
Thread.Sleep(100);
|
|
} while (true); |