Files
2024-04-15 11:16:50 +02:00

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);