using EgwProxy.Icoel; using EgwProxy.Icoel.SizerService; using IOB_UT_NEXT; using MapoSDK; using System; using System.Collections.Generic; using System.Net.NetworkInformation; namespace IOB_WIN_NEXT.IobNet { /// /// Adapter specializzato per ICOEL e le chiamate tramite WS Soap al Sizer, con libreria EgwProxy.Icoel /// public class IcoelSoap : Iob.Generic { #region Public Constructors /// /// Costruttore dell'IOB Icoel SOAP /// /// AdapterForm chiamante /// Configurazione IOB per avvio public IcoelSoap(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf) { /* -------------------------------------- * todo's * -------------------------------------- * - init obj comunicazione da conf e nuget * - test comunicazione * - estensione IOB come OPT_PAR di OVERRIDE (x inviare dati di un unico iOB da più IOB programs) * - gestione processCustomTaskLF * - x lettura dei 2 batch correnti (sx/dx) * - calcolo batch in corso/chiusi da date inizio/fine * - trasmettere a MP/IO risultato valutazioni * - gestione executeTasks * - task di invio batch configurato in coda * - task di recupero info anagrafiche (grower, variety, layout,...) * - contapezzi (SE ha senso con sizer oppure saltare) */ IcoelSizer = new Connector(IOBConf.cncIpAddr, IOBConf.cncPort); lastPING = DateTime.Now.AddHours(-1); } #endregion Public Constructors #region Public Methods /// /// Implementazione custom esecuzione task specifici /// /// /// public override Dictionary executeTasks(Dictionary task2exe) { /*--------------------------------------- * fixme todo fare !!! * gestione execute task SPECIFICI x il sizer: * - recupero anagrafice variety/layout (attivi) * - recupero grower * - invio batch da accodare * - recupero dati da sizer (OVE disponibili) * - recupero batch corrente (modalità force/resync?) * *---------------------------------------*/ // Verificare il protocollo: dovrebbe togliere SOLO i task eseguiti... 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.setSupplier: string cmdArgs = item.Value; taskVal = executeSetSupplier(cmdArgs); break; case taskType.setParameter: // richiedo da URL i parametri WRITE da popolare lgInfo("Chiamata setParameter --> processMemWriteRequests"); taskVal = processMemWriteRequests(); // se restituiscce "" faccio altra prova... if (string.IsNullOrEmpty(taskVal)) { // i parametri me li aspetto come stringa composta paramName|paramvalue if (item.Value.Contains("|")) { string[] paramsJob = item.Value.Split('|'); taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}"; } else { taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {item.Value} doesnt contain pipe for splitting key/value"; } } break; default: taskVal = $"taskReq: {tName} | key: {item.Key} | val: {item.Value} | SKIPPED | NO EXEC"; lgInfo($"Chiamata senza processing: taskOk: {taskOk} | taskVal: {taskVal}"); break; } // aggiungo task! taskDone.Add(item.Key, taskVal); } } else { lgError($"Attenzione! memMap è nullo, non posso eseguire task2exe!"); } } return taskDone; } /// /// Recupero dati dinamici... /// public override Dictionary getDynData() { // valore non presente in vers default... se gestito fare override Dictionary outVal = new Dictionary(); // recupero da libreria il dizionario dei performance parameters... perfParamDict = IcoelSizer.GetPerfMeters(); lgTrace("SOAP: effettuata chiamata IcoelSizer.GetPerfMeters()"); foreach (var item in perfParamDict) { saveValue(ref outVal, item.Key, item.Value); } // ora leggo i batch data batchDataDict = IcoelSizer.GetCurrBatchData(); lgTrace("SOAP: effettuata chiamata IcoelSizer.GetCurrBatchData()"); foreach (var item in batchDataDict) { saveValueString(ref outVal, item.Key, item.Value); } lastReadPLC = DateTime.Now; return outVal; } /// /// Effettua processing CUSTOM x Icoel: /// - recupera elenco batch delle 2 linee /// - invia al sistema /// public override void processCustomTaskLF() { lgInfo($"Richiesto processCustomTaskLF"); var currBatch = IcoelSizer.GetCurrentBatch(); lgTrace("SOAP: effettuata chiamata IcoelSizer.GetCurrentBatch()"); if (currBatch != null) { // verifico se i batch siano variati... e quindi da inviare... bool doSend = (currBatchList == null || currBatchList.Count == 0); if (currBatch.Count > 0 && !doSend) { foreach (var item in currBatch) { // se variato ID è cambiato doSend = doSend || (currBatchList[item.Key].Id != item.Value.Id); } } // se devo inviare impacchetto dati if (doSend) { accodaRawData(rawTransfType.IcoelBatch, currBatch); currBatchList = currBatch; } } lastReadPLC = DateTime.Now; } /// /// Effettua lettura semafori principale Parametri da /// aggiornare x display in form /// public override void readSemafori(ref newDisplayData currDispData) { if (connectionOk) { B_input = 1; currDispData.semIn = Semaforo.SV; if (currBatchList == null) { // se nullo --> provo a leggere! var currBatch = IcoelSizer.GetCurrentBatch(); lgTrace("SOAP: effettuata chiamata IcoelSizer.GetCurrentBatch()"); if (currBatch != null) { currBatchList = currBatch; accodaRawData(rawTransfType.IcoelBatch, currBatch); } } else { //se i perf parameters mi danno valire > 0 x quantità frutta... if (perfParamDict != null && perfParamDict.Count > 0) { if (perfParamDict.ContainsKey("VelFruttiMinuto")) { if (perfParamDict["VelFruttiMinuto"] > 0) { B_input += (1 << 1); } // metto manuale else { B_input += (1 << 4); } } } //usato altro criterio x stato verde da vel frutti minuto #if false // se ho batch NON chiusi (data = minValue) allora lavora if (currBatchList[1].EndTime == DateTime.MinValue || currBatchList[1].EndTime == DateTime.MinValue) { B_input = 3; } #endif } // accodo NON emergenza B_input += (1 << 7); } 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("IcoelSoap: 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; // recupero elenco batch... se != vuoto -_> connesso! var batchList = IcoelSizer.GetCurrentBatch(); lgTrace("SOAP: effettuata chiamata IcoelSizer.GetCurrentBatch()"); if (batchList != null && batchList.Count > 0) { lgInfo($"szStatusConnection IcoelSoap, recuperato elenco di {batchList.Count} batch"); parentForm.commPlcActive = false; connectionOk = true; } // refresh stato connessione!!! if (connectionOk) { queueInEnabCurr = true; if (adpRunning) { lgInfo("Connessione OK"); } } else { lgError("Impossibile procedere, connessione mancante..."); } } catch (Exception exc) { lgFatal($"Errore nella connessione all'adapter IcoelSoap: {szStatusConnection}{Environment.NewLine}{exc}"); connectionOk = false; lgInfo($"Eccezione in TryConnect, Adapter IcoelSoap 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: IcoelSoap controllo PING fallito per IP {cIobConf.cncPingAddr}"); } } } } else { needRefresh = true; } } public override void tryDisconnect() { // registro solo che è disconnesso connectionOk = false; queueInEnabCurr = false; } #endregion Public Methods #region Protected Properties /// /// Dizionario dati Batch correnti /// protected Dictionary batchDataDict { get; set; } = new Dictionary(); /// /// elenco dei BAtch correntemente caricati x testing variazione /// protected Dictionary currBatchList { get; set; } protected Connector IcoelSizer { get; set; } /// /// Dizionario parametri correnti /// protected Dictionary perfParamDict { get; set; } = new Dictionary(); #endregion Protected Properties #region Protected Methods /// /// Metodo da overridare x scrivere DAVVERO i parametri sul PLC /// /// protected override void plcWriteParams(ref List updatedPar) { foreach (var item in updatedPar) { // cerco se sia setSupplier if (item.uid == "setSupplier") { string reqVal = !string.IsNullOrEmpty(item.reqValue) ? item.reqValue : item.value; // chiamo var taskVal = executeSetSupplier(reqVal); bool fatto = taskVal.StartsWith("REQUEST"); // se fatto --> aggiorno! if (fatto) { //item.value = item.reqValue; item.reqValue = ""; item.lastRead = DateTime.Now; item.UM = ""; } } } } /// /// recupera le variety ed i rispettivi layout e li invia al sistema MP/IO /// protected void refreshVarietyData() { ///determina se recuperare SOLO varietà attive o tutte bool soloAttive = false; var varList = IcoelSizer.GetVarietyList(soloAttive); lgTrace("SOAP: effettuata chiamata IcoelSizer.GetVarietyList()"); if (varList != null && varList.Length > 0) { var varietyData = IcoelSizer.GetLayoutForVarietyList(varList); lgTrace("SOAP: effettuata chiamata IcoelSizer.GetLayoutForVarietyList()"); } lastReadPLC = DateTime.Now; } #endregion Protected Methods #region Private Methods private string executeSetSupplier(string cmdArgs) { string taskVal; // loggo i parametri ricevuti da popolare lgInfo($"Chiamata setSupplier --> params payload: {cmdArgs}"); // recupero batch corrente x preselezionare i dati guid layout/varietà... var currBatch = IcoelSizer.GetCurrentBatch(); // recupero GUID x variety e layout di default var varGuid = currBatch[1].VarietyId; var layGuid = currBatch[1].LayoutId; // predispongo grower di default GrowerInfo GrowerData = new GrowerInfo(); // le info sono una stringa separata da valore | di GrowerCode|GrowerName|varietyGuid|LayoutGuid; string[] reqParams = cmdArgs.Split('|'); // se ho dati sistemo grower... if (reqParams.Length >= 2) { //GrowerData.GrowerCode = reqParams[0]; //GrowerData.GrowerName = reqParams[1]; // sistemo commenti var comments = new List(); comments.Add($"sent {DateTime.Now: yyyy.MM.dd HH:mm:ss}"); comments.Add("Qty: no set"); comments.Add("------------"); //GrowerData.Comments = comments; GrowerData = new GrowerInfo() { GrowerCode = reqParams[0], GrowerName = reqParams[1], Comments = comments }; lgInfo($"Set grower data | GrowerCode: {GrowerData.GrowerCode} | GrowerName: {GrowerData.GrowerName}"); } if (reqParams.Length >= 3) { Guid.TryParse(reqParams[2], out varGuid); lgInfo($"Set Variety data | VarGUID: {varGuid}"); } if (reqParams.Length >= 4) { Guid.TryParse(reqParams[3], out layGuid); lgInfo($"Set Layout data | LayGUID: {layGuid}"); } // invio richiesta!!! IcoelSizer.EnqueueBatch(GrowerData, varGuid, layGuid); lgInfo($"Request sent!"); taskVal = $"REQUEST SET SUPPLIER | EnqueueBatch | GrowCode: {GrowerData.GrowerCode} | GrowName: {GrowerData.GrowerName} | Var: {varGuid} | Lay: {layGuid} "; // se restituiscce "" faccio altra prova... if (string.IsNullOrEmpty(taskVal)) { // i parametri me li aspetto come stringa composta paramName|paramvalue if (cmdArgs.Contains("|")) { string[] paramsJob = cmdArgs.Split('|'); taskVal = $"REQUEST SET PARAMETERS: {paramsJob[0]} --> {paramsJob[1]}"; } else { taskVal = $"WRONG REQUEST FOR SET PARAMETERS: {cmdArgs} doesnt contain pipe for splitting key/value"; } } return taskVal; } #endregion Private Methods } }