Files
Mapo-IOB-WIN/IOB-WIN-NEXT/IobFile/IobFileSoitaab.cs
T
Samuele Locatelli 83e971f1e9 Aggiunto veto accodamento QUEUE-IN:
- default 1 sec
- configurabile in OPCPAR come VETO_QUEUE_IN=15
- indica num secondi x cui NON accoda info IN per l'invio
2023-09-08 15:22:38 +02:00

568 lines
21 KiB
C#

using EgwProxy.SqlDb.DbModels;
using IOB_UT_NEXT;
using MapoSDK;
using Newtonsoft.Json;
using OpenQA.Selenium;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
namespace IOB_WIN_NEXT.IobFile
{
/// <summary>
/// Adapter specializzato per SOITAAB x la SOLA lettura stato macchina da log, da accoppiare a
/// adapter x IOB LANTEK x scrittura PODL
/// - IN: LOGFile macchina diretto
/// - OUT: --&gt; sigLog
/// --&gt; fluxLog
/// </summary>
public class IobFileSoitaab : Iob.Generic
{
#region Public Constructors
/// <summary>
/// Costruttore dell'IOB FileBased SOITAAB
/// </summary>
/// <param name="caller">AdapterForm chiamante</param>
/// <param name="IOBConf">Configurazione IOB per avvio</param>
public IobFileSoitaab(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf)
{
DateTime adesso = DateTime.Now;
string VetoReadSec = getOptPar("VetoReadSec");
setupSpecialParams();
if (pathList.ContainsKey("path-remBase"))
{
logDirPath = pathList["path-remBase"];
}
// fix parametri veto
if (!string.IsNullOrEmpty(VetoReadSec))
{
int.TryParse(VetoReadSec, out vetoReadFileSec);
}
// init a zero....
B_input = 0;
// provo prima lettura logfile
try
{
// eseguo subito un ciclo acquisizione log + processing
bool acquireLog = getRemoteLog();
if (acquireLog)
{
bool sentSignLog = processSignLogTable(adesso);
}
}
catch (Exception exc)
{
lgError($"Eccezione in IobFileSoitaab{Environment.NewLine}{exc}");
}
lastPING = DateTime.Now.AddHours(-1);
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Implementazione custom esecuzione task specifici
/// </summary>
/// <param name="task2exe"></param>
/// <returns></returns>
public override Dictionary<string, string> executeTasks(Dictionary<string, string> task2exe)
{
DateTime adesso = DateTime.Now;
// NON fa nulla... anche se non dovrebbe richiamarlo
Dictionary<string, string> taskDone = new Dictionary<string, string>();
lastReadPLC = DateTime.Now;
return taskDone;
}
/// <summary>
/// Recupero dati dinamici...
/// </summary>
public override Dictionary<string, string> getDynData()
{
DateTime adesso = DateTime.Now;
// dizionario vuoto / faccio direttamente accodamento in FluxLog
Dictionary<string, string> outVal = new Dictionary<string, string>();
// processo ed accodo!
processFluxLogTable(adesso);
lastReadPLC = DateTime.Now;
return outVal;
}
/// <summary>
/// Effettua lettura semafori principale <paramref name="currDispData">Parametri da
/// aggiornare x display in form</paramref>
/// </summary>
public override void readSemafori(ref newDisplayData currDispData)
{
DateTime adesso = DateTime.Now;
if (connectionOk)
{
// controllo veto checkDB
if (adesso > vetoDataRead)
{
// predispongo prox veto...
vetoDataRead = adesso.AddSeconds(vetoReadFileSec);
// semaforo
currDispData.semIn = Semaforo.SV;
// verifico SignLog e processo
bool acquireLog = getRemoteLog();
bool sentSignLog = processSignLogTable(adesso);
// verifico ProdData e processo
bool sentProdData = processProdDataTable(adesso);
}
}
else
{
B_input = 0;
currDispData.semIn = Semaforo.SR;
}
}
/// <summary>
/// Override connessione
/// </summary>
public override void tryConnect()
{
if (!connectionOk)
{
// controllo che il ping sia stato tentato almeno pingTestSec fa...
if (DateTime.Now.Subtract(lastPING).TotalSeconds > utils.CRI("pingTestSec"))
{
if (verboseLog || periodicLog)
{
lgInfo("FileSoitaab: ConnKO - tryConnect");
}
// in primis salvo data ping...
lastPING = DateTime.Now;
// se passa il ping faccio il resto...
if (testPingMachine == IPStatus.Success)
{
string szStatusConnection = "";
try
{
// ora provo connessione...
parentForm.commPlcActive = true;
// connessione OK se oltre al PING riesco a leggere la folder di base
if (Directory.Exists(logDirPath))
{
parentForm.commPlcActive = false;
connectionOk = true;
}
// refresh stato connessione!!!
if (connectionOk)
{
if (adpRunning)
{
lgInfo($"Connessione OK alla folder {logDirPath}");
lastReadPLC = DateTime.Now;
}
}
else
{
lgError("Impossibile procedere, connessione mancante...");
}
}
catch (Exception exc)
{
lgFatal($"Errore nella connessione all'adapter IobFileSoitaab: {szStatusConnection}{Environment.NewLine}{exc}");
connectionOk = false;
lgInfo($"Eccezione in TryConnect, Adapter IobFileSoitaab NON running, pausa di {utils.CRI("waitRecMSec")} msec prima di ulteriori tentativi di riconnessione");
}
}
else
{
// loggo no risposta ping ...
connectionOk = false;
B_input = 0;
if (verboseLog || periodicLog)
{
lgInfo($"Attenzione: IobFileSoitaab controllo PING fallito per IP {cIobConf.cncPingAddr}");
}
}
}
}
else
{
needRefresh = true;
}
}
public override void tryDisconnect()
{
// registro solo che è disconnesso
connectionOk = false;
}
#endregion Public Methods
#region Protected Fields
protected int vetoReadFileSec = 3;
#endregion Protected Fields
#region Protected Properties
/// <summary>
/// Stato di sync delle tab gestite
/// </summary>
protected List<SyncStateModel> elencoSyncState { get; set; } = new List<SyncStateModel>();
/// <summary>
/// Dati fluxLog serializzati in redis
/// </summary>
protected List<GenLogRow> fluxLogData
{
get
{
List<GenLogRow> answ = new List<GenLogRow>();
string redKeyFLog = redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:FluxLog");
var rawData = redisMan.getRSV(redKeyFLog);
if (!string.IsNullOrEmpty(rawData))
{
answ = JsonConvert.DeserializeObject<List<GenLogRow>>(rawData);
}
return answ;
}
set
{
string redKeyFLog = redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:FluxLog");
string fluxLogRaw = JsonConvert.SerializeObject(value);
redisMan.setRSV(redKeyFLog, fluxLogRaw);
}
}
#endregion Protected Properties
#region Private Properties
private string logDirPath { get; set; } = "\\\\ecs900\\Logs";
#endregion Private Properties
#region Private Methods
private static int decodeSoitaabLog(string val2test)
{
// di default NON EMERGENZA...
int valInt = 128;
switch (val2test)
{
case "START;1":
valInt = 1 + 2 + 128;
break;
case "END;1":
valInt = 1 + 128;
break;
case "START;0":
case "END;0":
case "HOLD;":
valInt = 1 + 16 + 128;
break;
default:
break;
}
return valInt;
}
private GenLogRow convertToMachineLog(string dailyLog)
{
GenLogRow answ = new GenLogRow();
if (!string.IsNullOrEmpty(dailyLog))
{
// preventivamente: doppio ";;" --> ";"
dailyLog = dailyLog.Replace(";;", ";");
var sSplit = dailyLog.Split(';');
answ.dtRif = DateTime.ParseExact($"{sSplit[0]} {sSplit[1]}", "yyyy-M-d HH:mm:ss", null);
answ.valString = $"{sSplit[2]};{sSplit[3]}";
}
return answ;
}
/// <summary>
/// Converte un file di log della macchina in un dizionario
/// </summary>
/// <param name="dailyLog"></param>
/// <returns></returns>
private List<GenLogRow> getGenLogFromMachineLog(string dailyLog)
{
List<GenLogRow> result = new List<GenLogRow>();
//se ho valori.. faccio split!
if (!string.IsNullOrEmpty(dailyLog))
{
var rowList = dailyLog.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
if (rowList != null && rowList.Length > 0)
{
result = rowList
.Where(x => !string.IsNullOrEmpty(x))
.Select(x => convertToMachineLog(x))
.ToList();
}
}
return result;
}
/// <summary>
/// Recupera file log da analizzare
/// </summary>
/// <returns></returns>
private bool getRemoteLog()
{
bool answ = false;
bool pingOk = testPingMachine == IPStatus.Success;
if (pingOk && Directory.Exists(logDirPath))
{
string filePath = Path.Combine(logDirPath, $"Report-{DateTime.Today.Day}.txt");
bool hasDaily = File.Exists(filePath);
// cerco file quotidiano...
if (!hasDaily)
{
var fileList = Directory.GetFiles(logDirPath, "Report-*.txt");
DateTime lastMod = DateTime.Today.AddYears(-1);
foreach (var cFile in fileList)
{
var tempFile = Path.Combine(logDirPath, cFile);
var lwTime = File.GetLastWriteTime(tempFile);
if (lwTime >= lastMod)
{
lastMod = lwTime;
filePath = tempFile;
}
}
}
// leggo file
string rawVal = File.ReadAllText(Path.Combine(logDirPath, filePath));
if (!string.IsNullOrEmpty(rawVal))
{
// salvo in redis
string redKey = redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:Act");
redisMan.setRSV(redKey, rawVal);
answ = true;
}
}
return answ;
}
/// <summary>
/// Esegue processing + invio dati tab SignLog
/// </summary>
/// <param name="adesso"></param>
/// <returns></returns>
private bool processFluxLogTable(DateTime adesso)
{
bool fatto = false;
// leggo eventuali dati di fluxlog ed invio...
if (fluxLogData != null && fluxLogData.Count > 0)
{
string sVal = "";
foreach (var fLog2send in fluxLogData)
{
var kvpFlux = fLog2send.valString.Split(';');
if (kvpFlux.Count() > 1)
{
sVal = $"[DYNDATA] |{fLog2send.dtRif:yyyy-MM-dd HH:mm:ss}|{kvpFlux[0]}|{kvpFlux[1]}";
// chiamo accodamento con dataora corretta...
accodaFLog(sVal, qEncodeFLog(fLog2send.dtRif, kvpFlux[0], kvpFlux[1]));
}
}
// svuoto coda invio fluxlog...
fluxLogData = new List<GenLogRow>();
// fatto!
fatto = true;
}
return fatto;
}
/// <summary>
/// Esegue processing + invio dati tab ProdData
/// </summary>
/// <param name="adesso"></param>
/// <returns></returns>
private bool processProdDataTable(DateTime adesso)
{
bool fatto = false;
#if false
string tabNameIn = "ProdData";
string tabNameOut = "ProdDataToMes";
// cerco info sui SignLog (ToMes e letti)
var currSignLogRead = elencoSyncState.FirstOrDefault(x => x.TableName == tabNameIn);
// se nullo inizializzo
if (currSignLogRead == null)
{
currSignLogRead = new SyncStateModel()
{
LastIdx = 0,
LastUpdate = adesso,
TableName = tabNameIn,
Note = "Init"
};
}
var currSignLogSent = elencoSyncState.FirstOrDefault(x => x.TableName == tabNameOut);
// se nullo inizializzo
if (currSignLogSent == null)
{
currSignLogSent = new SyncStateModel()
{
LastIdx = 0,
LastUpdate = adesso,
TableName = tabNameOut,
Note = "Init"
};
}
// verifica se ci siano dati da trasmettere (sui valori LastIdx)
if (currSignLogRead.LastIdx > currSignLogSent.LastIdx)
{
// recupero i dati dal DB...
var data2send = dbProxy.MachProdDataGetNew(currSignLogSent.LastIdx);
// se ho dati preparo invio
if (data2send != null && data2send.Count > 0)
{
foreach (var sLog2send in data2send)
{
// se il record è apertura PODL chiamo apertura
trySetupPODL(0);
// se il record è chiusura PODL chiamo chiusura... DOPO aver cercato PODL
// --> ODL
tryClosePODL(0);
}
}
// aggiorno idx inviato...
currSignLogSent.Note = $"Updated from {currSignLogRead.LastIdx}";
currSignLogSent.LastUpdate = adesso;
currSignLogSent.LastIdx = currSignLogRead.LastIdx;
fatto = true;
}
// alla fine aggiorno i dati inviati!
dbProxy.SyncStateUpsert(currSignLogSent);
#endif
return fatto;
}
/// <summary>
/// Esegue processing + invio dati tab SignLog
/// </summary>
/// <param name="adesso"></param>
/// <returns></returns>
private bool processSignLogTable(DateTime adesso)
{
bool fatto = false;
// init oggetto x processing
Dictionary<DateTime, int> sigLogFromFile = new Dictionary<DateTime, int>();
// recupero i dati dai logs files, attuale e nuovo...
string redKeyAct = redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:Act");
string redKeyLast = redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:Last");
string fileAct = redisMan.getRSV(redKeyAct);
string fileLast = redisMan.getRSV(redKeyLast);
// confronto con i dati dell'ultimo LOG FILE processato
int lenghtAct = fileAct != null ? fileAct.Length : 0;
int lenghtLast = fileLast != null ? fileLast.Length : 0;
//if (lenghtAct > 0 && lenghtAct != lenghtLast)
if (lenghtAct > 0)
{
List<GenLogRow> logNew = new List<GenLogRow>();
List<GenLogRow> sigLogData = new List<GenLogRow>();
List<GenLogRow> logLast = getGenLogFromMachineLog(fileLast);
List<GenLogRow> logAct = getGenLogFromMachineLog(fileAct);
// SE c'è prendo ultima riga inviata x confronto...
var fileLastEnd = logLast.LastOrDefault();
if (fileLastEnd != null)
{
// ora prendo SOLAMENTE le righe nuove
logNew = logAct
.Where(x => x.dtRif > fileLastEnd.dtRif)
.ToList();
}
else
{
logNew = logAct;
}
// solo SE ho nuovi dati....
if (logNew.Count > 0)
{
// processo le righe nuove e le accodo in redis come elenco FluxLog da inviare
// (appoggio in redis)... andando ad accodarle...
var currFLD = fluxLogData;
currFLD.AddRange(logNew);
fluxLogData = currFLD;
// estraggo solo info x sig log
sigLogData = logNew
.Where(x => x.valString.StartsWith("START") || x.valString.StartsWith("HOLD") || x.valString.StartsWith("END"))
.ToList();
// processo le righe nuove trasformando al volo eventi...
foreach (var sLog2send in sigLogData)
{
/* -----------------------------------------------------
* bitmap MAPO STANDARD 60
* B0: 001 POWER_ON
* B1: 002 RUN
* B2: 004 pzCount
* B3: 008 allarme
* B4: 016 manuale
* B5: 032 slowTC
* B6: 064 WarmUpCoolDown
* B7: 128 EmergArmata
*
----------------------------------------------------- */
int valInt = decodeSoitaabLog(sLog2send.valString);
string currVal = getEncodSigLog(sLog2send.dtRif, valInt, counterSigIN);
// verifico non sia in veto invio iniziale...
if (dtVetoQueueIN >= adesso)
{
lgDebug($"[VETO FOR QUEUE-IN] | {currVal} - MESSAGE NOT SENT | {adesso:yyyyMMdd_HHmmss}");
}
else
{
// --> accodo (valore già formattato)!
QueueIN.Enqueue(currVal);
// loggo!
lgTrace(string.Format("[QUEUE-IN] {0}", currVal));
counterSigIN++;
if (counterSigIN > 9999)
{
counterSigIN = 0;
}
}
}
// salvo in last il valore di act...
redisMan.setRSV(redKeyLast, fileAct);
// calcolo B_input valido
var lastRec = sigLogData.LastOrDefault();
if (lastRec != null)
{
// salvo in B_input ultimo valore letto...
int lastValInt = decodeSoitaabLog(lastRec.valString);
B_input = lastValInt;
}
}
fatto = true;
}
return fatto;
}
#endregion Private Methods
}
}