From 22d1e26712ddb31e42a99bb2acf462ee07cd8331 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Mon, 9 May 2022 11:18:27 +0200 Subject: [PATCH] Bozza x aggiunta OPC-UA Omron di ICOEL --- IOB-UT-NEXT/Enums.cs | 5 + IOB-WIN-NEXT/AdapterForm.cs | 5 + IOB-WIN-NEXT/IOB-WIN-NEXT.csproj | 1 + IOB-WIN-NEXT/IobOpcUaOmron.cs | 369 +++++++++++++++++++++++++++++++ 4 files changed, 380 insertions(+) create mode 100644 IOB-WIN-NEXT/IobOpcUaOmron.cs diff --git a/IOB-UT-NEXT/Enums.cs b/IOB-UT-NEXT/Enums.cs index b5c20475..e2572b71 100644 --- a/IOB-UT-NEXT/Enums.cs +++ b/IOB-UT-NEXT/Enums.cs @@ -355,6 +355,11 @@ namespace IOB_UT_NEXT /// OpcUaEwonMecolpress, + /// + /// Adapter OMRON (es ICOEL) + /// + OpcUaOmron, + /// /// Adapter OSAI CNDEX (Cndex) /// diff --git a/IOB-WIN-NEXT/AdapterForm.cs b/IOB-WIN-NEXT/AdapterForm.cs index 34e4d513..501ef25f 100644 --- a/IOB-WIN-NEXT/AdapterForm.cs +++ b/IOB-WIN-NEXT/AdapterForm.cs @@ -1271,6 +1271,11 @@ namespace IOB_WIN_NEXT start.Enabled = true; break; + case tipoAdapter.OpcUaOmron: + iobObj = new IobOpcUaOmron(this, IOBConf); + start.Enabled = true; + break; + case tipoAdapter.PingWatchdog: iobObj = new IobPing(this, IOBConf); start.Enabled = true; diff --git a/IOB-WIN-NEXT/IOB-WIN-NEXT.csproj b/IOB-WIN-NEXT/IOB-WIN-NEXT.csproj index 1a148125..1d25aed5 100644 --- a/IOB-WIN-NEXT/IOB-WIN-NEXT.csproj +++ b/IOB-WIN-NEXT/IOB-WIN-NEXT.csproj @@ -184,6 +184,7 @@ + diff --git a/IOB-WIN-NEXT/IobOpcUaOmron.cs b/IOB-WIN-NEXT/IobOpcUaOmron.cs new file mode 100644 index 00000000..4ab8cadc --- /dev/null +++ b/IOB-WIN-NEXT/IobOpcUaOmron.cs @@ -0,0 +1,369 @@ +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 + } +} \ No newline at end of file