From cfd95b00d305f7cd30e96d7d78079eda02391655 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Thu, 12 May 2022 17:08:07 +0200 Subject: [PATCH] COmpletata review Giacovelli's ICOEL OPC-UA --- CVCncLib/CVCncLib.dll | Bin 967680 -> 967680 bytes IOB-WIN-NEXT/DATA/CONF/GIACO_ICOEL_001.json | 6 +- IOB-WIN-NEXT/IobOpcUa.cs | 129 +++++++++++--------- IOB-WIN-NEXT/IobOpcUaOmronIcoel.cs | 127 ++++++++++++++++--- IOB-WIN-NEXT/Objects.cs | 5 + 5 files changed, 190 insertions(+), 77 deletions(-) diff --git a/CVCncLib/CVCncLib.dll b/CVCncLib/CVCncLib.dll index eb5b89860d1c1d54ea9ee292bc2583724e02c946..fd43c0852aaff56f31c5a32bf13764b954642ea6 100644 GIT binary patch delta 134 zcmZqZux{wEp3uR}zpAFOyS1CKwVSE6o4K`{rL~*2wVSQAo4vK0qqUo}wVSK8n|o_F zkL5yvCm%l5#44R~ni1=6l98MKq5aB29w6r3eq|xw9X=hPQArFyzy!oHj0_AX&hasv j*ucjS6zmdg--@6jqe|ITO?4wPC%uSvHi+I9w6r3eq|xw9X=hPQArFyzy!oHj0_AX&hasv j*ucjS6zmdgm&;c7|9G?K28PDBa|H_*Z@;sFPem92m`yUZ diff --git a/IOB-WIN-NEXT/DATA/CONF/GIACO_ICOEL_001.json b/IOB-WIN-NEXT/DATA/CONF/GIACO_ICOEL_001.json index 8caafeab..433ca0eb 100644 --- a/IOB-WIN-NEXT/DATA/CONF/GIACO_ICOEL_001.json +++ b/IOB-WIN-NEXT/DATA/CONF/GIACO_ICOEL_001.json @@ -2,10 +2,10 @@ "BrowseFullVal": "ns=4;s=NxController.GlobalVars", "BrowseNSIndex": 4, "BrowseValue": 5001, - "keyPartCount": "PartDone(0)", - "keyPartReq": "PartToDo(0)", + "keyPartCount": "", + "keyPartReq": "", "keyPartId": "", - "keyProgName": "PartName(0)", + "keyProgName": "", "keyRunMode": "", "pingAsPowerOn": true, "Identity": { diff --git a/IOB-WIN-NEXT/IobOpcUa.cs b/IOB-WIN-NEXT/IobOpcUa.cs index 44b6e334..9ea373e7 100644 --- a/IOB-WIN-NEXT/IobOpcUa.cs +++ b/IOB-WIN-NEXT/IobOpcUa.cs @@ -252,7 +252,7 @@ namespace IOB_WIN_NEXT /// /// /// - private void checkAndSend(Opc.Ua.Client.MonitoredItem MonIt, string NotifyValue, bool forceSend) + internal void checkAndSend(Opc.Ua.Client.MonitoredItem MonIt, string NotifyValue, bool forceSend) { if (MonIt != null) { @@ -296,6 +296,64 @@ namespace IOB_WIN_NEXT } } + /// + /// Verifica ed invia variazioni DAL FORMATO RAW data (byte[]) + /// + /// + /// + /// + internal virtual void checkAndSendRaw(Opc.Ua.Client.MonitoredItem MonIt, byte[] NotifyValue, bool forceSend) + { + if (MonIt != null) + { + if (NotifyValue != null && NotifyValue.Length > 0) + { + // versione base: il valore è la stringa composta da TUTTI i valori in BYTE espressi come comma-sep-string + StringBuilder sb = new StringBuilder(); + foreach (var bVal in NotifyValue) + { + sb.Append($"{bVal},"); + } + string currVal = sb.ToString(); + + string sVal = ""; + string descr = ""; + DateTime locTStamp = DateTime.Now; + descr = itemTranslation("OPC", MonIt.DisplayName); + sVal = $"Change: {locTStamp.ToString()} | descr: {descr} | Id: {MonIt.StartNodeId} | Val: {currVal}"; + lgInfo(sVal); + + // verifico se salvare + bool changed = checkSaveValue(MonIt, currVal); + // cerco se non sia un dato filtrato in FLUXLOG... + bool isFiltered = opcUaParams.fluxLogVeto.Contains(MonIt.DisplayName); + if (isFiltered) + { + lgTrace($"NON ACCODATO sample per {MonIt.DisplayName} - trovato VETO in fluxLogVeto", false); + } + else + { + if (changed || forceSend) + { + accodaFLog(sVal, qEncodeFLog(descr, $"{NotifyValue}")); + } + else + { + lgTrace($"NON ACCODATO sample per {MonIt.DisplayName} - verifica variazione ha dato esito negativo", false); + } + } + } + else + { + lgError($"checkAndSend ERROR | MonIt: {MonIt.DisplayName} | NotifyValue Null!!!"); + } + } + else + { + lgError("checkAndSend ERROR: MonIt null"); + } + } + /// /// Indica se si debba leggere un area di tipo "encoded" (byte raw --> obj) /// @@ -305,33 +363,6 @@ namespace IOB_WIN_NEXT /// protected byte[] byteRawData { get; set; } = new byte[1]; -#if false - /// - /// Restitusice una versione serializzata delle variabili complesse in formato string - /// - /// - /// - /// - protected virtual string bSerString(string dataType, byte[] rawBytes) - { - string answ = ""; - - return answ; - } - /// - /// Restitusice una versione serializzata delle variabili complesse in formato object (generico) - /// - /// - /// - /// - protected virtual object bSerObj(string dataType, byte[] rawBytes) - { - object answ = null; - - return answ; - } -#endif - /// /// Vera connessione ad OpcUa /// @@ -461,19 +492,14 @@ namespace IOB_WIN_NEXT if (rawVal != null) { byteRawData = getByteRaw((DataValue)rawVal); - StringBuilder sb = new StringBuilder(); - foreach (var bVal in byteRawData) - { - sb.Append($"{bVal},"); - } - currVal = sb.ToString(); + checkAndSendRaw(item, byteRawData, true); } } else { currVal = UA_ref.ReadNode(item.StartNodeId); + checkAndSend(item, currVal, true); } - checkAndSend(item, currVal, true); } } // gestione eventi change @@ -574,46 +600,35 @@ namespace IOB_WIN_NEXT utils.callUrlNow($"{urlSaveDataItems}", rawData); } - /// - /// Data ora ultima lettura RAW data (x lettura periodica forzata...) - /// - protected DateTime lastRawDataRead { get; set; } = DateTime.Now.AddMinutes(-1); - /// /// Periodo massimo (in sec) per letture dati RAW in mancanza di eventi /// - protected int rawDataReadMaxElapsed { get; set; } = 90; + protected int lastCurrentMaxElapsed { get; set; } = 120; /// /// Evento rilevazione modifica valori --> chiamo checkSend /// /// /// - private void UA_ref_eh_MonItChange(object sender, opcUaMonitItemChange e) + internal virtual void UA_ref_eh_MonItChange(object sender, opcUaMonitItemChange e) { string currVal = ""; // da verificare decodifica valore byte... if (doByteRead) { - // aggiorno ultima lettura - lastRawDataRead = DateTime.Now; var currNot = e.CurrNotify; if (currNot != null) { byteRawData = getByteRaw((DataValue)currNot.Value); - StringBuilder sb = new StringBuilder(); - foreach (var bVal in byteRawData) - { - sb.Append($"{bVal},"); - } - currVal = sb.ToString(); + checkAndSendRaw(e.CurrMonitoredItem, byteRawData, false); } } else { currVal = $"{e.CurrNotify.Value}"; + checkAndSend(e.CurrMonitoredItem, currVal, false); } - checkAndSend(e.CurrMonitoredItem, currVal, false); + // aggiorno ultima lettura lastCurrent = DateTime.Now; } @@ -706,7 +721,7 @@ namespace IOB_WIN_NEXT { if (!string.IsNullOrEmpty(NotifyValue)) { - lgTrace($"Richiesta checkSaveSample per {dataItem.DisplayName} | id: {dataItem.StartNodeId} | Valore: {NotifyValue}"); + lgTrace($"Richiesta checkSaveValue per {dataItem.DisplayName} | id: {dataItem.StartNodeId} | Valore: {NotifyValue}"); // verifico in memoria se ho l'oggetto condition ed il suo valore.. string uuid = $"{dataItem.DisplayName}"; DateTime adesso = DateTime.Now; @@ -757,7 +772,7 @@ namespace IOB_WIN_NEXT else { // registro non trovato da aggiungere... - lgInfo($"DataItem non trovato in checkSaveSample: {dataItem.DisplayName}"); + lgInfo($"DataItem non trovato in checkSaveValue: {dataItem.DisplayName}"); // provo a creare oggetto in memoria... try { @@ -836,8 +851,8 @@ namespace IOB_WIN_NEXT // bit 0 (poweron) imposto a 1 SE pingo + PowerOn=="ON"... bool powerOnOk = checkPing && hasPowerOn; - // controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto - if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2) + // controllo se sono poweroff e se non ho dati buoni da > lastCurrentMaxElapsed --> disconnetto + if (!powerOnOk && adesso.Subtract(lastCurrent).TotalSeconds > lastCurrentMaxElapsed) { tryDisconnect(); } @@ -1093,7 +1108,7 @@ namespace IOB_WIN_NEXT // verifico SE sia necessario forzare la lettura RAW if (doByteRead) { - if (DateTime.Now.Subtract(lastRawDataRead).TotalSeconds > rawDataReadMaxElapsed) + if (DateTime.Now.Subtract(lastCurrent).TotalSeconds > lastCurrentMaxElapsed) { // FIXME TODO !!! foreach (var item in dataItemMem) @@ -1103,7 +1118,7 @@ namespace IOB_WIN_NEXT if (rawVal != null) { byteRawData = getByteRaw((DataValue)rawVal); - lastRawDataRead = DateTime.Now; + lastCurrent = DateTime.Now; currReadErrors = 0; } else diff --git a/IOB-WIN-NEXT/IobOpcUaOmronIcoel.cs b/IOB-WIN-NEXT/IobOpcUaOmronIcoel.cs index cb93a84d..7fd8a32c 100644 --- a/IOB-WIN-NEXT/IobOpcUaOmronIcoel.cs +++ b/IOB-WIN-NEXT/IobOpcUaOmronIcoel.cs @@ -222,6 +222,9 @@ namespace IOB_WIN_NEXT #region Public Constructors + /// + /// Valore corrente dei dati ICOEL (traduzione JIT da byte[]) + /// protected DatiMesIcoel currData { get @@ -240,6 +243,16 @@ namespace IOB_WIN_NEXT } } + /// + /// Ultima versione validata delle info x confronto + /// + protected DatiMesIcoel lastData { get; set; } = new DatiMesIcoel(new byte[115]); + + internal override void UA_ref_eh_MonItChange(object sender, opcUaMonitItemChange e) + { + base.UA_ref_eh_MonItChange(sender, e); + } + /// /// 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 @@ -257,30 +270,110 @@ namespace IOB_WIN_NEXT doByteRead = true; lgInfo($"Avviato IobOpcUaOmronIcoel | encodeReadData: {doByteRead}"); } - - -#if false - protected override object bSerObj(string dataType, byte[] rawBytes) + /// + /// Verifico se salvare e inviare proprietà specificata + /// + /// + protected void testSendProperty(Opc.Ua.Client.MonitoredItem MonIt, string NotifyValue, bool forceSend) { - object answ = null; - // provo a deserializzare l'oggetto - try + DateTime locTStamp = DateTime.Now; + string sVal = ""; + string descr = ""; + descr = itemTranslation("OPC", MonIt.DisplayName); + sVal = $"Change: {locTStamp.ToString()} | descr: {descr} | Id: {MonIt.StartNodeId} | Val: {NotifyValue}"; + lgInfo($"TSP | {sVal}"); + + bool changed = checkSaveValue(MonIt, NotifyValue); + // cerco se non sia un dato filtrato in FLUXLOG... + bool isFiltered = opcUaParams.fluxLogVeto.Contains(MonIt.DisplayName); + if (isFiltered) { - if (rawBytes != null) + lgTrace($"TSP | NON ACCODATO sample per {MonIt.DisplayName} - trovato VETO in fluxLogVeto", false); + } + else + { + if (changed || forceSend) { - currData = new DatiMesIcoel(rawBytes); - answ = currData; + accodaFLog(sVal, qEncodeFLog(descr, $"{NotifyValue}")); } else { - answ = base.bSerObj(dataType, rawBytes); + lgTrace($"TSP | NON ACCODATO sample per {MonIt.DisplayName} - verifica variazione ha dato esito negativo", false); } } - catch - { } - return answ; - } -#endif + } + + /// + /// effettua verifica del datablock icoel invianod eventualmente i dati variati + /// + /// + /// + /// + /// + /// + /// + protected void testSendDataBlock(NodeId startNodeId, string blockName, MesItemStatus currBlock, MesItemStatus newBlock, bool forceSend) + { + // verifica globale blocchi old/new... + if (!currBlock.Equals(newBlock)) + { + // creo un nuovo monitoredItem se non ci fosse x ogni variabile dell'oggetto... + testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Stato", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Stato}", forceSend); + testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Velocita", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Velocita}", forceSend); + testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Termico", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Termico}", forceSend); + testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_MagnetoTermico", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.MagnetoTermico}", forceSend); + testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_AvariaInverter", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.AvariaInverter}", forceSend); + } + } + + + /// + /// Verifica ed invia variazioni DAL FORMATO RAW data (byte[]) --> esplode oggetti e li testa 1:1 + /// + /// + /// + /// + internal override void checkAndSendRaw(Opc.Ua.Client.MonitoredItem MonIt, byte[] NotifyValue, bool forceSend) + { + if (MonIt != null) + { + if (NotifyValue != null && NotifyValue.Length > 0) + { + + // verifico variazione "globale" + if (!lastData.Equals(currData)) + { + // effettuo test/invio x ogni info + testSendDataBlock(MonIt.StartNodeId, "Calibratrice_L1", lastData.Calibratrice_L1, currData.Calibratrice_L1, forceSend); + testSendDataBlock(MonIt.StartNodeId, "Calibratrice_L2", lastData.Calibratrice_L2, currData.Calibratrice_L2, forceSend); + testSendDataBlock(MonIt.StartNodeId, "Dewatering_L1", lastData.Dewatering_L1, currData.Dewatering_L1, forceSend); + testSendDataBlock(MonIt.StartNodeId, "Dewatering_L2", lastData.Dewatering_L2, currData.Dewatering_L2, forceSend); + testSendDataBlock(MonIt.StartNodeId, "Elevatore_L1", lastData.Elevatore_L1, currData.Elevatore_L1, forceSend); + testSendDataBlock(MonIt.StartNodeId, "Elevatore_L2", lastData.Elevatore_L2, currData.Elevatore_L2, forceSend); + testSendDataBlock(MonIt.StartNodeId, "ImmergitoreBins_L1", lastData.ImmergitoreBins_L1, currData.ImmergitoreBins_L1, forceSend); + testSendDataBlock(MonIt.StartNodeId, "ImmergitoreBins_L2", lastData.ImmergitoreBins_L2, currData.ImmergitoreBins_L2, forceSend); + testSendDataBlock(MonIt.StartNodeId, "ImmergitoreCasse_L1", lastData.ImmergitoreCasse_L1, currData.ImmergitoreCasse_L1, forceSend); + testSendDataBlock(MonIt.StartNodeId, "ImmergitoreCasse_L2", lastData.ImmergitoreCasse_L2, currData.ImmergitoreCasse_L2, forceSend); + testSendDataBlock(MonIt.StartNodeId, "NastroTaglierina_L1", lastData.NastroTaglierina_L1, currData.NastroTaglierina_L1, forceSend); + testSendDataBlock(MonIt.StartNodeId, "NastroTaglierina_L2", lastData.NastroTaglierina_L2, currData.NastroTaglierina_L2, forceSend); + testSendDataBlock(MonIt.StartNodeId, "Precalibro_L1", lastData.Precalibro_L1, currData.Precalibro_L1, forceSend); + testSendDataBlock(MonIt.StartNodeId, "Precalibro_L2", lastData.Precalibro_L2, currData.Precalibro_L2, forceSend); + testSendDataBlock(MonIt.StartNodeId, "Taglierina_L1", lastData.Taglierina_L1, currData.Taglierina_L1, forceSend); + testSendDataBlock(MonIt.StartNodeId, "Taglierina_L2", lastData.Taglierina_L2, currData.Taglierina_L2, forceSend); + // salvo lastData... + lastData = currData; + } + } + else + { + lgError($"checkAndSend ERROR | MonIt: {MonIt.DisplayName} | NotifyValue Null!!!"); + } + } + else + { + lgError("checkAndSend ERROR: MonIt null"); + } + } #endregion Public Constructors @@ -337,7 +430,7 @@ namespace IOB_WIN_NEXT B_input += (1 << 3); } } - + // controllo se sono poweroff e se non ho dati buoni da > 2 minuti --> disconnetto if (!powerOnOk && adesso.Subtract(lastCurrent).TotalMinutes > 2) { diff --git a/IOB-WIN-NEXT/Objects.cs b/IOB-WIN-NEXT/Objects.cs index 73a803c1..5304b21f 100644 --- a/IOB-WIN-NEXT/Objects.cs +++ b/IOB-WIN-NEXT/Objects.cs @@ -101,6 +101,11 @@ namespace IOB_WIN_NEXT /// public string value { get; set; } = ""; + /// + /// Valore Registrato in formato byte array + /// + public byte[] rawByte { get; set; } = new byte[1]; + /// /// Timestamp data-ora evento registrato ///