#if false using EgwProxy.SqlDb.DbModels; #endif using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using MapoSDK; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.NetworkInformation; using static IOB_UT_NEXT.CustomObj; namespace IOB_WIN_SQL.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.GenericNext { #region Public Constructors /// /// Costruttore dell'IOB FileBased SOITAAB /// /// Form chiamante /// Configurazione (v 4.x) public IobFileSoitaab(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull) { 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); } string sFluxOnRead = getOptPar("sendFluxOnRead"); if (!string.IsNullOrEmpty("sendFluxOnRead")) { bool.TryParse(sFluxOnRead, out sendFluxOnRead); } // 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}"); } DtHelp.lastPING = DateTime.Now.AddHours(-1); } #endregion Public Constructors #region Public Methods /// /// Implementazione custom esecuzione task specifici /// /// /// public override Dictionary executeTasks(Dictionary task2exe, string codTav) { DateTime adesso = DateTime.Now; // NON fa nulla... anche se non dovrebbe richiamarlo Dictionary taskDone = new Dictionary(); DtHelp.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); DtHelp.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 > DtHelp.vetoDataRead) { // predispongo prox veto... DtHelp.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(DtHelp.lastPING).TotalSeconds > utils.CRI("pingTestSec")) { if (verboseLog || periodicLog) { lgInfo("FileSoitaab: ConnKO - tryConnect"); } // in primis salvo data ping... DtHelp.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) { queueInEnabCurr = true; if (adpRunning) { lgInfo($"Connessione OK alla folder {logDirPath}"); DtHelp.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 {IOBConfFull.Device.Connect.PingIpAddr}"); } } } } else { needRefresh = true; } } public override void tryDisconnect() { // registro solo che è disconnesso connectionOk = false; queueInEnabCurr = false; } #endregion Public Methods #region Protected Fields protected bool sendFluxOnRead = false; protected int vetoReadFileSec = 3; #endregion Protected Fields #region Protected Properties #if false /// /// Stato di sync delle tab gestite /// protected List elencoSyncState { get; set; } = new List(); #endif #endregion Protected Properties #region Protected Methods /// /// Conversione string row in log generico /// /// /// protected override 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; } /// /// Recupera file log da analizzare /// /// protected override 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:{IOBConfFull.General.CodIOB}:LogFile:Act"); redisMan.setRSV(redKey, rawVal); answ = true; } } return answ; } #endregion Protected Methods #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; } /// /// 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... bool sent = accodaFLog(kvpFlux[0], sVal, qEncodeFLog(fLog2send.dtRif, kvpFlux[0], kvpFlux[1])); if (sent) { // traccio valore DynData x analisi trackDynData(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; 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 fileAct = redisMan.getRSV(redKeyLogfileAct); string fileLast = redisMan.getRSV(redKeyLogfileLast); // 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) .OrderBy(x => x.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")) .OrderBy(x => x.dtRif) .ToList(); // processo le righe nuove trasformando al volo SOLO 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 (queueInEnabCurr) { // --> accodo (valore già formattato)! QHelp.QueueIN.Enqueue(currVal); // loggo! lgTrace(string.Format("[QUEUE-IN] {0}", currVal)); counterSigIN++; if (counterSigIN > 9999) { counterSigIN = 0; } } else { lgDebug($"[VETO FOR QUEUE-IN] | {currVal} - MESSAGE NOT SENT | {adesso:yyyyMMdd_HHmmss}"); checkVetoQueueIn(); } } /* ---------------------------------------------------------- * processo righe x costruire una lista di eventi produzione: * - elenco di eventi contapezzi * - lista xODL come articolo + quantità prodotta (_1, _2, ...) * - eventuale ultimo xODL che resta aperto * * successivamente processing + invio info raccolte * - recupero PODL aperti * - apertura/chiusura PODL esistenti * - creazione PODL mancanti * - dichiarazione contapezzi * * ---------------------------------------------------------- */ string sVal = ""; string descrArt = ""; List elencoProdBatch = new List(); ProdBatchData lastProdBatch = new ProdBatchData(); // ora processo TUTTO x il generico caso fluxLog...... foreach (var fLog2send in logNew) { // separo per ";" var currData = fLog2send.valString.Split(';'); if (currData.Length > 1) { if (sendFluxOnRead) { // per prima cosa fluxLog a prescindere... sVal = $"[LOGFILE]{currData[0]}|{currData[1]}"; // ...e chiamo accodamento bool sent = accodaFLog(currData[0], sVal, qEncodeFLog(fLog2send.dtRif, currData[0], currData[1])); if (sent) { // traccio valore DynData x analisi trackDynData(currData[0], currData[1]); } } switch (currData[0]) { case "END": // se è un end --> è comunque NON + runnning isRunningState = false; // chiudo batch precedente + in elenco (SE era valido...) if (!string.IsNullOrEmpty(lastProdBatch.codArt)) { lastProdBatch.dtEnd = fLog2send.dtRif; elencoProdBatch.Add(lastProdBatch); } // reset articolo.. lastArtDescr = ""; // nuovo batch VUOTO lastProdBatch = new ProdBatchData(); contapezziPLC = 0; break; case "START": isRunningState = currData[1] == "1"; break; case "PARTCODE": // inizio confronto articolo con precedente x capire se sia NUOVO descrArt = currData[1]; // devo scartare i "doppi percorsi" if (descrArt != lastArtDescr) { // verifico se sia DAVVERO cambiato articolo... prendo // articolo senza indice (_1, _2, _3, ...) var artDataLast = lastArtDescr.Split('_'); var artDataNew = descrArt.Split('_'); // nuovo articolo (NON vuoto) if (!string.IsNullOrEmpty(artDataNew[0]) && artDataLast[0] != artDataNew[0]) { // chiudo batch precedente + in elenco (SE era valido...) if (!string.IsNullOrEmpty(lastProdBatch.codArt)) { // chiudo prod lastProdBatch.dtEnd = fLog2send.dtRif; elencoProdBatch.Add(lastProdBatch); } // nuovo batch lastProdBatch = new ProdBatchData() { codArt = artDataNew[0], dtStart = fLog2send.dtRif, numPz = 1 }; } // solo nuova copia articolo else { // aumento numPezzi batch lastProdBatch.numPz++; } // salvo nuovo art processato lastArtDescr = descrArt; } break; default: break; } } } // recupero elenco PODL corrente... List reqPOdlList = MachineNextPodl(); // adesso processo tutti i prodBatch collezionati x invio foreach (var item in elencoProdBatch) { int currIdxPOdl = 0; // cerco se articolo sia in elenco PODL var listPOdl = reqPOdlList .Where(x => x.CodArticolo.Equals(item.codArt, StringComparison.InvariantCultureIgnoreCase)) .ToList(); // se trovato --> invio start/stop PODL if (listPOdl != null && listPOdl.Count > 0) { var currPodl = listPOdl.FirstOrDefault(); if (currPodl != null) { currIdxPOdl = currPodl.IdxPromessa; // elimino da elenco PODL reqPOdlList.Remove(currPodl); } } else { // se NON trovato --> creo PODL, poi avvio/chiudo currIdxPOdl = TryCreatePodl(item.codArt, CodGruppoIob, item.numPz); } // se idxPOdl valido if (currIdxPOdl > 0) { // mando apertura PODL SendStartPodl(currIdxPOdl, item.dtStart); // chiudo PODL... if (item.dtEnd != null) { // invio pezzi 3 sec prima di chiusura SendPzIncrAtDate(item.numPz, ((DateTime)item.dtEnd).AddSeconds(-3)); // chiudo SendClosePOdl(currIdxPOdl, (DateTime)item.dtEnd); } } } // salvo in last il valore di act... redisMan.setRSV(redKeyLogfileLast, 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 } }