diff --git a/IOB-UT-NEXT/baseUtils.cs b/IOB-UT-NEXT/baseUtils.cs
index 25565fc4..7f074349 100644
--- a/IOB-UT-NEXT/baseUtils.cs
+++ b/IOB-UT-NEXT/baseUtils.cs
@@ -251,15 +251,15 @@ namespace IOB_UT_NEXT
{
// problema certificati locali...
// https://living-sun.com/it/c/226351-webexception-could-not-establish-trust-relationship-for-the-ssl-tls-secure-channel-c-aspnet-web-services-ssl-webexception.html
- // hack x certificati locali in 10.74.82.*
- if (URL.Contains("10.74.82."))
+ // hack x certificati locali in 10.74.*
+ if (URL.Contains("10.74."))
{
System.Net.ServicePointManager.ServerCertificateValidationCallback = (senderX, certificate, chain, sslPolicyErrors) => { return true; };
}
answ = clientPayload.UploadString(URL, payload);
if (answ != "OK")
{
- lg.Error($"Invio dati fallito:{Environment.NewLine}- URL{Environment.NewLine}{URL}{Environment.NewLine}- payload{Environment.NewLine}{payload}");
+ lg.Error($"Invio dati fallito, ricevuto messaggio [{answ}]:{Environment.NewLine}- URL{Environment.NewLine}{URL}{Environment.NewLine}- payload{Environment.NewLine}{payload}");
}
}
catch (Exception exc)
diff --git a/IOB-WIN-NEXT/IobOpcUa.cs b/IOB-WIN-NEXT/IobOpcUa.cs
index c92a8a14..3e21cc06 100644
--- a/IOB-WIN-NEXT/IobOpcUa.cs
+++ b/IOB-WIN-NEXT/IobOpcUa.cs
@@ -1,78 +1,26 @@
using IOB_UT_NEXT;
using MapoSDK;
-using MTConnect.Clients;
using Newtonsoft.Json;
-using System;
-using System.Threading.Tasks;
using Opc.Ua;
using Opc.Ua.Configuration;
+using System;
using System.Collections.Generic;
using System.IO;
-using System.Net.NetworkInformation;
-using System.Threading;
-using System.Windows.Forms;
using System.Linq;
+using System.Net.NetworkInformation;
using System.Text;
-using System.Runtime.Serialization.Formatters.Binary;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
namespace IOB_WIN_NEXT
{
public class IobOpcUa : IobGeneric
{
- #region Private Fields
-
- ///
- /// Struttura dove vengono memorizzati i dataitem ed i rispettivi valori x processing
- ///
- protected Dictionary dataItemMem = new Dictionary();
-
- ///
- /// Elenco degli items da monitorare come risultato del browse iniziale
- ///
- private Dictionary selectedItemList = new Dictionary();
-
- #endregion Private Fields
-
- #region Protected Fields
-
- ///
- /// Abilitazione restart (da opt par...)
- ///
- protected bool enableCliRestart = false;
-
- ///
- /// Gestione filtraggio dati
- ///
- protected bool enableDataFilter = false;
-
- ///
- /// Determina se ha effettuata lettura items in memoria x confronto...
- ///
- protected bool hasReadItems = false;
-
- ///
- /// Ultimo current received x gestione update periodico...
- ///
- protected DateTime lastCurrent = DateTime.Now;
-
-
- ///
- /// Oggetto MAIN x connessione MTC
- ///
- protected UAClient UA_ref;
-
- ///
- /// Veto controllo status x log...
- ///
- protected DateTime vetoCheckStatus = DateTime.Now;
-
- #endregion Protected Fields
-
#region Public Constructors
///
- /// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation
- /// https://github.com/OPCFoundation/UA-.NETStandard
+ /// Estende l'init della classe base, impiegando il pacchetto Nuget OPC-UA foundation https://github.com/OPCFoundation/UA-.NETStandard
///
///
///
@@ -106,889 +54,6 @@ namespace IOB_WIN_NEXT
#endregion Public Constructors
- #region Protected Properties
-
- ///
- /// Verifico se abbia ALMENO un errore...
- ///
- protected bool hasError
- {
- get
- {
- return checkMultiCondition(opcUaParams.condError);
- }
- }
-
- ///
- /// Indica se abbia emergenza ARMATA (cond normale)
- ///
- protected bool hasEStopArmed
- {
- get
- {
- return checkMultiCondition(opcUaParams.condEStop);
- }
- }
-
- ///
- /// Indica se abbia stato POWER ON (multicondizione)
- ///
- protected virtual bool hasPowerOn
- {
- get
- {
- return checkMultiCondition(opcUaParams.condPowerOn);
- }
- }
-
- ///
- /// Indica se abbia stato MANUAL (condizioni varie, es stopped)
- ///
- protected virtual bool isManual
- {
- get
- {
- return checkMultiCondition(opcUaParams.condManual);
- }
- }
-
- ///
- /// Indica se abbia stato READY (condizioni varie, es ausiliari OK)
- ///
- protected virtual bool isReady
- {
- get
- {
- return checkMultiCondition(opcUaParams.condReady);
- }
- }
-
- ///
- /// Indica se sia in stato WarmUp / CoolDown (riscaldamento/raffreddamento)
- ///
- protected bool isWarmUpCoolDown
- {
- get
- {
- return checkMultiCondition(opcUaParams.condWarmUpCoolDown);
- }
- }
-
- ///
- /// Indica se sia in stato Warning
- ///
- protected bool isWarning
- {
- get
- {
- return checkMultiCondition(opcUaParams.condWarning);
- }
- }
- ///
- /// Indica se sia in stato Ssetup
- ///
- protected bool isSetup
- {
- get
- {
- return checkMultiCondition(opcUaParams.condSetup);
- }
- }
-
- ///
- /// Indica se abbia stato READY (condizioni varie, es ausiliari OK)
- ///
- protected bool isWorking
- {
- get
- {
- // cerco SE HO cond OPC Ua o classica... nel caso creo e salvo
- if (opcUaParams.condWorkOpc.checkList == null || opcUaParams.condWorkOpc.checkList.Count == 0)
- {
- opcUaParams.condWorkOpc = new diCheckCondSetup()
- {
- checkList = opcUaParams.condWork,
- checkMode = boolCheckMode.AND,
- negateValue = false
- };
- }
- return checkMultiCondition(opcUaParams.condWorkOpc);
- }
- }
-
- ///
- /// Parametri specifici MTC
- ///
- protected OpcUaParamConf opcUaParams { get; set; }
-
- ///
- /// URL x salvataggio elenco dataItems OpcUa
- ///
- protected string urlSaveDataItems
- {
- get
- {
- string answ = "";
- try
- {
- string machineName = Environment.MachineName;
- answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/saveDataItems/{cIobConf.codIOB}";
- }
- catch (Exception exc)
- {
- lgError(exc, "Errore in composizione urlSaveDataItems");
- }
- return answ;
- }
- }
-
- #endregion Protected Properties
-
- #region Private Methods
-
- ///
- /// Verifica ed invia variazioni
- ///
- ///
- ///
- ///
- internal void checkAndSend(Opc.Ua.Client.MonitoredItem MonIt, string NotifyValue, bool forceSend)
- {
- if (MonIt != null)
- {
- if (!string.IsNullOrEmpty(NotifyValue))
- {
- string sVal = "";
- string descr = "";
- DateTime locTStamp = DateTime.Now;
- descr = itemTranslation("OPC", MonIt.DisplayName);
- sVal = $"Change: {locTStamp.ToString()} | descr: {descr} | Id: {MonIt.StartNodeId} | Val: {NotifyValue}";
- lgInfo(sVal);
-
- // verifico se salvare
- bool changed = checkSaveValue(MonIt, NotifyValue);
- // 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");
- }
- }
-
- ///
- /// 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)
- ///
- protected bool doByteRead { get; set; } = false;
- ///
- /// Area dati raw per lettura encoded (SE presente)
- ///
- protected byte[] byteRawData { get; set; } = new byte[1];
-
- ///
- /// Vera connessione ad OpcUa
- ///
- ///
- private async Task doConnect()
- {
- short esitoLink = 0;
- // reset memoria dataItem..
- dataItemMem = new Dictionary();
- // predisposizione conf oggetto di comunicazione MTC
- int port = 4840;
- int.TryParse(cIobConf.cncPort, out port);
- // ora avvio
- try
- {
- lgInfo("Start init OpcUa Client");
- // Define the UA Client application
- ApplicationInstance application = new ApplicationInstance();
- application.ApplicationName = "Steamware IOB-WIN Client";
- application.ApplicationType = ApplicationType.Client;
-
- // load the application configuration.
- string confPath = $"{Application.StartupPath}\\DATA\\CONF\\IobOpcUaClient.Config.xml";
- await application.LoadApplicationConfiguration(confPath, silent: false).ConfigureAwait(false);
- // check the application certificate.
- await application.CheckApplicationInstanceCertificate(silent: false, minimumKeySize: 0).ConfigureAwait(false);
-
- lgInfo($"Chiamata UAClient con configurazione standard: {application.ApplicationConfiguration.ApplicationName}");
- UA_ref = new UAClient(application.ApplicationConfiguration, cIobConf.codIOB, opcUaParams.Identity.UserName, opcUaParams.Identity.Passwd, isVerboseLog, ClientBase.ValidateResponse);
-
- lgInfo($"Chiamata apertura OpcUa Client: {cIobConf.cncIpAddr}:{port}");
- UA_ref.ServerUrl = $"opc.tcp://{cIobConf.cncIpAddr}:{port}";
-
- var task = Task.Run(async () =>
- {
- return await UA_ref.ConnectAsync().ConfigureAwait(false);
- });
- bool connected = task.Result;
- if (connected)
- {
- // faccio un primo browse dei dati...
- Dictionary nodeIdNameList = new Dictionary();
- if (!string.IsNullOrEmpty(opcUaParams.BrowseFullVal))
- {
- try
- {
- UA_ref.Browse(opcUaParams.BrowseFullVal, opcUaParams.filterItemsNodeId, ref nodeIdNameList);
- }
- catch
- { }
- }
- else
- {
- UA_ref.Browse(opcUaParams.BrowseNSIndex, opcUaParams.BrowseValue, opcUaParams.filterItemsNodeId, ref nodeIdNameList);
- }
- // loggo elenco degli item sottocrivibili...
- lgDebug("---------- AVAILABLE FOR SUBSCRIBE ----------");
- foreach (var item in nodeIdNameList)
- {
- lgDebug(item.Key);
- }
- lgDebug("---------- END LIST ----------");
-
- // se ho un insieme non vuoto degli item sottoscritti carico solo quelli
- if (opcUaParams.subscribedItems != null && opcUaParams.subscribedItems.Count > 0)
- {
- // cerco e aggiungo SOLO quelle indicati
- foreach (var currItem in opcUaParams.subscribedItems)
- {
- var foundItems = nodeIdNameList.Where(x => x.Key.Contains(currItem)).ToList();
- if (foundItems != null && foundItems.Count > 0)
- {
- foreach (var fItem in foundItems)
- {
- // verifico di NON duplicare...
- if (!selectedItemList.ContainsKey(fItem.Key))
- {
- selectedItemList.Add(fItem.Key, fItem.Value);
- }
- }
- }
- else
- {
- lgDebug($"subscribedItems non trovato: {currItem}");
- }
- }
- lgDebug($"Aggiunti {selectedItemList.Count} items!");
- }
- // altrimenti tutti!
- else
- {
- selectedItemList = nodeIdNameList;
- }
-
- // loggo elenco degli item sottocrivibili...
- lgInfo("---------- SUBSCRIBED NODES ----------");
- foreach (var item in selectedItemList)
- {
- lgInfo(item.Key);
- }
- lgInfo("---------- END LIST ----------");
-
- // sottoscrivo a rilevazione cambio dati solo l'incrocio degli insiemi
- List subscribedItems = UA_ref.SubscribeToDataChanges(selectedItemList);
- // aggiungo come DataItems
- int dSamplePeriod = 0;
- int threshDBand = 0;
- string uuid = "";
- foreach (var item in subscribedItems)
- {
- OpcUaDataItemExt newItem = formatDataItem(ref dSamplePeriod, ref threshDBand, ref uuid, item);
- // controllo non sia già stato aggiunto
- if (dataItemMem.ContainsKey(uuid))
- {
- lgDebug($"Item ALREADY subscribed: {uuid} | NOT re-adding");
- }
- else
- {
- dataItemMem.Add(uuid, newItem);
- lgDebug($"Item subscribed: {uuid}");
- // verifico se ho i dati complessi by design
- string currVal = "";
- if (doByteRead)
- {
- // restituisce i 115 byte da deserializzare... qui HACK!
- var rawVal = UA_ref.ReadNodeRaw(item.StartNodeId);
- if (rawVal != null)
- {
- byteRawData = getByteRaw((DataValue)rawVal);
- checkAndSendRaw(item, byteRawData, true);
- }
- }
- else
- {
- currVal = UA_ref.ReadNode(item.StartNodeId);
- checkAndSend(item, currVal, true);
- }
- }
- }
- // gestione eventi change
- UA_ref.eh_MonItChange += UA_ref_eh_MonItChange;
- lgInfo("eh_MonItChange event registered");
- }
-
- esitoLink = 1;
-
- // fix tempi!
- DateTime adesso = DateTime.Now;
- lastPzCountSend = adesso;
- lastWarnODL = adesso;
- lastCurrent = adesso;
- }
- catch (Exception exc)
- {
- lgError($"Eccezione in doConnect{Environment.NewLine}{exc}");
- }
- return esitoLink;
- }
-
- protected byte[] getByteRaw(DataValue obj)
- {
- if (obj == null)
- return null;
-
- byte[] rawByte = new byte[1];
- if (obj != null)
- {
- try
- {
- var wrapVal = ((DataValue)obj).WrappedValue;
- //rawByte = ObjectToByteArray(rawVal);
- var bodyVal = ((Opc.Ua.ExtensionObject)wrapVal.Value).Body;
- rawByte = (byte[])bodyVal;
- }
- catch
- { }
- }
- return rawByte;
- }
-
- ///
- /// Formatta un dataitem x salvataggio in memoria locale
- ///
- ///
- ///
- ///
- ///
- ///
- private OpcUaDataItemExt formatDataItem(ref int dSamplePeriod, ref int threshDBand, ref string uuid, Opc.Ua.Client.MonitoredItem dataItem)
- {
- OpcUaDataItemExt currDataItem;
- // calcolo parametri
- uuid = $"{dataItem.DisplayName}";
- // SOLO SE è abilitato il datafiltering...
- if (enableDataFilter)
- {
- threshDBand = 1;
- // controllo SE ho conf x deadband...
- if (opcUaParams.paramsEndThresh.Count > 0)
- {
- // ciclo su tutti i parametri indicati...
- foreach (var item in opcUaParams.paramsEndThresh)
- {
- if (uuid.EndsWith(item.Key))
- {
- threshDBand = item.Value;
- }
- }
- }
- }
- else
- {
- threshDBand = 0;
- }
- dSamplePeriod = 60;
-
- // salvo oggetto x "uso interno"
- currDataItem = new OpcUaDataItemExt(dataItem)
- {
- uid = uuid,
- thresholdDeadBand = threshDBand,
- samplePeriod = dSamplePeriod
- };
-
- return currDataItem;
- }
-
- ///
- /// Effettua invio a MP/IO dell'elenco serializzato dei dataItems
- ///
- ///
- private void sendDataItemsList(List dataItems)
- {
- string rawData = JsonConvert.SerializeObject(dataItems);
- try
- {
- utils.callUrlNow($"{urlSaveDataItems}", rawData);
- }
- catch(Exception exc)
- {
- lgError($"Eccezione in sendDataItemsList{Environment.NewLine} - url: {urlSaveDataItems}{Environment.NewLine}- payload:{rawData}{Environment.NewLine}Eccezione:{Environment.NewLine}{exc}");
- }
- }
-
- ///
- /// Periodo massimo (in sec) per letture dati RAW in mancanza di eventi
- ///
- protected int lastCurrentMaxElapsed { get; set; } = 120;
-
- ///
- /// Evento rilevazione modifica valori --> chiamo checkSend
- ///
- ///
- ///
- internal virtual void UA_ref_eh_MonItChange(object sender, opcUaMonitItemChange e)
- {
- string currVal = "";
- // da verificare decodifica valore byte...
- if (doByteRead)
- {
- var currNot = e.CurrNotify;
- if (currNot != null)
- {
- byteRawData = getByteRaw((DataValue)currNot.Value);
- checkAndSendRaw(e.CurrMonitoredItem, byteRawData, false);
- }
- }
- else
- {
- currVal = $"{e.CurrNotify.Value}";
- checkAndSend(e.CurrMonitoredItem, currVal, false);
- }
- // aggiorno ultima lettura
- lastCurrent = DateTime.Now;
- }
-
- protected int WatchDog = 0;
-
- #endregion Private Methods
-
- #region Protected Methods
-
- ///
- /// Verifica un DataItem e se il valore corrisponde a quello indicato come "true value" restituisce true
- ///
- ///
- ///
- ///
- protected bool checkDataItem(string itemName, string trueVal)
- {
- bool answ = false;
- OpcUaDataItemExt currValue = null;
- try
- {
- currValue = dataItemMem[itemName];
- answ = (currValue.value.Equals(trueVal));
- }
- catch
- {
- lgError($"Errore in decodifica valore per {itemName} rispetto a {trueVal} | recuperato {currValue} / {currValue.value}");
- }
- return answ;
- }
-
- ///
- /// Verifica condizione "multipla" secondo setup json
- ///
- /// Set condizioni da validare
- ///
- protected bool checkMultiCondition(diCheckCondSetup reqCondition)
- {
- bool answ = false;
- int numCondOk = 0;
- int numCond = 0;
- if (reqCondition.checkList != null && reqCondition.checkList.Count > 0)
- {
- numCond = reqCondition.checkList.Count;
- // cerco nell'elenco delle condizioni che indicano lavora se sono ok faccio +1 conteggio......
- foreach (var item in reqCondition.checkList)
- {
- if (string.IsNullOrEmpty(item.keyName))
- {
- lgError($"Attenzione: item vuoto in checkMultiCondition{Environment.NewLine}StackTrace: {Environment.StackTrace}");
- }
- else
- {
- if (getDataItemValue(item.keyName) == item.targetValue)
- {
- numCondOk++;
- }
- }
- }
- if (reqCondition.checkMode == boolCheckMode.AND)
- {
- answ = (numCond == numCondOk);
- }
- else if (reqCondition.checkMode == boolCheckMode.OR)
- {
- answ = numCondOk > 0;
- }
- }
- else
- {
- answ = true;
- }
- // verifico se devo negare il valore...
- answ = reqCondition.negateValue ? !answ : answ;
- // restituisco
- return answ;
- }
-
- ///
- /// Verifica / Salva valore e restitusice SE sia variato (e quindi da inviare...)
- ///
- ///
- ///
- protected bool checkSaveValue(Opc.Ua.Client.MonitoredItem dataItem, string NotifyValue)
- {
- bool answ = !enableDataFilter;
- double oldVal = 0;
- double newVal = 0;
- if (dataItem != null)
- {
- if (!string.IsNullOrEmpty(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;
- if (dataItemMem.ContainsKey(uuid))
- {
- OpcUaDataItemExt currDataItemMem = dataItemMem[uuid];
- // controllo SE SIA scaduto il tempo massimo...
- if (Math.Abs(dataItemMem[uuid].valueTimestamp.Subtract(adesso).TotalSeconds) > currDataItemMem.samplePeriod)
- {
- answ = true;
- }
- else
- {
- // ALTRIMENTI controllo SE diverso
- if (dataItemMem[uuid].value != $"{NotifyValue}")
- {
- lgInfo($"Val uuid: {dataItemMem[uuid].value} | NotifyValue: {NotifyValue}");
- // controllo SE ho DeadBand...
- if (dataItemMem[uuid].thresholdDeadBand > 0)
- {
- // recupero i valori e testo DeadBand...
- bool isNum01 = double.TryParse(dataItemMem[uuid].value.Replace(".", ","), out oldVal);
- bool isNum02 = double.TryParse($"{NotifyValue}".Replace(".", ","), out newVal);
- // test deadband!
- if (!(isNum01 && isNum02))
- {
- answ = true;
- }
- else
- {
- if (Math.Abs(newVal - oldVal) > dataItemMem[uuid].thresholdDeadBand)
- {
- // indico da salvare..
- answ = true;
- }
- }
- lgInfo($"Test deadband: oldVal: {oldVal} | newVal: {newVal}");
- }
- }
- }
- if (answ)
- {
- // salvo!
- dataItemMem[uuid].value = $"{NotifyValue}";
- dataItemMem[uuid].valueTimestamp = adesso;
- }
- }
- else
- {
- // registro non trovato da aggiungere...
- lgInfo($"DataItem non trovato in checkSaveValue: {dataItem.DisplayName}");
- // provo a creare oggetto in memoria...
- try
- {
- int dSamplePeriod = 0;
- int threshDBand = 0;
- uuid = "";
- var currDataItem = formatDataItem(ref dSamplePeriod, ref threshDBand, ref uuid, dataItem);
- // sistemo valore/periodo
- currDataItem.value = $"{NotifyValue}";
- currDataItem.valueTimestamp = adesso;
- // aggiungo
- dataItemMem.Add(uuid, currDataItem);
- // converto gli attuali nell'elenco dataitem...
- List elencoDataItems = dataItemMem.Select(d => new machDataItem()
- {
- uuid = d.Key,
- Category = DataItemCategory.EVENT,
- Name = d.Value.DisplayName,
- Type = $"{d.Value.NodeClass}",
- SubType = $"{dataItem.StartNodeId}"
- }
- ).ToList();
-
- // aspetta un tempo random da 10-100 ms...
- Random rnd = new Random();
- Task.Delay(rnd.Next(10, 100));
- // invio il dataItem serializzato...
- sendDataItemsList(elencoDataItems);
- }
- catch (Exception exc)
- {
- lgError($"Eccezione in checkSaveSample{Environment.NewLine}{exc}");
- }
- }
- }
- else
- {
- lgError("Attenzione: checkSaveItem con Notify null!");
- }
- }
- else
- {
- lgError("Attenzione: checkSaveItem con MonIt null!");
- }
- return answ;
- }
-
- ///
- /// Effettua decodifica aree memoria alla bitmap usata x MAPO
- ///
- protected virtual 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
- * B7: emergenza
- ---------------------------------------------------- */
-
- // se valido il check ping lo eseguo... altrimenti lo do x buono
- bool checkPing = !opcUaParams.pingAsPowerOn;
- string currRun = "";
- if (!checkPing)
- {
- checkPing = (testPingMachine == IPStatus.Success);
- }
- // 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 > lastCurrentMaxElapsed --> disconnetto
- if (!powerOnOk && adesso.Subtract(lastCurrent).TotalSeconds > lastCurrentMaxElapsed)
- {
- tryDisconnect();
- }
-
- // solo se non ho veto check
- int vFactor = 2;
- if (vetoCheckStatus < adesso)
- {
- lgTrace($"Stato variabili checkPing: {testPingMachine}");
- // imposto veto per vetoSeconds...
- vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
- }
-
- // se abilitato watchdog...
- if (opcUaParams.WatchDog.IsEnabled)
- {
- lgTrace("WatchDog 01");
- 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);
- lgTrace("Effettuata scrittura WatchDog");
- }
- catch (Exception exc)
- {
- lgError($"Eccezione in gestione WatchDog, valore attuale {WatchDog}{Environment.NewLine}{exc}");
- }
- }
- }
- else
- {
- lgTrace("WatchDog disabilitato");
- }
-
- // log opzionale!
- if (verboseLog)
- {
- lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
- }
- }
-
- ///
- /// Effettua traduzione ITEM da LUT parametrica (key: tipo+id) del file di conf, se non trovo uso key
- ///
- ///
- ///
- ///
- protected string itemTranslation(string tipo, string id)
- {
- string answ = "";
- string lemma = id;
- if (!string.IsNullOrEmpty(tipo))
- {
- lemma = $"{tipo}_{id}";
- }
- // cerco nel dizionario delle traduzioni SE esiste un valore e prendo quello, altrimenti uso il lemma...
- if (opcUaParams.itemTranslation.ContainsKey(lemma))
- {
- answ = opcUaParams.itemTranslation[lemma];
- }
- else
- {
- answ = lemma;
- }
- return answ;
- }
-
- ///
- /// Effettua lettura file di conf specifico OPC-UA da oggetto serializzato json
- /// Nome file da cui leggere i parametri json
- ///
- protected void loadOpcUaConf(string fileName)
- {
- string jsonFullPath = $"{Application.StartupPath}/DATA/CONF/{fileName}";
- lgInfo($"Apertura file {jsonFullPath}");
- using (StreamReader reader = new StreamReader(jsonFullPath))
- {
- string jsonData = reader.ReadToEnd().Replace("\n", "").Replace("\r", "");
- if (!string.IsNullOrEmpty(jsonData))
- {
- lgDebug($"File json composto da {jsonData.Length} caratteri");
- try
- {
- opcUaParams = JsonConvert.DeserializeObject(jsonData);
- lgDebug($"Decodifica aree OpcUaParamConf: trovati {opcUaParams.paramsEndThresh.Count} valori paramsEndThresh");
- // sistemo se ci sono dati memMap...
- memMap = new plcMemMap();
- if (opcUaParams.mMapWrite != null)
- {
- memMap.mMapWrite = opcUaParams.mMapWrite;
- }
- if (opcUaParams.mMapRead != null)
- {
- memMap.mMapRead = opcUaParams.mMapRead;
- }
- setupMemMap();
- }
- catch (Exception exc)
- {
- lgError($"Eccezione in decodifica conf json OPC-UA:{Environment.NewLine}{exc}");
- }
- }
- else
- {
- lgError("Errore in loadOpcUaConf: file json vuoto!");
- }
- }
- //reader.Dispose();
- }
-
- #endregion Protected Methods
-
#region Public Methods
///
@@ -1099,8 +164,8 @@ namespace IOB_WIN_NEXT
}
///
- /// Effettua lettura semafori principale
- /// Parametri da aggiornare x display in form
+ /// Effettua lettura semafori principale Parametri da
+ /// aggiornare x display in form
///
public override void readSemafori(ref newDisplayData currDispData)
{
@@ -1159,7 +224,6 @@ namespace IOB_WIN_NEXT
// altrimenti pausa forzata
Thread.Sleep(300);
}
-
}
///
@@ -1291,5 +355,960 @@ namespace IOB_WIN_NEXT
}
#endregion Public Methods
+
+ #region Internal Methods
+
+ ///
+ /// Verifica ed invia variazioni
+ ///
+ ///
+ ///
+ ///
+ internal bool checkAndSend(Opc.Ua.Client.MonitoredItem MonIt, string NotifyValue, bool forceSend)
+ {
+ bool changed = false;
+ if (MonIt != null)
+ {
+ if (!string.IsNullOrEmpty(NotifyValue))
+ {
+ string sVal = "";
+ string descr = "";
+ DateTime locTStamp = DateTime.Now;
+ descr = itemTranslation("OPC", MonIt.DisplayName);
+ sVal = $"Change: {locTStamp.ToString()} | descr: {descr} | Id: {MonIt.StartNodeId} | Val: {NotifyValue}";
+ lgInfo(sVal);
+
+ // verifico se salvare
+ changed = checkSaveValue(MonIt, NotifyValue, true);
+ // 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");
+ }
+ return changed;
+ }
+
+ ///
+ /// Verifica ed invia variazioni DAL FORMATO RAW data (byte[])
+ ///
+ ///
+ ///
+ ///
+ internal virtual bool checkAndSendRaw(Opc.Ua.Client.MonitoredItem MonIt, byte[] NotifyValue, bool forceSend)
+ {
+ bool changed = false;
+ 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
+ changed = checkSaveValue(MonIt, currVal, true);
+ // 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");
+ }
+ return changed;
+ }
+
+ internal void sendDataItemListToServer(NodeId StartNodeId)
+ {
+ // converto gli attuali nell'elenco dataitem...
+ List elencoDataItems = dataItemMem.Select(d => new machDataItem()
+ {
+ uuid = d.Key,
+ Category = DataItemCategory.EVENT,
+ Name = d.Value.DisplayName,
+ Type = $"{d.Value.NodeClass}",
+ SubType = $"{StartNodeId}"
+ }
+ ).ToList();
+
+ // aspetta un tempo random da 10-100 ms...
+ Random rnd = new Random();
+ Task.Delay(rnd.Next(10, 100));
+ // invio il dataItem serializzato...
+ sendDataItemsList(elencoDataItems);
+ }
+
+ ///
+ /// Evento rilevazione modifica valori --> chiamo checkSend
+ ///
+ ///
+ ///
+ internal virtual void UA_ref_eh_MonItChange(object sender, opcUaMonitItemChange e)
+ {
+ string currVal = "";
+ // da verificare decodifica valore byte...
+ if (doByteRead)
+ {
+ var currNot = e.CurrNotify;
+ if (currNot != null)
+ {
+ byteRawData = getByteRaw((DataValue)currNot.Value);
+ checkAndSendRaw(e.CurrMonitoredItem, byteRawData, false);
+ }
+ }
+ else
+ {
+ currVal = $"{e.CurrNotify.Value}";
+ checkAndSend(e.CurrMonitoredItem, currVal, false);
+ }
+ // aggiorno ultima lettura
+ lastCurrent = DateTime.Now;
+ }
+
+ #endregion Internal Methods
+
+ #region Protected Fields
+
+ ///
+ /// Struttura dove vengono memorizzati i dataitem ed i rispettivi valori x processing
+ ///
+ protected Dictionary dataItemMem = new Dictionary();
+
+ ///
+ /// Abilitazione restart (da opt par...)
+ ///
+ protected bool enableCliRestart = false;
+
+ ///
+ /// Gestione filtraggio dati
+ ///
+ protected bool enableDataFilter = false;
+
+ ///
+ /// Determina se ha effettuata lettura items in memoria x confronto...
+ ///
+ protected bool hasReadItems = false;
+
+ ///
+ /// Ultimo current received x gestione update periodico...
+ ///
+ protected DateTime lastCurrent = DateTime.Now;
+
+ ///
+ /// Oggetto MAIN x connessione MTC
+ ///
+ protected UAClient UA_ref;
+
+ ///
+ /// Veto controllo status x log...
+ ///
+ protected DateTime vetoCheckStatus = DateTime.Now;
+
+ protected int WatchDog = 0;
+
+ #endregion Protected Fields
+
+ #region Protected Properties
+
+ ///
+ /// Area dati raw per lettura encoded (SE presente)
+ ///
+ protected byte[] byteRawData { get; set; } = new byte[1];
+
+ ///
+ /// Indica se si debba leggere un area di tipo "encoded" (byte raw --> obj)
+ ///
+ protected bool doByteRead { get; set; } = false;
+
+ ///
+ /// Verifico se abbia ALMENO un errore...
+ ///
+ protected bool hasError
+ {
+ get
+ {
+ return checkMultiCondition(opcUaParams.condError);
+ }
+ }
+
+ ///
+ /// Indica se abbia emergenza ARMATA (cond normale)
+ ///
+ protected bool hasEStopArmed
+ {
+ get
+ {
+ return checkMultiCondition(opcUaParams.condEStop);
+ }
+ }
+
+ ///
+ /// Indica se abbia stato POWER ON (multicondizione)
+ ///
+ protected virtual bool hasPowerOn
+ {
+ get
+ {
+ return checkMultiCondition(opcUaParams.condPowerOn);
+ }
+ }
+
+ ///
+ /// Indica se abbia stato MANUAL (condizioni varie, es stopped)
+ ///
+ protected virtual bool isManual
+ {
+ get
+ {
+ return checkMultiCondition(opcUaParams.condManual);
+ }
+ }
+
+ ///
+ /// Indica se abbia stato READY (condizioni varie, es ausiliari OK)
+ ///
+ protected virtual bool isReady
+ {
+ get
+ {
+ return checkMultiCondition(opcUaParams.condReady);
+ }
+ }
+
+ ///
+ /// Indica se sia in stato Ssetup
+ ///
+ protected bool isSetup
+ {
+ get
+ {
+ return checkMultiCondition(opcUaParams.condSetup);
+ }
+ }
+
+ ///
+ /// Indica se sia in stato WarmUp / CoolDown (riscaldamento/raffreddamento)
+ ///
+ protected bool isWarmUpCoolDown
+ {
+ get
+ {
+ return checkMultiCondition(opcUaParams.condWarmUpCoolDown);
+ }
+ }
+
+ ///
+ /// Indica se sia in stato Warning
+ ///
+ protected bool isWarning
+ {
+ get
+ {
+ return checkMultiCondition(opcUaParams.condWarning);
+ }
+ }
+
+ ///
+ /// Indica se abbia stato READY (condizioni varie, es ausiliari OK)
+ ///
+ protected bool isWorking
+ {
+ get
+ {
+ // cerco SE HO cond OPC Ua o classica... nel caso creo e salvo
+ if (opcUaParams.condWorkOpc.checkList == null || opcUaParams.condWorkOpc.checkList.Count == 0)
+ {
+ opcUaParams.condWorkOpc = new diCheckCondSetup()
+ {
+ checkList = opcUaParams.condWork,
+ checkMode = boolCheckMode.AND,
+ negateValue = false
+ };
+ }
+ return checkMultiCondition(opcUaParams.condWorkOpc);
+ }
+ }
+
+ ///
+ /// Periodo massimo (in sec) per letture dati RAW in mancanza di eventi
+ ///
+ protected int lastCurrentMaxElapsed { get; set; } = 120;
+
+ ///
+ /// Parametri specifici MTC
+ ///
+ protected OpcUaParamConf opcUaParams { get; set; }
+
+ ///
+ /// URL x salvataggio elenco dataItems OpcUa
+ ///
+ protected string urlSaveDataItems
+ {
+ get
+ {
+ string answ = "";
+ try
+ {
+ string machineName = Environment.MachineName;
+ answ = $@"{cIobConf.serverData.TRANSP}://{cIobConf.serverData.MPIP}{cIobConf.serverData.MPURL}{cIobConf.serverData.CMDALIVE}/saveDataItems/{cIobConf.codIOB}";
+ }
+ catch (Exception exc)
+ {
+ lgError(exc, "Errore in composizione urlSaveDataItems");
+ }
+ return answ;
+ }
+ }
+
+ #endregion Protected Properties
+
+ #region Protected Methods
+
+ ///
+ /// Verifica un DataItem e se il valore corrisponde a quello indicato come "true value"
+ /// restituisce true
+ ///
+ ///
+ ///
+ ///
+ protected bool checkDataItem(string itemName, string trueVal)
+ {
+ bool answ = false;
+ OpcUaDataItemExt currValue = null;
+ try
+ {
+ currValue = dataItemMem[itemName];
+ answ = (currValue.value.Equals(trueVal));
+ }
+ catch
+ {
+ lgError($"Errore in decodifica valore per {itemName} rispetto a {trueVal} | recuperato {currValue} / {currValue.value}");
+ }
+ return answ;
+ }
+
+ ///
+ /// Verifica condizione "multipla" secondo setup json
+ ///
+ /// Set condizioni da validare
+ ///
+ protected bool checkMultiCondition(diCheckCondSetup reqCondition)
+ {
+ bool answ = false;
+ int numCondOk = 0;
+ int numCond = 0;
+ if (reqCondition.checkList != null && reqCondition.checkList.Count > 0)
+ {
+ numCond = reqCondition.checkList.Count;
+ // cerco nell'elenco delle condizioni che indicano lavora se sono ok faccio +1 conteggio......
+ foreach (var item in reqCondition.checkList)
+ {
+ if (string.IsNullOrEmpty(item.keyName))
+ {
+ lgError($"Attenzione: item vuoto in checkMultiCondition{Environment.NewLine}StackTrace: {Environment.StackTrace}");
+ }
+ else
+ {
+ if (getDataItemValue(item.keyName) == item.targetValue)
+ {
+ numCondOk++;
+ }
+ }
+ }
+ if (reqCondition.checkMode == boolCheckMode.AND)
+ {
+ answ = (numCond == numCondOk);
+ }
+ else if (reqCondition.checkMode == boolCheckMode.OR)
+ {
+ answ = numCondOk > 0;
+ }
+ }
+ else
+ {
+ answ = true;
+ }
+ // verifico se devo negare il valore...
+ answ = reqCondition.negateValue ? !answ : answ;
+ // restituisco
+ return answ;
+ }
+
+ ///
+ /// Verifica / Salva valore e restitusice SE sia variato (e quindi da inviare...)
+ ///
+ ///
+ ///
+ ///
+ ///
+ protected bool checkSaveValue(Opc.Ua.Client.MonitoredItem dataItem, string NotifyValue, bool sendItemList)
+ {
+ bool answ = !enableDataFilter;
+ double oldVal = 0;
+ double newVal = 0;
+ if (dataItem != null)
+ {
+ if (!string.IsNullOrEmpty(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;
+ if (dataItemMem.ContainsKey(uuid))
+ {
+ OpcUaDataItemExt currDataItemMem = dataItemMem[uuid];
+ // controllo SE SIA scaduto il tempo massimo...
+ if (Math.Abs(dataItemMem[uuid].valueTimestamp.Subtract(adesso).TotalSeconds) > currDataItemMem.samplePeriod)
+ {
+ answ = true;
+ }
+ else
+ {
+ // ALTRIMENTI controllo SE diverso
+ if (dataItemMem[uuid].value != $"{NotifyValue}")
+ {
+ lgInfo($"Val uuid: {dataItemMem[uuid].value} | NotifyValue: {NotifyValue}");
+ // controllo SE ho DeadBand...
+ if (dataItemMem[uuid].thresholdDeadBand > 0)
+ {
+ // recupero i valori e testo DeadBand...
+ bool isNum01 = double.TryParse(dataItemMem[uuid].value.Replace(".", ","), out oldVal);
+ bool isNum02 = double.TryParse($"{NotifyValue}".Replace(".", ","), out newVal);
+ // test deadband!
+ if (!(isNum01 && isNum02))
+ {
+ answ = true;
+ }
+ else
+ {
+ if (Math.Abs(newVal - oldVal) > dataItemMem[uuid].thresholdDeadBand)
+ {
+ // indico da salvare..
+ answ = true;
+ }
+ }
+ lgInfo($"Test deadband: oldVal: {oldVal} | newVal: {newVal}");
+ }
+ }
+ }
+ if (answ)
+ {
+ // salvo!
+ dataItemMem[uuid].value = $"{NotifyValue}";
+ dataItemMem[uuid].valueTimestamp = adesso;
+ }
+ }
+ else
+ {
+ // registro non trovato da aggiungere...
+ lgInfo($"DataItem non trovato in checkSaveValue: {dataItem.DisplayName}");
+ // provo a creare oggetto in memoria...
+ try
+ {
+ int dSamplePeriod = 0;
+ int threshDBand = 0;
+ uuid = "";
+ var currDataItem = formatDataItem(ref dSamplePeriod, ref threshDBand, ref uuid, dataItem);
+ // sistemo valore/periodo
+ currDataItem.value = $"{NotifyValue}";
+ currDataItem.valueTimestamp = adesso;
+ // aggiungo
+ dataItemMem.Add(uuid, currDataItem);
+ if (sendItemList)
+ {
+ sendDataItemListToServer(dataItem.StartNodeId);
+ }
+ }
+ catch (Exception exc)
+ {
+ lgError($"Eccezione in checkSaveSample{Environment.NewLine}{exc}");
+ }
+ }
+ }
+ else
+ {
+ lgError("Attenzione: checkSaveItem con Notify null!");
+ }
+ }
+ else
+ {
+ lgError("Attenzione: checkSaveItem con MonIt null!");
+ }
+ return answ;
+ }
+
+ ///
+ /// Effettua decodifica aree memoria alla bitmap usata x MAPO
+ ///
+ protected virtual 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
+ * B7: emergenza
+ ---------------------------------------------------- */
+
+ // se valido il check ping lo eseguo... altrimenti lo do x buono
+ bool checkPing = !opcUaParams.pingAsPowerOn;
+ string currRun = "";
+ if (!checkPing)
+ {
+ checkPing = (testPingMachine == IPStatus.Success);
+ }
+ // 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 > lastCurrentMaxElapsed --> disconnetto
+ if (!powerOnOk && adesso.Subtract(lastCurrent).TotalSeconds > lastCurrentMaxElapsed)
+ {
+ tryDisconnect();
+ }
+
+ // solo se non ho veto check
+ int vFactor = 2;
+ if (vetoCheckStatus < adesso)
+ {
+ lgTrace($"Stato variabili checkPing: {testPingMachine}");
+ // imposto veto per vetoSeconds...
+ vetoCheckStatus = adesso.AddSeconds(vetoSeconds * vFactor);
+ }
+
+ // se abilitato watchdog...
+ if (opcUaParams.WatchDog.IsEnabled)
+ {
+ lgTrace("WatchDog 01");
+ 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);
+ lgTrace("Effettuata scrittura WatchDog");
+ }
+ catch (Exception exc)
+ {
+ lgError($"Eccezione in gestione WatchDog, valore attuale {WatchDog}{Environment.NewLine}{exc}");
+ }
+ }
+ }
+ else
+ {
+ lgTrace("WatchDog disabilitato");
+ }
+
+ // log opzionale!
+ if (verboseLog)
+ {
+ lgDebug($"Trasformazione checkPing: {checkPing} | hasPowerOn: {hasPowerOn} | B_input: {B_input} | currRun = {currRun}");
+ }
+ }
+
+ protected byte[] getByteRaw(DataValue obj)
+ {
+ if (obj == null)
+ return null;
+
+ byte[] rawByte = new byte[1];
+ if (obj != null)
+ {
+ try
+ {
+ var wrapVal = ((DataValue)obj).WrappedValue;
+ //rawByte = ObjectToByteArray(rawVal);
+ var bodyVal = ((Opc.Ua.ExtensionObject)wrapVal.Value).Body;
+ rawByte = (byte[])bodyVal;
+ }
+ catch
+ { }
+ }
+ return rawByte;
+ }
+
+ ///
+ /// Effettua traduzione ITEM da LUT parametrica (key: tipo+id) del file di conf, se non
+ /// trovo uso key
+ ///
+ ///
+ ///
+ ///
+ protected string itemTranslation(string tipo, string id)
+ {
+ string answ = "";
+ string lemma = id;
+ if (!string.IsNullOrEmpty(tipo))
+ {
+ lemma = $"{tipo}_{id}";
+ }
+ // cerco nel dizionario delle traduzioni SE esiste un valore e prendo quello, altrimenti
+ // uso il lemma...
+ if (opcUaParams.itemTranslation.ContainsKey(lemma))
+ {
+ answ = opcUaParams.itemTranslation[lemma];
+ }
+ else
+ {
+ answ = lemma;
+ }
+ return answ;
+ }
+
+ ///
+ /// Effettua lettura file di conf specifico OPC-UA da oggetto serializzato json Nome file da cui leggere i parametri json
+ ///
+ protected void loadOpcUaConf(string fileName)
+ {
+ string jsonFullPath = $"{Application.StartupPath}/DATA/CONF/{fileName}";
+ lgInfo($"Apertura file {jsonFullPath}");
+ using (StreamReader reader = new StreamReader(jsonFullPath))
+ {
+ string jsonData = reader.ReadToEnd().Replace("\n", "").Replace("\r", "");
+ if (!string.IsNullOrEmpty(jsonData))
+ {
+ lgDebug($"File json composto da {jsonData.Length} caratteri");
+ try
+ {
+ opcUaParams = JsonConvert.DeserializeObject(jsonData);
+ lgDebug($"Decodifica aree OpcUaParamConf: trovati {opcUaParams.paramsEndThresh.Count} valori paramsEndThresh");
+ // sistemo se ci sono dati memMap...
+ memMap = new plcMemMap();
+ if (opcUaParams.mMapWrite != null)
+ {
+ memMap.mMapWrite = opcUaParams.mMapWrite;
+ }
+ if (opcUaParams.mMapRead != null)
+ {
+ memMap.mMapRead = opcUaParams.mMapRead;
+ }
+ setupMemMap();
+ }
+ catch (Exception exc)
+ {
+ lgError($"Eccezione in decodifica conf json OPC-UA:{Environment.NewLine}{exc}");
+ }
+ }
+ else
+ {
+ lgError("Errore in loadOpcUaConf: file json vuoto!");
+ }
+ }
+ //reader.Dispose();
+ }
+
+ #endregion Protected Methods
+
+ #region Private Fields
+
+ ///
+ /// Elenco degli items da monitorare come risultato del browse iniziale
+ ///
+ private Dictionary selectedItemList = new Dictionary();
+
+ #endregion Private Fields
+
+ #region Private Methods
+
+ ///
+ /// Vera connessione ad OpcUa
+ ///
+ ///
+ private async Task doConnect()
+ {
+ short esitoLink = 0;
+ // reset memoria dataItem..
+ dataItemMem = new Dictionary();
+ // predisposizione conf oggetto di comunicazione MTC
+ int port = 4840;
+ int.TryParse(cIobConf.cncPort, out port);
+ // ora avvio
+ try
+ {
+ lgInfo("Start init OpcUa Client");
+ // Define the UA Client application
+ ApplicationInstance application = new ApplicationInstance();
+ application.ApplicationName = "Steamware IOB-WIN Client";
+ application.ApplicationType = ApplicationType.Client;
+
+ // load the application configuration.
+ string confPath = $"{Application.StartupPath}\\DATA\\CONF\\IobOpcUaClient.Config.xml";
+ await application.LoadApplicationConfiguration(confPath, silent: false).ConfigureAwait(false);
+ // check the application certificate.
+ await application.CheckApplicationInstanceCertificate(silent: false, minimumKeySize: 0).ConfigureAwait(false);
+
+ lgInfo($"Chiamata UAClient con configurazione standard: {application.ApplicationConfiguration.ApplicationName}");
+ UA_ref = new UAClient(application.ApplicationConfiguration, cIobConf.codIOB, opcUaParams.Identity.UserName, opcUaParams.Identity.Passwd, isVerboseLog, ClientBase.ValidateResponse);
+
+ lgInfo($"Chiamata apertura OpcUa Client: {cIobConf.cncIpAddr}:{port}");
+ UA_ref.ServerUrl = $"opc.tcp://{cIobConf.cncIpAddr}:{port}";
+
+ var task = Task.Run(async () =>
+ {
+ return await UA_ref.ConnectAsync().ConfigureAwait(false);
+ });
+ bool connected = task.Result;
+ if (connected)
+ {
+ // faccio un primo browse dei dati...
+ Dictionary nodeIdNameList = new Dictionary();
+ if (!string.IsNullOrEmpty(opcUaParams.BrowseFullVal))
+ {
+ try
+ {
+ UA_ref.Browse(opcUaParams.BrowseFullVal, opcUaParams.filterItemsNodeId, ref nodeIdNameList);
+ }
+ catch
+ { }
+ }
+ else
+ {
+ UA_ref.Browse(opcUaParams.BrowseNSIndex, opcUaParams.BrowseValue, opcUaParams.filterItemsNodeId, ref nodeIdNameList);
+ }
+ // loggo elenco degli item sottocrivibili...
+ lgDebug("---------- AVAILABLE FOR SUBSCRIBE ----------");
+ foreach (var item in nodeIdNameList)
+ {
+ lgDebug(item.Key);
+ }
+ lgDebug("---------- END LIST ----------");
+
+ // se ho un insieme non vuoto degli item sottoscritti carico solo quelli
+ if (opcUaParams.subscribedItems != null && opcUaParams.subscribedItems.Count > 0)
+ {
+ // cerco e aggiungo SOLO quelle indicati
+ foreach (var currItem in opcUaParams.subscribedItems)
+ {
+ var foundItems = nodeIdNameList.Where(x => x.Key.Contains(currItem)).ToList();
+ if (foundItems != null && foundItems.Count > 0)
+ {
+ foreach (var fItem in foundItems)
+ {
+ // verifico di NON duplicare...
+ if (!selectedItemList.ContainsKey(fItem.Key))
+ {
+ selectedItemList.Add(fItem.Key, fItem.Value);
+ }
+ }
+ }
+ else
+ {
+ lgDebug($"subscribedItems non trovato: {currItem}");
+ }
+ }
+ lgDebug($"Aggiunti {selectedItemList.Count} items!");
+ }
+ // altrimenti tutti!
+ else
+ {
+ selectedItemList = nodeIdNameList;
+ }
+
+ // loggo elenco degli item sottocrivibili...
+ lgInfo("---------- SUBSCRIBED NODES ----------");
+ foreach (var item in selectedItemList)
+ {
+ lgInfo(item.Key);
+ }
+ lgInfo("---------- END LIST ----------");
+
+ // sottoscrivo a rilevazione cambio dati solo l'incrocio degli insiemi
+ List subscribedItems = UA_ref.SubscribeToDataChanges(selectedItemList);
+ // aggiungo come DataItems
+ int dSamplePeriod = 0;
+ int threshDBand = 0;
+ string uuid = "";
+ foreach (var item in subscribedItems)
+ {
+ bool changed = false;
+ OpcUaDataItemExt newItem = formatDataItem(ref dSamplePeriod, ref threshDBand, ref uuid, item);
+ // controllo non sia già stato aggiunto
+ if (dataItemMem.ContainsKey(uuid))
+ {
+ lgDebug($"Item ALREADY subscribed: {uuid} | NOT re-adding");
+ }
+ else
+ {
+ dataItemMem.Add(uuid, newItem);
+ lgDebug($"Item subscribed: {uuid}");
+ // verifico se ho i dati complessi by design
+ string currVal = "";
+ if (doByteRead)
+ {
+ // restituisce i 115 byte da deserializzare... qui HACK!
+ var rawVal = UA_ref.ReadNodeRaw(item.StartNodeId);
+ if (rawVal != null)
+ {
+ byteRawData = getByteRaw((DataValue)rawVal);
+ changed = checkAndSendRaw(item, byteRawData, true);
+ }
+ }
+ else
+ {
+ currVal = UA_ref.ReadNode(item.StartNodeId);
+ changed = checkAndSend(item, currVal, true);
+ }
+ }
+ }
+ // gestione eventi change
+ UA_ref.eh_MonItChange += UA_ref_eh_MonItChange;
+ lgInfo("eh_MonItChange event registered");
+ }
+
+ esitoLink = 1;
+
+ // fix tempi!
+ DateTime adesso = DateTime.Now;
+ lastPzCountSend = adesso;
+ lastWarnODL = adesso;
+ lastCurrent = adesso;
+ }
+ catch (Exception exc)
+ {
+ lgError($"Eccezione in doConnect{Environment.NewLine}{exc}");
+ }
+ return esitoLink;
+ }
+
+ ///
+ /// Formatta un dataitem x salvataggio in memoria locale
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private OpcUaDataItemExt formatDataItem(ref int dSamplePeriod, ref int threshDBand, ref string uuid, Opc.Ua.Client.MonitoredItem dataItem)
+ {
+ OpcUaDataItemExt currDataItem;
+ // calcolo parametri
+ uuid = $"{dataItem.DisplayName}";
+ // SOLO SE è abilitato il datafiltering...
+ if (enableDataFilter)
+ {
+ threshDBand = 1;
+ // controllo SE ho conf x deadband...
+ if (opcUaParams.paramsEndThresh.Count > 0)
+ {
+ // ciclo su tutti i parametri indicati...
+ foreach (var item in opcUaParams.paramsEndThresh)
+ {
+ if (uuid.EndsWith(item.Key))
+ {
+ threshDBand = item.Value;
+ }
+ }
+ }
+ }
+ else
+ {
+ threshDBand = 0;
+ }
+ dSamplePeriod = 60;
+
+ // salvo oggetto x "uso interno"
+ currDataItem = new OpcUaDataItemExt(dataItem)
+ {
+ uid = uuid,
+ thresholdDeadBand = threshDBand,
+ samplePeriod = dSamplePeriod
+ };
+
+ return currDataItem;
+ }
+
+ ///
+ /// Effettua invio a MP/IO dell'elenco serializzato dei dataItems
+ ///
+ ///
+ private void sendDataItemsList(List dataItems)
+ {
+ string rawData = JsonConvert.SerializeObject(dataItems);
+ try
+ {
+ utils.callUrlNow($"{urlSaveDataItems}", rawData);
+ }
+ catch (Exception exc)
+ {
+ lgError($"Eccezione in sendDataItemsList{Environment.NewLine} - url: {urlSaveDataItems}{Environment.NewLine}- payload:{rawData}{Environment.NewLine}Eccezione:{Environment.NewLine}{exc}");
+ }
+ }
+
+ #endregion Private Methods
}
}
\ No newline at end of file
diff --git a/IOB-WIN-NEXT/IobOpcUaOmronIcoel.cs b/IOB-WIN-NEXT/IobOpcUaOmronIcoel.cs
index 3afd1935..49039eb3 100644
--- a/IOB-WIN-NEXT/IobOpcUaOmronIcoel.cs
+++ b/IOB-WIN-NEXT/IobOpcUaOmronIcoel.cs
@@ -1,12 +1,9 @@
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
{
@@ -17,127 +14,10 @@ namespace IOB_WIN_NEXT
Manuale = 2,
Automatico = 3
}
- public class MesItemStatus
- {
- public IcoelStatus Stato { get; set; } = 0;
- public UInt16 Velocita { get; set; } = 0;
- public bool Termico { get; set; } = false;
- public bool MagnetoTermico { get; set; } = false;
- public bool AvariaInverter { get; set; } = false;
-
- public MesItemStatus(byte[] rawData)
- {
- Stato = (IcoelStatus)BitConverter.ToUInt16(rawData, 0);
- Velocita = BitConverter.ToUInt16(rawData, 2);
- Termico = !rawData.Skip(4).Take(1).FirstOrDefault().Equals(0);
- MagnetoTermico = !rawData.Skip(5).Take(1).FirstOrDefault().Equals(0);
- AvariaInverter = !rawData.Skip(6).Take(1).FirstOrDefault().Equals(0);
- }
-
- ///
- /// Converte un singolo item in un array di byte per scrittura su PLC S7
- ///
- ///
- public byte[] serialize()
- {
- byte[] answ = new byte[7];
- Buffer.BlockCopy(BitConverter.GetBytes((short)Stato), 0, answ, 0, 2);
- Buffer.BlockCopy(BitConverter.GetBytes(Velocita), 0, answ, 2, 2);
- Buffer.BlockCopy(BitConverter.GetBytes(Termico), 0, answ, 4, 1);
- Buffer.BlockCopy(BitConverter.GetBytes(MagnetoTermico), 0, answ, 5, 1);
- Buffer.BlockCopy(BitConverter.GetBytes(AvariaInverter), 0, answ, 6, 1);
- return answ;
- }
-
-
- public override bool Equals(object obj)
- {
- // Object is not a GaugeModel instance
- if (!(obj is MesItemStatus item))
- return false;
-
- if (Stato != item.Stato)
- return false;
- if (Velocita != item.Velocita)
- return false;
- if (Termico != item.Termico)
- return false;
- if (MagnetoTermico != item.MagnetoTermico)
- return false;
- if (AvariaInverter != item.AvariaInverter)
- return false;
-
- return true;
- }
-
- public override int GetHashCode()
- {
- return base.GetHashCode();
- }
-
- }
-
- public class PlantStatus
- {
- public bool InMarcia { get; set; } = false;
- public bool InEmergenza { get; set; } = false;
- public bool InStop { get; set; } = false;
- public PlantStatus(byte[] rawData)
- {
- InMarcia = !rawData.Skip(0).Take(1).FirstOrDefault().Equals(0);
- InEmergenza = !rawData.Skip(1).Take(1).FirstOrDefault().Equals(0);
- InStop = !rawData.Skip(2).Take(1).FirstOrDefault().Equals(0);
-
- //var valore = BitConverter.ToUInt16(rawData, 0);
-
-
- //byte b = rawData.Skip(0).Take(1).FirstOrDefault();
- //InMarcia = (b & (1 << (1 - 1))) != 0;
- //InEmergenza = (b & (1 << (2 - 1))) != 0;
- //InStop = (b & (1 << (3 - 1))) != 0; ;
- }
-
- public override bool Equals(object obj)
- {
- // Object is not a GaugeModel instance
- if (!(obj is PlantStatus item))
- return false;
-
- if (InMarcia != item.InMarcia)
- return false;
- if (InEmergenza != item.InEmergenza)
- return false;
- if (InStop != item.InStop)
- return false;
-
- return true;
- }
-
- public override int GetHashCode()
- {
- return base.GetHashCode();
- }
- }
public class DatiMesIcoel
{
- public MesItemStatus Calibratrice_L1 { get; set; }
- public MesItemStatus Calibratrice_L2 { get; set; }
- public MesItemStatus Dewatering_L1 { get; set; }
- public MesItemStatus Dewatering_L2 { get; set; }
- public MesItemStatus Precalibro_L1 { get; set; }
- public MesItemStatus Precalibro_L2 { get; set; }
- public MesItemStatus Taglierina_L1 { get; set; }
- public MesItemStatus Taglierina_L2 { get; set; }
- public MesItemStatus NastroTaglierina_L1 { get; set; }
- public MesItemStatus NastroTaglierina_L2 { get; set; }
- public MesItemStatus Elevatore_L1 { get; set; }
- public MesItemStatus Elevatore_L2 { get; set; }
- public MesItemStatus ImmergitoreBins_L1 { get; set; }
- public MesItemStatus ImmergitoreBins_L2 { get; set; }
- public MesItemStatus ImmergitoreCasse_L1 { get; set; }
- public MesItemStatus ImmergitoreCasse_L2 { get; set; }
- public PlantStatus Varie { get; set; }
+ #region Public Constructors
///
/// Inizializzazione classe con dati RAW da byte[]
@@ -169,6 +49,31 @@ namespace IOB_WIN_NEXT
}
}
+ #endregion Public Constructors
+
+ #region Public Properties
+
+ public MesItemStatus Calibratrice_L1 { get; set; }
+ public MesItemStatus Calibratrice_L2 { get; set; }
+ public MesItemStatus Dewatering_L1 { get; set; }
+ public MesItemStatus Dewatering_L2 { get; set; }
+ public MesItemStatus Elevatore_L1 { get; set; }
+ public MesItemStatus Elevatore_L2 { get; set; }
+ public MesItemStatus ImmergitoreBins_L1 { get; set; }
+ public MesItemStatus ImmergitoreBins_L2 { get; set; }
+ public MesItemStatus ImmergitoreCasse_L1 { get; set; }
+ public MesItemStatus ImmergitoreCasse_L2 { get; set; }
+ public MesItemStatus NastroTaglierina_L1 { get; set; }
+ public MesItemStatus NastroTaglierina_L2 { get; set; }
+ public MesItemStatus Precalibro_L1 { get; set; }
+ public MesItemStatus Precalibro_L2 { get; set; }
+ public MesItemStatus Taglierina_L1 { get; set; }
+ public MesItemStatus Taglierina_L2 { get; set; }
+ public PlantStatus Varie { get; set; }
+
+ #endregion Public Properties
+
+ #region Public Methods
public override bool Equals(object obj)
{
@@ -218,17 +123,193 @@ namespace IOB_WIN_NEXT
{
return base.GetHashCode();
}
+
+ #endregion Public Methods
}
public class IobOpcUaOmronIcoel : IobOpcUaOmron
{
- #region Protected Fields
-
-
- #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 IobOpcUaOmronIcoel(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;
+ doByteRead = true;
+ lgInfo($"Avviato IobOpcUaOmronIcoel | encodeReadData: {doByteRead}");
+ }
+
+ #endregion Public Constructors
+
+ #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 false
+ 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");
+ }
+ }
+ }
+#endif
+ }
+
+ ///
+ /// 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
+
+ #region Internal Methods
+
+ ///
+ /// Verifica ed invia variazioni DAL FORMATO RAW data (byte[]) --> esplode oggetti e li
+ /// testa 1:1
+ ///
+ ///
+ ///
+ ///
+ internal override bool checkAndSendRaw(Opc.Ua.Client.MonitoredItem MonIt, byte[] NotifyValue, bool forceSend)
+ {
+ bool changed = false;
+ if (MonIt != null)
+ {
+ if (NotifyValue != null && NotifyValue.Length > 0)
+ {
+ // verifico variazione "globale"
+ if (!lastData.Equals(currData))
+ {
+ // effettuo test/invio x ogni info
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "Calibratrice_L1", lastData.Calibratrice_L1, currData.Calibratrice_L1, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "Calibratrice_L2", lastData.Calibratrice_L2, currData.Calibratrice_L2, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "Dewatering_L1", lastData.Dewatering_L1, currData.Dewatering_L1, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "Dewatering_L2", lastData.Dewatering_L2, currData.Dewatering_L2, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "Elevatore_L1", lastData.Elevatore_L1, currData.Elevatore_L1, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "Elevatore_L2", lastData.Elevatore_L2, currData.Elevatore_L2, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "ImmergitoreBins_L1", lastData.ImmergitoreBins_L1, currData.ImmergitoreBins_L1, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "ImmergitoreBins_L2", lastData.ImmergitoreBins_L2, currData.ImmergitoreBins_L2, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "ImmergitoreCasse_L1", lastData.ImmergitoreCasse_L1, currData.ImmergitoreCasse_L1, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "ImmergitoreCasse_L2", lastData.ImmergitoreCasse_L2, currData.ImmergitoreCasse_L2, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "NastroTaglierina_L1", lastData.NastroTaglierina_L1, currData.NastroTaglierina_L1, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "NastroTaglierina_L2", lastData.NastroTaglierina_L2, currData.NastroTaglierina_L2, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "Precalibro_L1", lastData.Precalibro_L1, currData.Precalibro_L1, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "Precalibro_L2", lastData.Precalibro_L2, currData.Precalibro_L2, forceSend);
+ changed = changed || testSendDataBlock(MonIt.StartNodeId, "Taglierina_L1", lastData.Taglierina_L1, currData.Taglierina_L1, forceSend);
+ changed = changed || 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");
+ }
+ return changed;
+ }
+
+ internal override void UA_ref_eh_MonItChange(object sender, opcUaMonitItemChange e)
+ {
+ base.UA_ref_eh_MonItChange(sender, e);
+ }
+
+ #endregion Internal Methods
+
+ #region Protected Properties
+
///
/// Valore corrente dei dati ICOEL (traduzione JIT da byte[])
///
@@ -250,139 +331,49 @@ namespace IOB_WIN_NEXT
}
}
+ ///
+ /// Indica se abbia stato POWER ON (multicondizione)
+ ///
+ protected override bool hasPowerOn
+ {
+ get
+ {
+ // da rivedere
+ return true;
+ //return checkMultiCondition(opcUaParams.condPowerOn);
+ }
+ }
+
+ ///
+ /// Indica se abbia stato MANUAL (condizioni varie, es stopped)
+ ///
+ protected override bool isManual
+ {
+ get
+ {
+ return false;
+ //return checkMultiCondition(opcUaParams.condManual);
+ }
+ }
+
+ ///
+ /// Indica se abbia stato READY (condizioni varie, es ausiliari OK)
+ ///
+ protected override bool isReady
+ {
+ get
+ {
+ return false;
+ //return checkMultiCondition(opcUaParams.condReady);
+ }
+ }
+
///
/// 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
- ///
- ///
- ///
- public IobOpcUaOmronIcoel(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;
- doByteRead = true;
- lgInfo($"Avviato IobOpcUaOmronIcoel | encodeReadData: {doByteRead}");
- }
- ///
- /// Verifico se salvare e inviare proprietà specificata
- ///
- ///
- protected void testSendProperty(Opc.Ua.Client.MonitoredItem MonIt, string NotifyValue, bool forceSend)
- {
- 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)
- {
- lgTrace($"TSP | NON ACCODATO sample per {MonIt.DisplayName} - trovato VETO in fluxLogVeto", false);
- }
- else
- {
- if (changed || forceSend)
- {
- accodaFLog(sVal, qEncodeFLog(descr, $"{NotifyValue}"));
- }
- else
- {
- lgTrace($"TSP | NON ACCODATO sample per {MonIt.DisplayName} - verifica variazione ha dato esito negativo", false);
- }
- }
- }
-
- ///
- /// 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
+ #endregion Protected Properties
#region Protected Methods
@@ -460,150 +451,204 @@ namespace IOB_WIN_NEXT
}
}
-
-
- ///
- /// Indica se abbia stato POWER ON (multicondizione)
- ///
- protected override bool hasPowerOn
- {
- get
- {
- // da rivedere
- return true;
- //return checkMultiCondition(opcUaParams.condPowerOn);
- }
- }
-
- ///
- /// Indica se abbia stato MANUAL (condizioni varie, es stopped)
- ///
- protected override bool isManual
- {
- get
- {
- return false;
- //return checkMultiCondition(opcUaParams.condManual);
- }
- }
-
- ///
- /// Indica se abbia stato READY (condizioni varie, es ausiliari OK)
- ///
- protected override bool isReady
- {
- get
- {
- return false;
- //return checkMultiCondition(opcUaParams.condReady);
- }
- }
-
///
/// Effettua vera scrittura parametri
///
///
protected override void plcWriteParams(ref List updatedPar)
{
+ }
+ ///
+ /// effettua verifica del datablock icoel invianod eventualmente i dati variati
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ protected bool testSendDataBlock(NodeId startNodeId, string blockName, MesItemStatus currBlock, MesItemStatus newBlock, bool forceSend)
+ {
+ bool changed = false;
+ // verifica globale blocchi old/new...
+ if (!currBlock.Equals(newBlock))
+ {
+ // creo un nuovo monitoredItem se non ci fosse x ogni variabile dell'oggetto...
+ changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Stato", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Stato}", forceSend);
+ changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Velocita", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Velocita}", forceSend);
+ changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_Termico", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.Termico}", forceSend);
+ changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_MagnetoTermico", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.MagnetoTermico}", forceSend);
+ changed = changed || testSendProperty(new Opc.Ua.Client.MonitoredItem() { DisplayName = $"{blockName}_AvariaInverter", NodeClass = NodeClass.Variable, StartNodeId = startNodeId }, $"{newBlock.AvariaInverter}", forceSend);
+
+ // spostare sotto in checkAndSendRaw ??? FIXME todo
+ if (changed)
+ {
+ sendDataItemListToServer(startNodeId);
+ }
+ }
+ return changed;
+ }
+
+ ///
+ /// Verifico se salvare e inviare proprietà specificata
+ ///
+ ///
+ protected bool testSendProperty(Opc.Ua.Client.MonitoredItem MonIt, string NotifyValue, bool forceSend)
+ {
+ bool changed = false;
+ 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}");
+
+ changed = checkSaveValue(MonIt, NotifyValue, false);
+ // cerco se non sia un dato filtrato in FLUXLOG...
+ bool isFiltered = opcUaParams.fluxLogVeto.Contains(MonIt.DisplayName);
+ if (isFiltered)
+ {
+ lgTrace($"TSP | NON ACCODATO sample per {MonIt.DisplayName} - trovato VETO in fluxLogVeto", false);
+ }
+ else
+ {
+ if (changed || forceSend)
+ {
+ accodaFLog(sVal, qEncodeFLog(descr, $"{NotifyValue}"));
+ }
+ else
+ {
+ lgTrace($"TSP | NON ACCODATO sample per {MonIt.DisplayName} - verifica variazione ha dato esito negativo", false);
+ }
+ }
+ return changed;
}
#endregion Protected Methods
+ }
+
+ public class MesItemStatus
+ {
+ #region Public Constructors
+
+ public MesItemStatus(byte[] rawData)
+ {
+ Stato = (IcoelStatus)BitConverter.ToUInt16(rawData, 0);
+ Velocita = BitConverter.ToUInt16(rawData, 2);
+ Termico = !rawData.Skip(4).Take(1).FirstOrDefault().Equals(0);
+ MagnetoTermico = !rawData.Skip(5).Take(1).FirstOrDefault().Equals(0);
+ AvariaInverter = !rawData.Skip(6).Take(1).FirstOrDefault().Equals(0);
+ }
+
+ #endregion Public Constructors
+
+ #region Public Properties
+
+ public bool AvariaInverter { get; set; } = false;
+ public bool MagnetoTermico { get; set; } = false;
+ public IcoelStatus Stato { get; set; } = 0;
+ public bool Termico { get; set; } = false;
+ public UInt16 Velocita { get; set; } = 0;
+
+ #endregion Public Properties
#region Public Methods
- ///
- /// Processo i task richiesti e li elimino dalla coda 1:1
- ///
- ///
- public override Dictionary executeTasks(Dictionary task2exe)
+ public override bool Equals(object obj)
{
- // uso metodo base x ora
- return base.executeTasks(task2exe);
+ // Object is not a GaugeModel instance
+ if (!(obj is MesItemStatus item))
+ return false;
+
+ if (Stato != item.Stato)
+ return false;
+ if (Velocita != item.Velocita)
+ return false;
+ if (Termico != item.Termico)
+ return false;
+ if (MagnetoTermico != item.MagnetoTermico)
+ return false;
+ if (AvariaInverter != item.AvariaInverter)
+ return false;
+
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
}
///
- /// Effettua vero processing contapezzi
- ///
- public override void processContapezzi()
- {
-#if false
- 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");
- }
- }
- }
-#endif
- }
-
- ///
- /// Effettua reset del contapezzi, NON POSSIBILE in questa versione
+ /// Converte un singolo item in un array di byte per scrittura su PLC S7
///
///
- public override bool resetcontapezziPLC()
+ public byte[] serialize()
{
- bool answ = false;
- return answ;
- }
-
- ///
- /// Effettua IMPOSTAZIONE FORZATA del contapezzi, NON POSSIBILE in questa versione
- ///
- ///
- public override bool setcontapezziPLC(int newPzCount)
- {
- bool answ = false;
+ byte[] answ = new byte[7];
+ Buffer.BlockCopy(BitConverter.GetBytes((short)Stato), 0, answ, 0, 2);
+ Buffer.BlockCopy(BitConverter.GetBytes(Velocita), 0, answ, 2, 2);
+ Buffer.BlockCopy(BitConverter.GetBytes(Termico), 0, answ, 4, 1);
+ Buffer.BlockCopy(BitConverter.GetBytes(MagnetoTermico), 0, answ, 5, 1);
+ Buffer.BlockCopy(BitConverter.GetBytes(AvariaInverter), 0, answ, 6, 1);
return answ;
}
#endregion Public Methods
}
+
+ public class PlantStatus
+ {
+ #region Public Constructors
+
+ public PlantStatus(byte[] rawData)
+ {
+ InMarcia = !rawData.Skip(0).Take(1).FirstOrDefault().Equals(0);
+ InEmergenza = !rawData.Skip(1).Take(1).FirstOrDefault().Equals(0);
+ InStop = !rawData.Skip(2).Take(1).FirstOrDefault().Equals(0);
+
+ //var valore = BitConverter.ToUInt16(rawData, 0);
+
+ //byte b = rawData.Skip(0).Take(1).FirstOrDefault();
+ //InMarcia = (b & (1 << (1 - 1))) != 0;
+ //InEmergenza = (b & (1 << (2 - 1))) != 0;
+ //InStop = (b & (1 << (3 - 1))) != 0; ;
+ }
+
+ #endregion Public Constructors
+
+ #region Public Properties
+
+ public bool InEmergenza { get; set; } = false;
+ public bool InMarcia { get; set; } = false;
+ public bool InStop { get; set; } = false;
+
+ #endregion Public Properties
+
+ #region Public Methods
+
+ public override bool Equals(object obj)
+ {
+ // Object is not a GaugeModel instance
+ if (!(obj is PlantStatus item))
+ return false;
+
+ if (InMarcia != item.InMarcia)
+ return false;
+ if (InEmergenza != item.InEmergenza)
+ return false;
+ if (InStop != item.InStop)
+ return false;
+
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
+ }
+
+ #endregion Public Methods
+ }
}
\ No newline at end of file