using EgwProxy.SqlDb.Controllers; using EgwProxy.SqlDb.DbModels; using IOB_UT_NEXT; using IOB_UT_NEXT.Config; using IOB_UT_NEXT.Objects; using IOB_UT_NEXT.Services.Files; using IOB_UT_NEXT.Services.Networking; using MapoSDK; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.NetworkInformation; using System.Threading.Tasks; using static IOB_UT_NEXT.CustomObj; namespace IOB_WIN_SQL.IobSql { /// /// Adapter specializzato per PAMA e le chiamate tramite DB per i dati /// - PODL (ordini MES --> MACCHINA) /// - SignLog (da stato macchina) /// - FluxLog (da stato macchina) /// - ProdLog (da stato Prod) /// public class SqlServPama : Iob.GenericNext { #region Public Constructors /// /// Costruttore dell'IOB DB PAMA /// /// Form chiamante /// Configurazione (legacy) /// Configurazione (v 4.x) public SqlServPama(AdapterFormNext caller, IobConfTree IobConfFull) : base(caller, IobConfFull) { string SyncStateServ = getOptPar("SyncStateServer"); string SyncStateDb = getOptPar("SyncStateDb"); string SyncStateUser = getOptPar("SyncStateUser"); string SyncStatePwd = getOptPar("SyncStatePwd"); string SyncStateCTout = getOptPar("SyncStateCTout"); string VetoReadSec = getOptPar("VetoReadSec"); string VetoSyncSec = getOptPar("VetoSyncSec"); string sFullPodlUrl = getOptPar("FullPodlUrl"); string connSyncState = $"data source={SyncStateServ};initial catalog={SyncStateDb};persist security info=True;user id={SyncStateUser};password={SyncStatePwd};MultipleActiveResultSets=True;App=IOB-WIN-NEXT"; // gestione command timeout da https://erikej.github.io/sqlclient/2020/10/26/sqlclient-commandtimeout-preview.html if (!string.IsNullOrEmpty(SyncStateCTout)) { connSyncState = $"{connSyncState};Command Timeout={SyncStateCTout}"; } if (!string.IsNullOrEmpty(VetoReadSec)) { int.TryParse(VetoReadSec, out vetoReadDbSec); } if (!string.IsNullOrEmpty(VetoSyncSec)) { int.TryParse(VetoSyncSec, out VetoSyncDbSec); } if (!string.IsNullOrEmpty(sFullPodlUrl)) { bool.TryParse(sFullPodlUrl, out FullPodlUrl); } // eccezione: NON TROVA EntityFramework 6.0.0 o successivo... why?!? dbProxy = new DbController(connSyncState); 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; // unico task ammissibile: fare un SYNC forzato... Dictionary taskDone = new Dictionary(); if (task2exe != null) { // controllo se memMap != null... if (memMap != null) { bool taskOk = false; string taskVal = ""; // cerco task specifici: se ho startSetup --> imposto bit DBB701.DBB0.4 foreach (var item in task2exe) { taskOk = false; taskVal = ""; // converto richiesta in enum... taskType tName = taskType.nihil; Enum.TryParse(item.Key, out tName); // controllo sulla KEY... switch (tName) { case taskType.setComm: case taskType.syncDbData: lgInfo($"executeTasks --> {tName}"); // per prima cosa recupero elenco PODL attivi x la macchina List listaPODL = new List(); bool okPodl = false; string rawListPODL = ""; try { // PONTE SYNC/ASYNC: Ora sw.Stop() aspetterà la fine reale dell'operazione Task.Run(async () => { rawListPODL = await HttpService.CallUrlAsync(urlGetNextPODL); }) .GetAwaiter() .GetResult(); } catch (Exception ex) { lgError($"Errore in callUrl per {urlGetNextPODL}: {ex.Message}"); } if (!string.IsNullOrEmpty(rawListPODL)) { try { listaPODL = JsonConvert.DeserializeObject>(rawListPODL); okPodl = listaPODL.Count > 0; } catch (Exception exc) { lg.Error($"Errore: chiamata elenco PODL ha restituito errore{Environment.NewLine}{exc}"); } } else { lg.Error($"Errore: chiamata elenco PODL ({urlGetNextPODL}) ha restituito valore vuoto"); } if (okPodl) { //mando elenco PODL... List CurrPodlReq = listaPODL .Select(x => new MesPODLReqModel() { Attivabile = x.Attivabile, CodArticolo = x.CodArticolo, CodCli = x.CodCli, CodGruppo = x.CodGruppo, DueDate = x.DueDate ?? DateTime.Now, IdxMacchina = x.IdxMacchina, IdxODL = x.IdxOdl, IdxPromessa = x.IdxPromessa, InsertDate = x.InsertDate, KeyBCode = x.KeyBCode, KeyRichiesta = x.KeyRichiesta, Note = x.Note, NumPezzi = x.NumPezzi, Priorita = x.Priorita, PzPallet = x.PzPallet, TCAssegnato = x.Tcassegnato }) .ToList(); // scrivo i record richiesti bool fatto = dbProxy.MesPodlWriteReq(CurrPodlReq); if (fatto) { taskVal = $"EXECUTED task: {item.Key} / {item.Value}"; // registro evento chiamata scrittura in syncState... string tabName = item.Key; var currSyncState = elencoSyncState.FirstOrDefault(x => x.TableName == tabName); // verifico x registrare azione if (currSyncState == null) { currSyncState = new SyncStateModel() { LastIdx = 0, LastUpdate = adesso, TableName = tabName, Note = "Init" }; } else { var lastRec = CurrPodlReq.OrderBy(x => x.IdxPromessa).LastOrDefault(); currSyncState.LastIdx = lastRec != null ? lastRec.IdxPromessa : -1; currSyncState.Note = $"Esecuzione {tabName} per {CurrPodlReq.Count} rec | last code: {currSyncState.LastIdx}"; currSyncState.LastUpdate = adesso; } // salvo sync... dbProxy.SyncStateUpsert(currSyncState); } // effettua sync scrivendo i dati in export (PODL) execExportAll(); } break; default: taskVal = $"IobSqlServPama | taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC"; lgInfo($"IobSqlServPama | chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}"); break; } // aggiungo task! taskDone.Add(item.Key, taskVal); } } } 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 processing CUSTOM: /// - esegue tutti gli import /// public override void processCustomTaskLF() { lgInfo($"Richiesto processCustomTaskLF"); // effettua sync execImportAll(); DtHelp.lastReadPLC = DateTime.Now; } /// /// 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(vetoReadDbSec); if (adesso > DtHelp.vetoDataSync) { // effettua sync execImportAll(); DtHelp.vetoDataSync = adesso.AddSeconds(VetoSyncDbSec); } // semaforo currDispData.semIn = Semaforo.SV; // recupero syncState elencoSyncState = dbProxy.SyncStateGetAll(); // verifico SignLog e processo 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("SqlDb PAMA: 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; if (dbProxy != null) { elencoSyncState = dbProxy.SyncStateDoImportAll(); if (elencoSyncState != null && elencoSyncState.Count > 0) { parentForm.commPlcActive = false; connectionOk = true; } } // refresh stato connessione!!! if (connectionOk) { queueInEnabCurr = true; if (adpRunning) { lgInfo("Connessione OK"); DtHelp.lastReadPLC = DateTime.Now; } } else { lgError("Impossibile procedere, connessione mancante..."); } } catch (Exception exc) { lgFatal($"Errore nella connessione all'adapter SqlDb PAMA: {szStatusConnection}{Environment.NewLine}{exc}"); connectionOk = false; lgInfo($"Eccezione in TryConnect, Adapter SqlDb PAMA NON running, pausa di {utils.CRI("waitRecMSec")} msec prima di ulteriori tentativi di riconnessione"); } } else { // loggo no risposta ping ... connectionOk = false; if (verboseLog || periodicLog) { lgInfo($"Attenzione: SqlDb PAMA 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 FullPodlUrl = false; protected int vetoReadDbSec = 3; protected int VetoSyncDbSec = 20; #endregion Protected Fields #region Protected Properties protected DbController dbProxy { get; set; } = null; /// /// Stato di sync delle tab gestite /// protected List elencoSyncState { get; set; } = new List(); #endregion Protected Properties #region Private Methods /// /// Esegue task EXPORT (MES PODL to MACHINE) /// private void execExportAll() { Stopwatch sw = new Stopwatch(); sw.Start(); elencoSyncState = dbProxy.SyncStateDoExportAll(); sw.Stop(); DateTime adesso = DateTime.Now; DtHelp.lastReadPLC = adesso; lgInfo($"DB: esecuzione task dbProxy.SyncStateDoExportAll() in {sw.ElapsedMilliseconds} ms"); if (elencoSyncState != null) { // registro evento chiamata scrittura in syncState... string tabName = "ExportAll"; var currSyncState = elencoSyncState.FirstOrDefault(x => x.TableName == tabName); // verifico x registrare azione if (currSyncState == null) { currSyncState = new SyncStateModel() { LastIdx = 0, LastUpdate = adesso, TableName = tabName, Note = "Init" }; } else { // calcolo lastIDX valore da datetime int valInt = -1; var nowString = $"{adesso:HHmmss}"; int.TryParse(nowString, out valInt); currSyncState.LastIdx = valInt; currSyncState.Note = $"Esecuzione {tabName} | DT code: {currSyncState.LastIdx}"; currSyncState.LastUpdate = adesso; } // salvo sync... dbProxy.SyncStateUpsert(currSyncState); foreach (var item in elencoSyncState) { lgTrace($"TAB {item.TableName} | LastIdx {item.LastIdx} | Note {item.Note} | Last Upd {item.LastUpdate}"); } } } /// /// Esegue task IMPORT (MES PODL to MACHINE) /// private void execImportAll() { Stopwatch sw = new Stopwatch(); sw.Start(); elencoSyncState = dbProxy.SyncStateDoImportAll(); sw.Stop(); DtHelp.lastReadPLC = DateTime.Now; lgInfo($"DB: esecuzione task dbProxy.SyncStateDoImportAll() in {sw.ElapsedMilliseconds} ms"); if (elencoSyncState != null) { foreach (var item in elencoSyncState) { lgTrace($"TAB {item.TableName} | LastIdx {item.LastIdx} | Note {item.Note} | Last Upd {item.LastUpdate}"); } } } /// /// Esegue processing + invio dati tab SignLog /// /// /// private bool processFluxLogTable(DateTime adesso) { bool fatto = false; string tabNameIn = "FluxLog"; string tabNameOut = "FluxLogToMes"; // cerco info sui FluxLog (ToMes e letti) var currFluxLogRead = elencoSyncState.FirstOrDefault(x => x.TableName == tabNameIn); // se nullo inizializzo if (currFluxLogRead == null) { currFluxLogRead = new SyncStateModel() { LastIdx = 0, LastUpdate = adesso, TableName = tabNameIn, Note = "Init" }; } var currFluxLogSent = elencoSyncState.FirstOrDefault(x => x.TableName == tabNameOut); // se nullo inizializzo if (currFluxLogSent == null) { currFluxLogSent = new SyncStateModel() { LastIdx = 0, LastUpdate = adesso, TableName = tabNameOut, Note = "Init" }; } // verifica se ci siano dati da trasmettere (sui valori LastIdx) if (currFluxLogRead.LastIdx > currFluxLogSent.LastIdx) { // recupero i dati dal DB... var data2send = dbProxy.MachFluxLogGetNew(currFluxLogSent.LastIdx); // se ho dati preparo invio if (data2send != null && data2send.Count > 0) { string sVal = ""; foreach (var fLog2send in data2send) { sVal = $"[DYNDATA] |{fLog2send.DtEvento:yyyy-MM-dd HH:mm:ss}|{fLog2send.CodFlux}|{fLog2send.Valore}"; // chiamo accodamento con dataora corretta... bool sent = accodaFLog(fLog2send.CodFlux, sVal, qEncodeFLog(fLog2send.DtEvento, fLog2send.CodFlux, fLog2send.Valore)); if (sent) { // traccio valore DynData x analisi trackDynData(fLog2send.CodFlux, fLog2send.Valore); } } fatto = true; } // aggiorno idx inviato... currFluxLogSent.Note = $"Updated from {currFluxLogRead.LastIdx}"; currFluxLogSent.LastUpdate = adesso; currFluxLogSent.LastIdx = currFluxLogRead.LastIdx; } // alla fine aggiorno i dati inviati! dbProxy.SyncStateUpsert(currFluxLogSent); return fatto; } /// /// Esegue processing + invio dati tab ProdData /// /// /// private bool processProdDataTable(DateTime adesso) { bool fatto = 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) { if (sLog2send.Action == "StartOrd") { try { // PONTE SYNC/ASYNC: Ora sw.Stop() aspetterà la fine reale dell'operazione Task.Run(async () => { if (FullPodlUrl) { // se il record è apertura PODL chiamo apertura await TrySetupPODLAsync(sLog2send.IdxPodl); //TrySetupPODLAsync(sLog2send.IdxPodl, true, sLog2send.DtEve, DateTime.Now); } else { await TrySetupPODLAsync(0); } }) .GetAwaiter() .GetResult(); } catch (Exception ex) { lgError($"Errore in tryClosePODL | {Environment.NewLine}{ex.Message}"); } } else if (sLog2send.Action == "EndOrd") { // se il record è chiusura PODL chiamo chiusura... DOPO aver cercato // PODL --> ODL if (FullPodlUrl) { TryClosePODL(sLog2send.IdxPodl); //tryClosePODL(sLog2send.IdxPodl, sLog2send.DtEve, DateTime.Now); } else { TryClosePODL(0); } } } } // aggiorno idx inviato... currSignLogSent.Note = $"Updated from {currSignLogRead.LastIdx}"; currSignLogSent.LastUpdate = adesso; currSignLogSent.LastIdx = currSignLogRead.LastIdx; #if false var lastRec = data2send.LastOrDefault(); if (lastRec != null) { // salvo in B_input ultimo valore letto... B_input = lastRec.ValInt; } #endif fatto = true; } // alla fine aggiorno i dati inviati! dbProxy.SyncStateUpsert(currSignLogSent); return fatto; } /// /// Esegue processing + invio dati tab SignLog /// /// /// private bool processSignLogTable(DateTime adesso) { bool fatto = false; string tabNameIn = "SignLog"; string tabNameOut = "SignLogToMes"; // 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... List data2send = dbProxy.MachSigLogGetNew(currSignLogSent.LastIdx); // se ho dati preparo invio if (data2send != null && data2send.Count > 0) { foreach (var sLog2send in data2send) { string currVal = getEncodSigLog(sLog2send.DtEve, sLog2send.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(); } } } // aggiorno idx inviato... currSignLogSent.Note = $"Updated from {currSignLogRead.LastIdx}"; currSignLogSent.LastUpdate = adesso; currSignLogSent.LastIdx = currSignLogRead.LastIdx; var lastRec = data2send.LastOrDefault(); if (lastRec != null) { // salvo in B_input ultimo valore letto... B_input = lastRec.ValInt; } fatto = true; } // alla fine aggiorno i dati inviati! dbProxy.SyncStateUpsert(currSignLogSent); return fatto; } #endregion Private Methods } }