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 { /// /// 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: --> sigLog /// --> fluxLog /// public class IobFileSoitaab : Iob.Generic { #region Public Constructors /// /// Costruttore dell'IOB FileBased SOITAAB /// /// AdapterForm chiamante /// Configurazione IOB per avvio 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 /// /// Implementazione custom esecuzione task specifici /// /// /// public override Dictionary executeTasks(Dictionary task2exe) { DateTime adesso = DateTime.Now; // NON fa nulla... anche se non dovrebbe richiamarlo Dictionary taskDone = new Dictionary(); lastReadPLC = DateTime.Now; return taskDone; } /// /// Recupero dati dinamici... /// public override Dictionary getDynData() { DateTime adesso = DateTime.Now; // dizionario vuoto / faccio direttamente accodamento in FluxLog Dictionary outVal = new Dictionary(); // processo ed accodo! processFluxLogTable(adesso); lastReadPLC = DateTime.Now; return outVal; } /// /// Effettua lettura semafori principale Parametri da /// aggiornare x display in form /// 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; } } /// /// Override connessione /// 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 /// /// Stato di sync delle tab gestite /// protected List elencoSyncState { get; set; } = new List(); /// /// Dati fluxLog serializzati in redis /// protected List fluxLogData { get { List answ = new List(); string redKeyFLog = redisMan.redHash($"IOB:CurrData:{cIobConf.codIOB}:LogFile:FluxLog"); var rawData = redisMan.getRSV(redKeyFLog); if (!string.IsNullOrEmpty(rawData)) { answ = JsonConvert.DeserializeObject>(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; } /// /// Converte un file di log della macchina in un dizionario /// /// /// private List getGenLogFromMachineLog(string dailyLog) { List result = new List(); //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; } /// /// Recupera file log da analizzare /// /// 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; } /// /// Esegue processing + invio dati tab SignLog /// /// /// 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(); // fatto! fatto = true; } return fatto; } /// /// Esegue processing + invio dati tab ProdData /// /// /// 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; } /// /// Esegue processing + invio dati tab SignLog /// /// /// private bool processSignLogTable(DateTime adesso) { bool fatto = false; // init oggetto x processing Dictionary sigLogFromFile = new Dictionary(); // 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 logNew = new List(); List sigLogData = new List(); List logLast = getGenLogFromMachineLog(fileLast); List 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); // --> 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 } }