using MapoSDK; using Newtonsoft.Json; using Opc.Ua; using System; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; using System.Text; using System.Threading.Tasks; namespace IOB_WIN_NEXT { public class IobOpcUaOmron : IobOpcUa { #region Protected Fields /// /// Modalità cambio ODL /// protected string CHANGE_ODL_MODE = ""; protected bool testDone = false; #endregion Protected Fields #region Public Constructors /// /// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation con la gestione specifica per Omron (es ICOEL) /// https://github.com/OPCFoundation/UA-.NETStandard /// /// /// public IobOpcUaOmron(AdapterForm caller, IobConfiguration IOBConf) : base(caller, IOBConf) { // inizializzo classe base... if (!string.IsNullOrEmpty(getOptPar("CHANGE_ODL_MODE"))) { CHANGE_ODL_MODE = getOptPar("CHANGE_ODL_MODE"); } sendKeyRichiesta = true; } #endregion Public Constructors #region Protected Methods /// /// Effettua decodifica aree memoria alla bitmap usata x MAPO /// protected override void decodeToBaseBitmap() { DateTime adesso = DateTime.Now; // init a zero... B_input = 0; /* ----------------------------------------------------- * STATE MACHINE 60 STD / SIMULA *------------------------------------------------------ * bitmap MAPO * B0: POWER_ON * B1: RUN * B2: pzCount * B3: allarme * B4: manuale * B5: SlowTC (NON gestito qui) * B6: warm-up / cool-down / setup * B7: emergenza ARMATA (1=ok, 0 = premuta) ---------------------------------------------------- */ // se valido il check ping lo eseguo... altrimenti lo do x buono bool checkPing = !opcUaParams.pingAsPowerOn; string currRun = "N.A."; if (!checkPing) { checkPing = (testPingMachine == IPStatus.Success); } // bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"... bool powerOnOk = checkPing && hasPowerOn; // procedo SOLO SE mi da ping OK... if (checkPing) { B_input = powerOnOk ? 1 : 0; // variabili RUN... if (!string.IsNullOrEmpty(opcUaParams.keyRunMode)) { currRun = getDataItemValue(opcUaParams.keyRunMode); } // salvo running come = working... isRunning = isWorking; // se ho emergenza premuta --> emergenza! if (hasEStopArmed) { B_input += (1 << 7); } // se ho emergenza premuta --> emergenza! if (isWarmUpCoolDown) { B_input += (1 << 6); } // se ho almeno 1 allarme E NON SONO IN AUTO --> ALARM! if (hasError) { B_input += (1 << 3); } if (isWorking) { // RUN = LAVORA! B_input += (1 << 1); } else if (powerOnOk && (!isReady || isManual)) { // se NON ready --> manual B_input += (1 << 4); } } // se abilitato watchdog... if (opcUaParams.WatchDog.IsEnabled) { if (adesso.Subtract(lastWatchDogPLC).TotalSeconds > 2) { lastWatchDogPLC = adesso; WatchDog++; WatchDog = WatchDog > opcUaParams.WatchDog.MaxVal ? 0 : WatchDog; lgTrace($"WatchDog val: {WatchDog}"); try { WriteValue commWriteVal = new WriteValue(); commWriteVal.NodeId = new NodeId(opcUaParams.WatchDog.MemConfWrite); commWriteVal.AttributeId = Attributes.Value; commWriteVal.Value = new DataValue(); commWriteVal.Value.Value = WatchDog; List nodes2Write = new List(); nodes2Write.Add(commWriteVal); UA_ref.WriteNodes(nodes2Write); } catch (Exception exc) { lgError($"Eccezione in gestione WatchDog, valore attuale {WatchDog}{Environment.NewLine}{exc}"); } } } else { lgTrace("WatchDog disabilitato"); } // controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2) { tryDisconnect(); } // solo se non ho veto check int vFactor = 2; if (vetoCheckStatus < adesso) { lgDebug($"Stato variabili checkPing: {testPingMachine}"); // imposto veto per vetoSeconds... vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor); } // log opzionale! if (verboseLog) { lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}"); } } /// /// Effettua vera scrittura parametri /// /// protected override void plcWriteParams(ref List updatedPar) { dataConf currMem = null; int byteSize = 0; string memAddrWrite = ""; string serObj = ""; if (updatedPar != null) { List nodes2Write = new List(); // controllo i parametri... ne gestisco 4... foreach (var item in updatedPar) { try { memAddrWrite = ""; int valInt = 0; double valReal = 0; // cerco in area memMapWrite... if (memMap.mMapWrite.ContainsKey(item.uid)) { // recupero! currMem = memMap.mMapWrite[item.uid]; byteSize = currMem.size; memAddrWrite = currMem.memAddr; WriteValue commWriteVal = new WriteValue(); commWriteVal.NodeId = new NodeId(currMem.memAddr); commWriteVal.AttributeId = Attributes.Value; commWriteVal.Value = new DataValue(); commWriteVal.Value.Value = item.reqValue; // faccio preliminarmente upsertKey... upsertKey(currMem.name, currMem.value); serObj = JsonConvert.SerializeObject(item); lgInfo($"Inizio processing plcWriteParams per {currMem.name} | valore richiesto {currMem.value}"); lgInfo($"---------------{Environment.NewLine}UPDATED PARAM:{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); serObj = JsonConvert.SerializeObject(currMem); lgInfo($"---------------{Environment.NewLine}MEMORY CONTENT:{Environment.NewLine}{serObj}{Environment.NewLine}---------------"); switch (currMem.tipoMem) { case plcDataType.Boolean: break; case plcDataType.Int: case plcDataType.DInt: case plcDataType.Word: case plcDataType.DWord: int.TryParse(item.reqValue, out valInt); commWriteVal.Value.Value = valInt; break; case plcDataType.Real: double.TryParse(item.reqValue, out valReal); commWriteVal.Value.Value = valReal; break; case plcDataType.String: commWriteVal.Value.Value = item.reqValue; break; default: break; } lgInfo($"---------------{Environment.NewLine}OPC-UA data:{Environment.NewLine}NodeId: {commWriteVal.NodeId}{Environment.NewLine}Value: {commWriteVal.Value.Value}{Environment.NewLine}---------------"); if (!string.IsNullOrEmpty(memAddrWrite)) { nodes2Write.Add(commWriteVal); } else { lgInfo($"Errore: memAddrWrite vuoto!"); } } else { lgInfo($"Errore uid non trovato in area write memory: {item.uid}, ci sono {memMap.mMapWrite.Count} in area write"); } } catch (Exception exc) { lgError($"Eccezione in fase di plcWriteParams per item {item.uid} con valore {item.value}{Environment.NewLine}{exc}"); } } if (nodes2Write.Count > 0) { UA_ref.WriteNodes(nodes2Write); } } } #endregion Protected Methods #region Public Methods /// /// Processo i task richiesti e li elimino dalla coda 1:1 /// /// public override Dictionary executeTasks(Dictionary task2exe) { // uso metodo base x ora return base.executeTasks(task2exe); } /// /// Effettua vero processing contapezzi /// public override void processContapezzi() { if (utils.CRB("enableContapezzi")) { // check condizione validazione if (checkMultiCondition(opcUaParams.condCountEnabled) || opcUaParams.condCountEnabled.checkList.Count == 0) { // cerco parametro contapezzi... string currPzCount = getDataItemValue(opcUaParams.keyPartCount); // se ho un contapezzi... processo... if (!string.IsNullOrEmpty(currPzCount)) { int newVal = -1; bool fatto = Int32.TryParse(currPzCount, out newVal); if (fatto) { // gestione decremento contapezzi: viene "messo via" solo SE c'è un effettivo decremento contapezzi... if (newVal < contapezziPLC) { pzCountResetted = true; // incremento contatore richiesta countKeyRichiesta = countKeyRichiesta + 1; // log lgInfo("Contapezzi resettato (PLC) --> pzCountResetted = true"); } // salvo nuovo valore contapezziPLC contapezziPLC = newVal > -1 ? newVal : contapezziPLC; } else { lgError($"Errore in decodifica valore contapezzi, valore rilevato: {currPzCount}"); } } else { lgError("Errore in decodifica valore contapezzi, valore vuoto!"); } } if (CHANGE_ODL_MODE == "PZCOUNT_RESET") { // controllo comunque, se è ZERO il contapezzi, e sul server è maggiore il valore x ODL e NON abilitato il trigger reset --> abilito trigger... if (!pzCountResetted && contapezziPLC == 0 && contapezziIOB > 0) { pzCountResetted = true; // incremento contatore richiesta countKeyRichiesta = countKeyRichiesta + 1; // log lgInfo("Contapezzi resettato (PLC==0 e IOB>PLC) --> pzCountResetted = true"); } } } } /// /// Effettua reset del contapezzi, NON POSSIBILE in questa versione /// /// public override bool resetcontapezziPLC() { bool answ = false; return answ; } /// /// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE in questa versione /// /// public override bool setcontapezziPLC(int newPzCount) { bool answ = false; return answ; } #endregion Public Methods } }