Files
Mapo-IOB-WIN/IOB-WIN-NEXT/UAClient.cs
T
2024-11-29 12:10:05 +01:00

1863 lines
80 KiB
C#

using NLog;
using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IOB_WIN_NEXT
{
/// <summary>
/// Evento per incapsulare dati x refresh pagina
/// </summary>
public class opcUaMonitItemChange : EventArgs
{
#region Public Constructors
/// <summary>
/// salvataggio obj
/// </summary>
/// <param name="newObject"></param>
public opcUaMonitItemChange(MonitoredItem monitoredItem, MonitoredItemNotification notification)
{
_monitoredItem = monitoredItem;
_notification = notification;
}
#endregion Public Constructors
#region Public Properties
/// <summary>
/// Proprietà lettura del MonitoredItem
/// </summary>
public MonitoredItem CurrMonitoredItem
{
get { return _monitoredItem; }
}
/// <summary>
/// Proprietà lettura della notifica
/// </summary>
public MonitoredItemNotification CurrNotify
{
get { return _notification; }
}
#endregion Public Properties
#region Private Fields
/// <summary>
/// Monitored Item da notificare
/// </summary>
private readonly MonitoredItem _monitoredItem;
/// <summary>
/// Valore notifica
/// </summary>
private readonly MonitoredItemNotification _notification;
#endregion Private Fields
}
/// <summary>
/// OPC UA Client with examples of basic functionality.
/// </summary>
public class UAClient
{
#region Public Constructors
/// <summary>
/// Initializes a new instance of the UAClient class.
/// </summary>
/// <param name="configuration"></param>
/// <param name="codIOB"></param>
/// <param name="user"></param>
/// <param name="pwd"></param>
/// <param name="verboseLog"></param>
/// <param name="validateResponse"></param>
/// <param name="maxNullRead">Numero massimo di risposte nulle in browsing prima di disconnettersi</param>
public UAClient(ApplicationConfiguration configuration, string codIOB, string user, string pwd, bool verboseLog, Action<IList, IList> validateResponse, int maxNullRead)
{
currNullReceiv = 0;
maxNullAllowed = maxNullRead;
m_validateResponse = validateResponse;
currIob = codIOB;
lg = LogManager.GetCurrentClassLogger();
if (!string.IsNullOrEmpty(user) && !string.IsNullOrEmpty(pwd))
{
CurrUserIdentity = new UserIdentity(user, pwd);
}
else
{
CurrUserIdentity = new UserIdentity();
}
isLogVerbose = verboseLog;
m_configuration = configuration;
m_configuration.CertificateValidator.CertificateValidation += CertificateValidation;
}
#endregion Public Constructors
#region Public Events
/// <summary>
/// Evento notifica variazione MonitoredItem
/// </summary>
public event EventHandler<opcUaMonitItemChange> eh_MonItChange;
/// <summary>
/// Evento notifica superamento numero letture null
/// </summary>
public event EventHandler<int> eh_nullExceed;
#endregion Public Events
#region Public Properties
/// <summary>
/// The user identity to use when creating the session.
/// </summary>
public IUserIdentity CurrUserIdentity { get; set; } = new UserIdentity();
/// <summary>
/// Gets or sets the server URL.
/// </summary>
public string ServerUrl { get; set; } = "opc.tcp://localhost:4840";
/// <summary>
/// Gets the client session.
/// </summary>
public Session Session => m_session;
#endregion Public Properties
#region Public Methods
/// <summary>
/// Browse Server nodes
/// </summary>
public bool Browse(ushort startNodeNS, uint startNodeVal, List<string> vetoBrowse, ref Dictionary<string, string> nodeIdNameList)
{
bool fatto = false;
if (m_session == null || m_session.Connected == false)
{
lgError("Session not connected!");
return false;
}
try
{
// Create a Browser object
Browser browser = new Browser(m_session);
// Set browse parameters
browser.BrowseDirection = BrowseDirection.Forward;
browser.NodeClassMask = (int)NodeClass.Object | (int)NodeClass.Variable;
browser.ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences;
//NodeId nodeToBrowse = ObjectIds.Server;
//NodeId nodeToBrowse = new NodeId("ns=4,i=5001");
NodeId nodeToBrowse = new NodeId(startNodeVal, startNodeNS);
// Call Browse service
lgTrace($"Browsing {nodeToBrowse} node...");
ReferenceDescriptionCollection browseResults = browser.Browse(nodeToBrowse);
// Display the results
lgTrace($"Browse returned {browseResults.Count} results:");
foreach (ReferenceDescription result in browseResults)
{
lgTrace($" NodeId = {result.NodeId}, TypeId = {result.TypeId}, DisplayName = {result.DisplayName.Text}, NodeClass = {result.NodeClass}, Others: {result.BinaryEncodingId} | {result.BrowseName}");
// se NON fa parte dell'elenco dei VETO di filterItems...
if (!vetoBrowse.Contains($"{result.NodeId}"))
{
// se mancasse aggiungo...
if (!nodeIdNameList.ContainsKey($"{result.NodeId}"))
{
nodeIdNameList.Add(result.NodeId.ToString(), result.DisplayName.Text);
}
}
}
fatto = true;
}
catch (Exception ex)
{
// Log Error
lgError($"Browse Error : {ex.Message}");
}
return fatto;
}
/// <summary>
/// Browse Server nodes
/// </summary>
public bool Browse(string browsePath, List<string> vetoBrowse, ref Dictionary<string, string> nodeIdNameList)
{
bool fatto = false;
if (m_session == null || m_session.Connected == false)
{
lgError("Session not connected!");
return false;
}
try
{
// Create a Browser object
Browser browser = new Browser(m_session);
// Set browse parameters
browser.BrowseDirection = BrowseDirection.Forward;
browser.NodeClassMask = (int)NodeClass.Object | (int)NodeClass.Variable;
browser.ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences;
//NodeId nodeToBrowse = ObjectIds.Server;
//NodeId nodeToBrowse = new NodeId("ns=4;i=5001");
NodeId nodeToBrowse = new NodeId(browsePath);
//nodeToBrowse = ObjectIds.Server;
//nodeToBrowse = new NodeId("Calibratrice_L1", 4);
//nodeToBrowse = new NodeId("Dati_Mes", 4);
//nodeToBrowse = new NodeId(5001, 2);
//nodeToBrowse = new NodeId("ns=4;s=NxController");
//nodeToBrowse = new NodeId("ns=4;s=Dati_Mes");
//nodeToBrowse = new NodeId("NxController.GlobalVars", 4);
//nodeToBrowse = new NodeId("Dati_Mes", 4);
// Call Browse service
lgTrace($"Browsing {nodeToBrowse} node...");
ReferenceDescriptionCollection browseResults = browser.Browse(nodeToBrowse);
// Display the results
lgTrace($"Browse returned {browseResults.Count} results:");
foreach (ReferenceDescription result in browseResults)
{
// se veto --> loggo veto
if (vetoBrowse.Contains($"{result.NodeId}"))
{
lgDebug($"FILTER --> NodeId = {result.NodeId}, DisplayName = {result.DisplayName.Text}, NodeClass = {result.NodeClass}, Others: {result.BinaryEncodingId} | {result.BrowseName}");
}
// se NON fa parte dell'elenco dei VETO di filterItems...
else
{
lgDebug($" NodeId = {result.NodeId}, DisplayName = {result.DisplayName.Text}, NodeClass = {result.NodeClass}, Others: {result.BinaryEncodingId} | {result.BrowseName}");
// se mancasse aggiungo...
if (!nodeIdNameList.ContainsKey($"{result.NodeId}"))
{
nodeIdNameList.Add($"{result.NodeId}", result.DisplayName.Text);
// se è un nodo object --> faccio sub browse!
if (result.NodeClass != NodeClass.Variable)
{
this.Browse($"{result.NodeId}", vetoBrowse, ref nodeIdNameList);
}
}
}
}
fatto = true;
}
catch (Exception ex)
{
// Log Error
lgError($"Browse Error : {ex.Message}");
}
return fatto;
}
/// <summary>
/// Call UA method
/// </summary>
public void CallMethod()
{
if (m_session == null || m_session.Connected == false)
{
lgError("Session not connected!");
return;
}
try
{
// Define the UA Method to call Parent node - Objects\CTT\Methods Method node - Objects\CTT\Methods\Add
NodeId objectId = new NodeId("ns=2;s=Methods");
NodeId methodId = new NodeId("ns=2;s=Methods_Add");
// Define the method parameters Input argument requires a Float and an UInt32 value
object[] inputArguments = new object[] { (float)10.5, (uint)10 };
IList<object> outputArguments = null;
// Invoke Call service
lgDebug($"Calling UAMethod for node {methodId} ...");
outputArguments = m_session.Call(objectId, methodId, inputArguments);
// Display results
lgDebug($"Method call returned {outputArguments.Count} output argument(s):");
foreach (var outputArgument in outputArguments)
{
lgDebug($" OutputValue = {outputArgument}");
}
}
catch (Exception ex)
{
lgError($"Method call error: {ex.Message}");
}
}
/// <summary>
/// Creates a session with the UA server
/// </summary>
public async Task<bool> ConnectAsync()
{
try
{
if (m_session != null && m_session.Connected == true)
{
lgInfo("Session already connected!");
}
else
{
lgInfo("Connecting...");
// Get the endpoint by connecting to server's discovery endpoint. Try to find
// the first endopint without security.
EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint(ServerUrl, false);
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(m_configuration);
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);
// Create the session
Session session = await Session.Create(
m_configuration,
endpoint,
true,
false,
m_configuration.ApplicationName,
60 * 60 * 1000,
CurrUserIdentity,
null
);
#if false
Session session = await Session.Create(
m_configuration,
endpoint,
false,
false,
m_configuration.ApplicationName,
60 * 60 * 1000,
CurrUserIdentity,
null
);
#endif
// Assign the created session
if (session != null && session.Connected)
{
m_session = session;
}
// Session created successfully.
lgInfo($"New Session Created with SessionName = {m_session.SessionName}");
}
return true;
}
catch (Exception ex)
{
// Log Error
lgError($"Create Session Error : {ex.Message}");
return false;
}
}
/// <summary>
/// Disconnects the session.
/// </summary>
public void Disconnect()
{
try
{
if (m_session != null)
{
lgInfo("Disconnecting...");
m_session.Close();
m_session.Dispose();
m_session = null;
// Log Session Disconnected event
lgInfo("Session Disconnected.");
}
else
{
lgError("Session not created!");
}
}
catch (Exception ex)
{
// Log Error
lgError($"Disconnect Error : {ex.Message}");
}
}
/// <summary>
/// Reads a node by node Id
/// </summary>
/// <param name="nodeIdString">The node Id as string</param>
/// <returns>The read node</returns>
/// <exception cref="Exception">Throws and forwards any exception with short error description.</exception>
public Node ReadNode(String nodeIdString)
{
//Create a nodeId using the identifier string
NodeId nodeId = new NodeId(nodeIdString);
//Create a node
Node node = new Node();
//Read the dataValue
node = m_session.ReadNode(nodeId);
return node;
}
/// <summary>Reads a node by node Id</summary>
/// <param name="nodeIdString">The node Id as string</param>
/// <returns>A dictionary containing the attribute values of the node using the attribute uint identifier as key;
/// <exception cref="Exception">Throws and forwards any exception with short error description.</exception>
public Dictionary<uint, DataValue> ReadNodeAttributes(String nodeIdString)
{
//Create a nodeId using the identifier string
NodeId nodeId = new NodeId(nodeIdString);
return ReadNodeAttributes(nodeId);
}
/// <summary>Reads a node by node Id</summary>
/// <param name="nodeId">The node Id</param>
/// <returns>A dictionary containing the attribute values of the node using the attribute uint identifier as key;
/// <exception cref="Exception">Throws and forwards any exception with short error description.</exception>
public Dictionary<uint, DataValue> ReadNodeAttributes(NodeId nodeId)
{
Dictionary<int, uint> attributeDictionary = new Dictionary<int, uint>()
{
{1, Attributes.NodeId },
{2, Attributes.NodeClass },
{3, Attributes.BrowseName },
{4, Attributes.DisplayName },
{5, Attributes.Value },
{6, Attributes.DataType},
{7, Attributes.ValueRank},
{8, Attributes.ArrayDimensions},
{9, Attributes.AccessLevel }
};
//Create a read value id collection to store the nodes to read
ReadValueIdCollection nodesToRead = new ReadValueIdCollection();
//Create a StatusCodeCollection
DataValueCollection dataValueCollection = new DataValueCollection();
//Create a DiagnosticInfoCollection
DiagnosticInfoCollection diag = new DiagnosticInfoCollection();
//Go through the attribute dictionary and create the nodes accordingly
for (int i = 0; i < attributeDictionary.Count; i++)
{
///Create a read value id with the nessessary attribute
ReadValueId nodeToRead = new ReadValueId();
nodeToRead.NodeId = nodeId;
nodeToRead.AttributeId = attributeDictionary[i + 1];
nodesToRead.Add(nodeToRead);
}
//Read the read value id collection
m_session.Read(null, 0, TimestampsToReturn.Neither, nodesToRead, out dataValueCollection, out diag);
//Create a dictionary to store the attribute values
Dictionary<uint, DataValue> attributeValues = new Dictionary<uint, DataValue>();
//Go through the data value collection to store every data value according to its attribute identifier
int counter = 1;
foreach (DataValue dataValue in dataValueCollection)
{
attributeValues.Add(attributeDictionary[counter], dataValue);
counter += 1;
}
return attributeValues;
}
/// <summary>
/// Read a SINGLE of nodes value (RAW) from Server
/// </summary>
/// <param name="reqNodeId"></param>
/// <returns></returns>
public object ReadNodeRaw(NodeId reqNodeId)
{
object answ = null;
if (m_session == null || m_session.Connected == false)
{
lgError("Session not connected!");
numErrors++;
return answ;
}
try
{
#region Read the Value attribute of a node by calling the Session.ReadValue method
try
{
DataValue resp = m_session.ReadValue(reqNodeId);
answ = resp;
}
catch (Exception exc)
{
// Log Error
lgError($"ReadNodeRaw Error 01: {Environment.NewLine}{exc}");
numErrors++;
// provare disconnect?!?
if (numErrors >= maxErrors)
{
numErrors = 0;
Disconnect();
}
}
#endregion Read the Value attribute of a node by calling the Session.ReadValue method
}
catch (Exception ex)
{
// Log Error
lgError($"ReadNodeRaw Error 02: {ex.Message}.");
}
return answ;
}
/// <summary>
/// Read a list of nodes from Server
/// </summary>
public void ReadNodes()
{
if (m_session == null || m_session.Connected == false)
{
lgError("Session not connected!");
return;
}
try
{
#region Read a node by calling the Read Service
// build a list of nodes to be read
ReadValueIdCollection nodesToRead = new ReadValueIdCollection()
{
// Value of ServerStatus
new ReadValueId() { NodeId = Variables.Server_ServerStatus, AttributeId = Attributes.Value },
// BrowseName of ServerStatus_StartTime
new ReadValueId() { NodeId = Variables.Server_ServerStatus_StartTime, AttributeId = Attributes.BrowseName },
// Value of ServerStatus_StartTime
new ReadValueId() { NodeId = Variables.Server_ServerStatus_StartTime, AttributeId = Attributes.Value }
};
// Read the node attributes
lgInfo("Reading nodes...");
// Call Read Service
m_session.Read(
null,
0,
TimestampsToReturn.Both,
nodesToRead,
out DataValueCollection resultsValues,
out DiagnosticInfoCollection diagnosticInfos);
// Validate the results
m_validateResponse(resultsValues, nodesToRead);
// Display the results.
foreach (DataValue result in resultsValues)
{
lgTrace($"Read Value = {result.Value} , StatusCode = {result.StatusCode}");
}
#endregion Read a node by calling the Read Service
#region Read the Value attribute of a node by calling the Session.ReadValue method
// Read Server NamespaceArray
lgTrace("Reading Value of NamespaceArray node...");
DataValue namespaceArray = m_session.ReadValue(Variables.Server_NamespaceArray);
// Display the result
lgTrace($"NamespaceArray Value = {namespaceArray}");
#endregion Read the Value attribute of a node by calling the Session.ReadValue method
}
catch (Exception ex)
{
// Log Error
lgError($"Read Nodes Error : {ex.Message}.");
}
}
/// <summary>
/// Read a SINGLE of nodes value from Server
/// </summary>
/// <param name="reqNodeId"></param>
/// <returns>Value as String</returns>
public string ReadNodeString(NodeId reqNodeId)
{
string answ = "";
if (m_session == null || m_session.Connected == false)
{
lgError("Session not connected!");
return answ;
}
try
{
try
{
DataValue resp = m_session.ReadValue(reqNodeId);
answ = $"{resp.Value}";
// decremento errore...
if (numErrors > 0)
{
numErrors--;
}
}
catch (Exception exc)
{
// Log Error
numErrors++;
lgTrace($"ReadValue Error | reqNodeId: {reqNodeId} | numErrors: {numErrors}{Environment.NewLine}{exc}");
}
}
catch (Exception ex)
{
// Log Error
lgError($"Read Nodes Error : {ex.Message}.");
numErrors++;
}
// se supero maxErrors --> invio eccezione e resetto...
if (numErrors >= maxErrors)
{
// reset + exception
numErrors = 0;
throw new Exception($"Errore lettura OPC-UA: {maxErrors} errori");
}
return answ;
}
/// <summary>Reads a struct or UDT by node Id</summary>
/// <param name="nodeIdString">The node Id as strings</param>
/// <returns>The read struct/UDT elements as a list of string[3]; string[0] = tag name, string[1] = value, string[2] = opc data type</returns>
/// <exception cref="Exception">Throws and forwards any exception with short error description.</exception>
public List<string[]> ReadStructUdt(String nodeIdString)
{
//Define result list to return var name and var value
List<string[]> resultStringList = new List<string[]>();
//Read the node attributes
Dictionary<uint, DataValue> nodeAttributes = ReadNodeAttributes(nodeIdString);
//Create a data value storing the value
DataValue data = nodeAttributes[Attributes.Value];
//Check if the entered node id is of type array and accordingly get the byte array of the data values
ExtensionObject dataValue = new ExtensionObject();
List<byte[]> byteArrays = new List<byte[]>();
if (data.Value is ExtensionObject[])
{
foreach (ExtensionObject temp in (ExtensionObject[])data.Value)
{
byteArrays.Add((byte[])temp.Body);
}
}
else if (data.Value is ExtensionObject)
{
dataValue = (ExtensionObject)data.Value;
byteArrays.Add((byte[])dataValue.Body);
}
else
{
throw new Exception("The entered node id is not of tpye struct/udt.");
}
//Get the type dictionary of the struct as a list of string[3]; string[0] = tag name, string[1] = data type, string [2] = array dimensions
List<string[]> structureDictionary = new List<string[]>();
DataTypeNode dataTypeNode = (DataTypeNode)ReadNode(nodeAttributes[Attributes.DataType].ToString());
GetTypeDictionary(dataTypeNode, m_session, structureDictionary);
//Get the deserialized byte string as a list of string[4]; string[0]=array index; string[1]=tag name; string[2]=tag value; string[3]=tag data type
resultStringList = ParseDataToTagsFromDictionary(structureDictionary, byteArrays);
//return result as List<string[]> string[0]=array index; string[1]=tag name; string[2]=tag value; string[3]=tag data type
return resultStringList;
}
/// <summary>
/// Create Subscription and MonitoredItems for DataChanges
/// </summary>
public List<MonitoredItem> SubscribeToDataChanges(Dictionary<string, string> DataList)
{
List<MonitoredItem> monItList = new List<MonitoredItem>();
if (m_session == null || m_session.Connected == false)
{
lgError("Session not connected!");
return monItList;
}
try
{
// Create a subscription for receiving data change notifications
// Define Subscription parameters
Subscription subscription = new Subscription(m_session.DefaultSubscription);
subscription.DisplayName = "Steamware IOB-WIN Subscription";
subscription.PublishingEnabled = true;
subscription.PublishingInterval = 1000;
m_session.AddSubscription(subscription);
// Create the subscription on Server side
subscription.Create();
lgInfo($"New Subscription created with SubscriptionId = {subscription.Id}");
// Create MonitoredItems for data changes
foreach (var item in DataList)
{
MonitoredItem currMonIt = new MonitoredItem(subscription.DefaultItem);
// Int32 Node - Objects\CTT\Scalar\Simulation\Int32
currMonIt.StartNodeId = new NodeId(item.Key);
currMonIt.AttributeId = Attributes.Value;
currMonIt.DisplayName = item.Value;
currMonIt.SamplingInterval = 1000;
currMonIt.Notification += OnMonitoredItemNotification;
subscription.AddItem(currMonIt);
monItList.Add(currMonIt);
}
// Create the monitored items on Server side
subscription.ApplyChanges();
lgInfo($"MonitoredItems created for SubscriptionId = {subscription.Id}");
}
catch (Exception ex)
{
lgError($"Subscribe error: {ex.Message}");
}
return monItList;
}
/// <summary>
/// Write a list of nodes to the Server
/// </summary>
/// <param name="node2Write"></param>
public void WriteNodes(List<WriteValue> node2Write)
{
if (node2Write != null)
{
if (m_session == null || m_session.Connected == false)
{
lgError("Session not connected!");
return;
}
try
{
// Write the configured nodes
WriteValueCollection nodesToWrite = new WriteValueCollection();
nodesToWrite.AddRange(node2Write);
// Write the node attributes
StatusCodeCollection results = null;
DiagnosticInfoCollection diagnosticInfos;
lgDebug("Writing nodes...");
// Call Write Service
m_session.Write(null,
nodesToWrite,
out results,
out diagnosticInfos);
// Validate the response
m_validateResponse(results, nodesToWrite);
// Display the results.
lgDebug("Write Results :");
foreach (StatusCode writeResult in results)
{
lgDebug($" {writeResult}");
}
}
catch (Exception ex)
{
// Log Error
lgError($"Write Nodes Error : {ex.Message}");
numErrors++;
// provare disconnect?!?
if (numErrors >= maxErrors)
{
numErrors = 0;
Disconnect();
}
}
}
}
/// <summary>
/// Metodo scrittura alternativa in formato UDT dei dati...
/// </summary>
/// <param name="node2Write"></param>
public void WriteNodesUdt(string nodeIdString, Dictionary<string, string> data2write)
{
if (!string.IsNullOrEmpty(nodeIdString) && data2write != null && data2write.Count > 0)
{
if (m_session == null || m_session.Connected == false)
{
lgError("Session not connected!");
return;
}
try
{
var listUdtRows = new List<string[]>();
listUdtRows = new List<string[]>();
// leggo elenco dati in primis
listUdtRows = ReadStructUdt(nodeIdString);
// va scartata ultima riga!!!!
listUdtRows.RemoveAt(listUdtRows.Count - 1);
// sostituzione dei dati: per ogni valore corrente cerco nei valori data2write
foreach (var item in listUdtRows)
{
// se trovo valore da sostituire
if (data2write.ContainsKey(item[1]))
{
item[2] = data2write[item[1]];
}
}
// scrivo
WriteStructUdt(nodeIdString, listUdtRows);
}
catch (Exception ex)
{
// Log Error
lgError($"Error WriteNodesUdt for {nodeIdString}: {Environment.NewLine}{ex.Message}");
numErrors++;
// provare disconnect?!?
if (numErrors >= maxErrors)
{
numErrors = 0;
Disconnect();
}
}
}
}
/// <summary>
/// Writes values to node Ids
/// </summary>
/// <param name="valuesByNodeId">Mulitple NodeIds with there values (multiple possible if not a scalar).</param>
/// <exception cref="Exception">Throws and forwards any exception with short error description.</exception>
public void WriteValues(Dictionary<NodeId, IEnumerable<string>> valuesByNodeId)
{
//Create a collection of values to write
WriteValueCollection valuesToWrite = new WriteValueCollection();
foreach (var keyValuePair in valuesByNodeId)
{
NodeId nodeId = keyValuePair.Key;
IEnumerable<string> values = keyValuePair.Value;
//Get the OPC UA data type and the Rank
Node node = m_session.ReadNode(nodeId);
VariableNode variableNode = (VariableNode)node.DataLock;
NodeId datatypeId = variableNode.DataType;
BuiltInType targetype = TypeInfo.GetBuiltInType(datatypeId);
//Ensure that target type is not null by reading the node of the data type id
if (targetype == BuiltInType.Null)
{
try
{
//Get the node id of the parent base data type
NodeId tempNodeId = GetParentDataType(datatypeId, m_session);
targetype = TypeInfo.GetBuiltInType(tempNodeId);
if (targetype == BuiltInType.Null)//Ensure that the entered node id is not of type struct
{
throw new Exception("The entered node id may be of type struct. Please use the methods for read/write structs.");
}
}
catch
{
throw new Exception("The node id data type is not castable. Please check the data type.");
}
}
int valueRank = variableNode.ValueRank;
DataValue dataValue = null;
//Check if there is a value entered
if (!values.Any() || nodeId == null) //no value in the textbox
{
throw new Exception("There is no NodeId or value entered.");
}
//Ensure that boolean entries have lower case initial and float, double and long double have the rigth format
List<string> tempValues = new List<string>();
if (targetype == BuiltInType.Boolean)
{
foreach (string tempValue in values)
{
tempValues.Add(tempValue.ToLower());
}
values = tempValues;
}
if (targetype == BuiltInType.Float || targetype == BuiltInType.Double)
{
foreach (string tempValue in values)
{
tempValues.Add(tempValue.Replace(',', '.'));
}
values = tempValues;
}
//Check if the inputed value has the type matrix or array
if (valueRank >= ValueRanks.OneDimension)
{
//Cast values in the target type
Array castValues = TypeInfo.CastArray(values.ToArray(), BuiltInType.Null, targetype, (object source, BuiltInType srcType, BuiltInType dstType) => TypeInfo.Cast(source, dstType));
if (valueRank == ValueRanks.OneDimension) //Check if inputed value is array
{
dataValue = new DataValue(new Variant(castValues, new TypeInfo(targetype, valueRank)));
}
else //Inputed value has more then one dimension
{
//Get matrix dimensions
int[] dimensions = new int[valueRank];
for (int i = 0; i < valueRank; i++)
{
dimensions[i] = (int)variableNode.ArrayDimensions[i];
}
Matrix matrix = new Matrix(castValues, targetype, dimensions);
dataValue = new DataValue(new Variant(matrix));
}
}
else
{
try //OPC UA data type with type scalar
{
dataValue = new DataValue(new Variant(TypeInfo.Cast(values.First(), targetype)));
}
catch //no OPC UA data type
{
throw new FormatException($"The value inputed [{values.First()}] is not convertable to type {targetype}.");
}
}
//Create a WriteValue using the NodeId, dataValue and attributeType
WriteValue valueToWrite = new WriteValue()
{
Value = dataValue,
NodeId = nodeId,
AttributeId = Attributes.Value
};
//Add the dataValues to the collection
valuesToWrite.Add(valueToWrite);
}
StatusCodeCollection result;
//Write the collection to the server
m_session.Write(null, valuesToWrite, out result, out _);
foreach (StatusCode code in result)
{
lgDebug($" {code}");
#if false
if (code != 0)
{
throw new Exception(code.ToString());
}
#endif
}
}
/// <summary>
/// Write a list of nodes to the Server
/// </summary>
public void WriteSingleNode(WriteValue node2Write)
{
if (node2Write != null)
{
if (m_session == null || m_session.Connected == false)
{
lgError("Session not connected!");
return;
}
try
{
// Write the configured nodes
WriteValueCollection nodesToWrite = new WriteValueCollection();
nodesToWrite.Add(node2Write);
// Write the node attributes
StatusCodeCollection results = null;
DiagnosticInfoCollection diagnosticInfos;
lgDebug("Writing nodes...");
// Call Write Service
m_session.Write(null,
nodesToWrite,
out results,
out diagnosticInfos);
// Validate the response
m_validateResponse(results, nodesToWrite);
// Display the results.
lgDebug("Write Results :");
foreach (StatusCode writeResult in results)
{
lgDebug($" {writeResult}");
}
}
catch (Exception ex)
{
// Log Error
lgError($"Write Nodes Error : {ex.Message}");
}
}
}
/// <summary>Writes data to a struct or UDT by node Id</summary>
/// <param name="nodeIdString">The node Id as strings</param>
/// <param name="dataToWrite">The data to write as string[3]; string[0] = tag name, string[1] = value, string[2] = opc data type</param>
/// <exception cref="Exception">Throws and forwards any exception with short error description.</exception>
public void WriteStructUdt(String nodeIdString, List<string[]> dataToWrite)
{
//Create a NodeId from the NodeIdString
NodeId nodeId = new NodeId(nodeIdString);
//Creat a WriteValueColelction
WriteValueCollection valuesToWrite = new WriteValueCollection();
//Create a WriteValue
WriteValue writevalue = new WriteValue();
//Read the node attributes
Dictionary<uint, DataValue> nodeAttributes = ReadNodeAttributes(nodeIdString);
//Create a data value storing the value
DataValue data = nodeAttributes[Attributes.Value];
//Check if the entered node id is of type array and accordingly get the byte array(s) of the data values
ExtensionObject extensionObject = new ExtensionObject();
List<byte[]> byteArrays = new List<byte[]>();
if (data.Value is ExtensionObject[])
{
foreach (ExtensionObject temp in (ExtensionObject[])data.Value)
{
byteArrays.Add((byte[])temp.Body);
}
}
else
{
extensionObject = (ExtensionObject)data.Value;
byteArrays.Add((byte[])extensionObject.Body);
}
//Get the type dictionary of the struct as a list of string[3]; string[0] = tag name, string[1] = data type, string [2] = array dimensions
List<string[]> structureDictionary = new List<string[]>();
DataTypeNode dataTypeNode = (DataTypeNode)ReadNode(nodeAttributes[Attributes.DataType].ToString());
GetTypeDictionary(dataTypeNode, m_session, structureDictionary);
//Delete empty list elements in the structure dictionary and in the list of the gui
for (int j = 0; j < structureDictionary.Count; j++)
{
if (structureDictionary[j][1] == "")//Check if the data type entry is empty as this is an indicator for a struct/udt in a struct/udt
{
structureDictionary.RemoveAt(j);
//dataToWrite.RemoveAt(j);
}
}
//Create a byte array
List<byte[]> bytesToWrite;
//Parse dataToWrite to the byte array
bytesToWrite = ParseDataToByteArray(dataToWrite, structureDictionary);
//Create a StatusCodeCollection
StatusCodeCollection results = new StatusCodeCollection();
//Create a DiagnosticInfoCollection
DiagnosticInfoCollection diag = new DiagnosticInfoCollection();
//Get the structure definition
StructureDefinition structureDefinition = (StructureDefinition)dataTypeNode.DataTypeDefinition.Body;
//Create an ExtensionObject from the Structure given to this function
ExtensionObject writeExtObj = new ExtensionObject();
//Copy the encoding node id to the extension object type id
writeExtObj.TypeId = structureDefinition.DefaultEncodingId;
//Copy data to extension object body
DataValue dataValue = null;
if (bytesToWrite.Count == 1)
{
//Copy data to extension object body
writeExtObj.Body = bytesToWrite[0];
//Turn the created ExtensionObject into a DataValue
dataValue = new DataValue(writeExtObj);
}
else
{
ExtensionObject[] writeExteObjArr = new ExtensionObject[bytesToWrite.Count];
for (int i = 0; i < bytesToWrite.Count; i++)
{
//Create an ExtensionObject from the Structure given to this function
writeExtObj = new ExtensionObject();
//Copy data to extension object body
writeExtObj.TypeId = structureDefinition.DefaultEncodingId;
writeExtObj.Body = bytesToWrite[i];
//Copy extension object to extension object array
writeExteObjArr[i] = writeExtObj;
}
//Turn the created ExtensionObject[] into a DataValue
dataValue = new DataValue(writeExteObjArr);
}
//Setup for the WriteValue
writevalue.NodeId = nodeId;
writevalue.Value = dataValue;
writevalue.AttributeId = Attributes.Value;
//Add the created value to the collection
valuesToWrite.Add(writevalue);
try
{
m_session.Write(null, valuesToWrite, out results, out diag);
}
catch (Exception e)
{
//Handle Exception here
throw e;
}
//Check result codes
foreach (StatusCode result in results)
{
if (result.ToString() != "Good")
{
Exception e = new Exception(result.ToString());
throw e;
}
}
}
/// <summary>
/// Write a list of nodes to the Server
/// </summary>
public void WriteTestNodes()
{
if (m_session == null || m_session.Connected == false)
{
lgError("Session not connected!");
return;
}
try
{
// scrivo vaslori a caso.. hhmm odierni
int hhmm = 9876;
int.TryParse(DateTime.Now.ToString("HHmm"), out hhmm);
// Write the configured nodes
WriteValueCollection nodesToWrite = new WriteValueCollection();
// Int32 Node - Objects\CTT\Scalar\Scalar_Static\Int32
WriteValue commWriteVal = new WriteValue();
commWriteVal.NodeId = new NodeId("ns=4;s=IO_151");
commWriteVal.AttributeId = Attributes.Value;
commWriteVal.Value = new DataValue();
commWriteVal.Value.Value = (int)hhmm - 10;
nodesToWrite.Add(commWriteVal);
WriteValue artWriteVal = new WriteValue();
artWriteVal.NodeId = new NodeId("ns=4;s=IO_151");
artWriteVal.AttributeId = Attributes.Value;
artWriteVal.Value = new DataValue();
artWriteVal.Value.Value = (int)hhmm;
nodesToWrite.Add(artWriteVal);
WriteValue qtyWriteVal = new WriteValue();
qtyWriteVal.NodeId = new NodeId("ns=4;s=IO_153");
qtyWriteVal.AttributeId = Attributes.Value;
qtyWriteVal.Value = new DataValue();
qtyWriteVal.Value.Value = (int)hhmm + 10;
nodesToWrite.Add(qtyWriteVal);
// Write the node attributes
StatusCodeCollection results = null;
DiagnosticInfoCollection diagnosticInfos;
lgDebug("Writing nodes...");
// Call Write Service
m_session.Write(null,
nodesToWrite,
out results,
out diagnosticInfos);
// Validate the response
m_validateResponse(results, nodesToWrite);
// Display the results.
lgDebug("Write Results :");
foreach (StatusCode writeResult in results)
{
lgDebug($" {writeResult}");
}
}
catch (Exception ex)
{
// Log Error
lgError($"Write Nodes Error : {ex.Message}.");
}
}
#endregion Public Methods
#region Protected Fields
protected static bool isLogVerbose = false;
protected static Logger lg;
protected int currNullReceiv = 0;
protected int maxErrors = 20;
protected int maxNullAllowed = 100;
protected int numErrors = 0;
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// Effettua logging DEBUG corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgDebug(string message)
{
lg.Factory.Configuration.Variables["codIOB"] = currIob;
lg.Debug(message);
}
/// <summary>
/// Effettua logging ERROR corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgError(string message)
{
lg.Factory.Configuration.Variables["codIOB"] = currIob;
lg.Error(message);
}
/// <summary>
/// Effettua logging INFO corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgInfo(string message)
{
lg.Factory.Configuration.Variables["codIOB"] = currIob;
lg.Info(message);
}
/// <summary>
/// Effettua logging TRACE corretto impostanto anche la variabile IOB prima di scrivere...
/// </summary>
/// <param name="message"></param>
protected void lgTrace(string message)
{
lg.Factory.Configuration.Variables["codIOB"] = currIob;
lg.Trace(message);
}
#endregion Protected Methods
#region Private Fields
private readonly string currIob;
private readonly Action<IList, IList> m_validateResponse;
private ApplicationConfiguration m_configuration;
private Session m_session;
#endregion Private Fields
#region Private Methods
/// <summary>
/// Browses for the node id of the parent data type of type base data type
/// </summary>
/// <param name="nodeId">The data to write as string[4]; string[0] = index, string[1] = tag name, string[2] = value, string[3] = opc data type</param>
/// <returns>The node id of the parent data type</returns>
/// <exception cref="Exception">Throws and forwards any exception with short error description.</exception>
private static NodeId GetParentDataType(NodeId nodeId, Session theSessionToBrowseIn)
{
ReferenceDescriptionCollection referenceDescriptionCollection;
byte[] continuationPoint;
theSessionToBrowseIn.Browse(null, null, nodeId, 1, BrowseDirection.Inverse, ReferenceTypeIds.HasSubtype, true, 0, out continuationPoint, out referenceDescriptionCollection);
NodeId nodeIdParentDataType = (NodeId)referenceDescriptionCollection[0].NodeId;
if (nodeIdParentDataType.NamespaceIndex != 0)
{
nodeIdParentDataType = GetParentDataType(nodeIdParentDataType, theSessionToBrowseIn);
}
return nodeIdParentDataType;
}
/// <summary>
/// Browses for the desired type dictonary to parse for containing data types
/// </summary>
/// <param name="dataTypeNode">The data type node</param>
/// <param name="theSessionToBrowseIn">The current session to browse in</param>
/// <param name="structureDictionary">The list of string arrays containing the structure information tag name, data type id and array dimension</param>
/// <returns>The dictionary as List of string arrays</returns>
/// <exception cref="Exception">Throws and forwards any exception with short error description.</exception>
private static List<string[]> GetTypeDictionary(DataTypeNode dataTypeNode, Session theSessionToBrowseIn, List<string[]> structureDictionary)
{
//Create a temp list of string containing the structure dictionary
List<string[]> tempStructureDictionary = new List<string[]>();
tempStructureDictionary = structureDictionary;
Session tempSession = theSessionToBrowseIn;
//Get Structure Definition
StructureFieldCollection structureFieldCollection = null;
try
{
StructureDefinition structureDefinition = (StructureDefinition)dataTypeNode.DataTypeDefinition.Body;
structureFieldCollection = (StructureFieldCollection)structureDefinition.Fields;
}
catch
{
structureDictionary[structureDictionary.Count - 1][1] = UAClient.GetParentDataType(dataTypeNode.NodeId, tempSession).ToString();
return structureDictionary;
}
//Check every structure field in the collection
foreach (StructureField structureField in structureFieldCollection)
{
if (structureField.DataType.NamespaceIndex != 0) //Check for structures in the structure
{
//Create a reference description collection
ReferenceDescriptionCollection referenceDescriptionCollection;
//Create a continuationPoint
byte[] continuationPoint;
//Browse for the references of the data type of the structure field
theSessionToBrowseIn.Browse(
null,
null,
structureField.DataType,
0u,
BrowseDirection.Both,
ReferenceTypeIds.References,
true,
0,
out continuationPoint,
out referenceDescriptionCollection);
if (((NodeId)referenceDescriptionCollection[0].NodeId).NamespaceIndex == 0)//Data type is sub type of a base data type, i.e. BYTE is a sub tpye of Byte
{
//Check if the structure field is an array and parse the dimension to string
string arrayDimensions = "";
if (structureField.ValueRank == ValueRanks.OneDimension)
{
arrayDimensions = structureField.ArrayDimensions[0].ToString();
}
//Add the structure field name, its data type node id and array dimension to the dictionary list
tempStructureDictionary.Add(new string[] { structureField.Name, ((NodeId)referenceDescriptionCollection[0].NodeId).ToString(), arrayDimensions });
}
else//The structure/udt has at least one additional structure/udt inside
{
//Read the data type node id of the structure/udt in the parent structure/udt
DataTypeNode tempDataTypeNode = (DataTypeNode)theSessionToBrowseIn.ReadNode(structureField.DataType.ToString());
//Enter an empty Line to seperate the inside structure from the parent
tempStructureDictionary.Add(new string[] { structureField.Name + " - " + "struct/udt of type: " + tempDataTypeNode.DisplayName.ToString(), "", "" });
//Copy the temp dictionary to the real dictionary
structureDictionary = tempStructureDictionary;
//Recursively get the structure of the structure/udt in the parent structure/udt
GetTypeDictionary(tempDataTypeNode, tempSession, structureDictionary);
}
}
else //The structure field is a base data type
{
//Check if the structure field is an array and parse the dimension to string
string arrayDimensions = "";
if (structureField.ValueRank == ValueRanks.OneDimension)
{
arrayDimensions = structureField.ArrayDimensions[0].ToString();
}
//Add the structure field name, its data type node id and array dimension to the dictionary list
tempStructureDictionary.Add(new string[] { structureField.Name, structureField.DataType.ToString(), arrayDimensions });
}
//Copy the temp dictionary to the real dictionary
structureDictionary = tempStructureDictionary;
}
//Add a row for marking the end of the struct/udt
structureDictionary.Add(new string[] { "/" + dataTypeNode.DisplayName.ToString(), "", "" });
return structureDictionary;
}
/// <summary>
/// Parses data to write to a list of byte arrays
/// </summary>
/// <param name="dataToWrite">The data to write as string[4]; string[0] = index, string[1] = tag name, string[2] = value, string[3] = opc data type</param>
/// <param name="structureDictionary">The structure of the udt/struct</param>
/// <returns>The list of the parsed byte arrays</returns>
/// <exception cref="Exception">Throws and forwards any exception with short error description.</exception>
private static List<byte[]> ParseDataToByteArray(List<string[]> dataToWrite, List<string[]> structureDictionary)
{
//Define result byte list to return a byte array
List<byte[]> resultByteList = new List<byte[]>();
//Dictionary to get the right byte length depending on the data type
Dictionary<BuiltInType, int> byteLength = new Dictionary<BuiltInType, int>()
{
{BuiltInType.Boolean, 1},
{BuiltInType.Int16, 2},
{BuiltInType.Int32, 4},
{BuiltInType.Int64, 8},
{BuiltInType.UInt16, 2},
{BuiltInType.UInt32, 4},
{BuiltInType.UInt64, 8},
{BuiltInType.Float, 4},
{BuiltInType.Double, 8},
{BuiltInType.Byte, 1},
{BuiltInType.String, 4}
};
//Counter for the structure dictionary browsing
int tempCounter = 0;
//Create byte array to store the data of one array
byte[] byteArray = new byte[0];
//Start decoding for opc data types
foreach (string[] element in dataToWrite)
{
//Reset the counter and the byteArray each time the counter reaches the size of the target Structure/UDT and the data to write include the target Structure/UDT several times
if (tempCounter == structureDictionary.Count)
{
//Add the byte array to the result byte array list
resultByteList.Add(byteArray);
//Reset the counter and the array
byteArray = new byte[0];
tempCounter = 0;
}
//Create byte array to store the bytes of one value depending on its data type, hence it is reseted to 0 for each element
byte[] dataByteArray = new byte[0];
//Get the data type id and the target type by using the structure dictionary
NodeId datatypeId = new NodeId(structureDictionary[tempCounter][1]);
BuiltInType targetType = TypeInfo.GetBuiltInType(datatypeId);
//Get the system type for getting the rigth parse method
Type systemType = TypeInfo.GetSystemType(datatypeId, null);
//Parse array dimension string of the structure dictionary in int32
Int32 arraySize = 0;
if (structureDictionary[tempCounter][2] != "")
{
arraySize = Int32.Parse(structureDictionary[tempCounter][2]);
}
//Serialize the byte string depending on the target data type and the array size - special if queries are needed for byte and string
if (targetType == BuiltInType.String && !(arraySize > 0))
{
//Resize the data byte array to its correct size depending on the byte length of target type by using the dictionary
dataByteArray = new byte[byteLength[targetType]];
//Get the bytes of the string length and copy them to the beginning of the data byte array
Array.Copy(BitConverter.GetBytes(element[2].Length), 0, dataByteArray, 0, byteLength[targetType]);
//Convert the string to byte and concat them to the data byte array by converting every char of the string
foreach (Char c in element[2])
{
byte[] tempArray = new byte[1];
tempArray[0] = Convert.ToByte(c);
dataByteArray = dataByteArray.Concat(tempArray).ToArray();
}
}
else if (targetType == BuiltInType.String && arraySize > 0)
{
//Create a temp string array with the array size
string[] tempStringArr = new string[(arraySize - 1)];
//Resize the data byte array to its correct size depending on the byte length of target type by using the dictionary
dataByteArray = new byte[byteLength[BuiltInType.UInt32]];
//Get the bytes of the string length and copy them to the beginning of the data byte array
Array.Copy(BitConverter.GetBytes(arraySize), 0, dataByteArray, 0, byteLength[targetType]);
//Get the single array elements by splitting the string
tempStringArr = element[2].Split(';');
//Go through every string element in the temp string array
for (int ii = 0; ii < arraySize; ii++)
{
//Copy the string length as byte to the data byte array
byte[] tempArray = new byte[byteLength[targetType]];
Array.Copy(BitConverter.GetBytes(tempStringArr[ii].Length), 0, tempArray, 0, byteLength[targetType]);
dataByteArray = dataByteArray.Concat(tempArray).ToArray();
//Convert the string to byte and concat them to the data byte array by converting every char of the string
foreach (Char c in tempStringArr[ii])
{
byte[] tempArraySingle = new byte[1];
tempArraySingle[0] = Convert.ToByte(c);
dataByteArray = dataByteArray.Concat(tempArraySingle).ToArray();
}
}
}
else if (targetType == BuiltInType.Byte && !(arraySize > 0))
{
//Convert the string of byte to byte
dataByteArray = new byte[byteLength[targetType]];
dataByteArray[0] = Convert.ToByte(element[2]);
}
else if (targetType == BuiltInType.Byte && arraySize > 0)
{
//Create a temp string variable to store the array elements which are divided by ;
String tempString = "";
//Resize the data byte array to its correct size depending on the byte length of target type by using the dictionary
dataByteArray = new byte[byteLength[BuiltInType.UInt32]];
//Get the bytes of the string length and copy them to the beginning of the data byte array
Array.Copy(BitConverter.GetBytes(arraySize), 0, dataByteArray, 0, byteLength[targetType]);
//Check every char in the string value of the element
foreach (Char c in element[2])
{
if (c != ';') //Concat every char to a string value in elements except the ;
{
tempString = String.Concat(tempString, c);
}
else
{
//Create a temp byte array with its correct size depending on the byte length of the target data type
byte[] tempArray = new byte[byteLength[targetType]];
tempArray[0] = Convert.ToByte(tempString);
//Copy the temp array to the data byte array
dataByteArray = dataByteArray.Concat(tempArray).ToArray();
//Reset the temp string
tempString = "";
}
}
}
else if (!(arraySize > 0))
{
//Create the byte array with its correct size depending on the byte length of the target data type
dataByteArray = new byte[byteLength[targetType]];
//Get the converter method depending on the system type
System.Reflection.MethodInfo methodInfo = typeof(Convert).GetMethod("To" + systemType.Name, new Type[] { typeof(string) });
//Call the converter method
dynamic convertDataType = methodInfo.Invoke(null, new Object[] { element[2] });
Array.Copy(BitConverter.GetBytes(convertDataType), 0, dataByteArray, 0, byteLength[targetType]);
}
else if (arraySize > 0)
{
//Create a temp string variable to store the array elements which are divided by ;
String tempString = "";
//Create the byte array with the size of a UInt32, which is the data type for the array dimension
dataByteArray = new byte[byteLength[BuiltInType.UInt32]];
//Get the bytes of the array dimension and copy them to the data byte array as they always are at the beginning of a byte array of an array
Array.Copy(BitConverter.GetBytes(arraySize), 0, dataByteArray, 0, byteLength[targetType]);
//Check every char in the string value of the element
foreach (Char c in element[2])
{
if (c != ';') //Concat every char to a string value in elements except the ;
{
tempString = String.Concat(tempString, c);
}
else
{
//Create a temp byte array with its correct size depending on the byte length of the target data type
byte[] tempArray = new byte[byteLength[targetType]];
//Get the converter method depending on the system type
System.Reflection.MethodInfo methodInfo = typeof(Convert).GetMethod("To" + systemType.Name, new Type[] { typeof(string) });
//Call the converter method
dynamic convertDataType = methodInfo.Invoke(null, new Object[] { element[2] });
Array.Copy(BitConverter.GetBytes(convertDataType), 0, tempArray, 0, byteLength[targetType]);
//Concat the temp array with the data array
dataByteArray = dataByteArray.Concat(tempArray).ToArray();
//Reset the temp string
tempString = "";
}
}
}
else
{
Exception e = new Exception("Data type is too complex to be parsed." + System.Environment.NewLine);
throw e;
}
//Add the data byte array to the byte array containing all data and count the counter
byteArray = byteArray.Concat(dataByteArray).ToArray();
tempCounter++;
}
resultByteList.Add(byteArray);
return resultByteList;
}
/// <summary>
/// Parses a byte array to objects containing tag names and tag data types
/// </summary>
/// <param name="varList">List of object containing tag names and tag data types</param>
/// <param name="byteResult">A byte array to parse</param>
/// <returns>A list of string[4]; string[0] = tag name, string[1] = tag name, string[2] = value, string[3] = opc data type</returns>
/// <exception cref="Exception">Throws and forwards any exception with short error description.</exception>
private static List<string[]> ParseDataToTagsFromDictionary(List<string[]> varList, List<byte[]> byteArrays)
{
//Define result list to return var name, var value and var data type
List<string[]> resultStringList = new List<string[]>();
//Dictionary for index counting
Dictionary<BuiltInType, int> byteLength = new Dictionary<BuiltInType, int>()
{
{BuiltInType.Boolean, 1},
{BuiltInType.Int16, 2},
{BuiltInType.Int32, 4},
{BuiltInType.Int64, 8},
{BuiltInType.UInt16, 2},
{BuiltInType.UInt32, 4},
{BuiltInType.UInt64, 8},
{BuiltInType.Float, 4},
{BuiltInType.Double, 8},
{BuiltInType.Byte, 1},
{BuiltInType.String, 4}
};
//Array index
int arrayIndex = 0;
foreach (byte[] byteResult in byteArrays)
{
//Byte decoding index
int index = 0;
//Start decoding for opc data types
foreach (string[] val in varList)
{
string[] dataReferenceStringArray = new string[4];
//Check if the type dictionary includes a struct/udt in a struct/udt and enter an empty row
if (val[1] == "")
{
dataReferenceStringArray[0] = "[" + arrayIndex.ToString() + "]";
dataReferenceStringArray[1] = val[0];
dataReferenceStringArray[2] = "";
dataReferenceStringArray[3] = "";
//Add the data Reference string array to the result string list
resultStringList.Add(dataReferenceStringArray);
continue;
}
//Add array index
dataReferenceStringArray[0] = "[" + arrayIndex.ToString() + "]";
//Copy tag name
dataReferenceStringArray[1] = val[0];
//Get the target data type via the data type id
NodeId datatypeId = new NodeId(val[1]);
BuiltInType targetType = TypeInfo.GetBuiltInType(datatypeId);
//Get the system type
Type systemType = TypeInfo.GetSystemType(datatypeId, null);
//Parse array dimensions string in int32
Int32 arrayDimensions = 0;
if (val[2] != "")
{
arrayDimensions = Int32.Parse(val[2]);
}
//Deserialize the byte array depending on the target data type and the array dimension
if (targetType == BuiltInType.String && !(arrayDimensions > 0))
{
//Get the string length
Int32 stringlength = BitConverter.ToInt32(byteResult, index);
index += byteLength[targetType];
if (stringlength > 0) //Decode the bytes to its string value
{
dataReferenceStringArray[2] = Encoding.UTF8.GetString(byteResult, index, stringlength);
index += stringlength;
}
else
{
dataReferenceStringArray[2] = "";
}
}
else if (targetType == BuiltInType.String && arrayDimensions > 0)
{
//Skip array information (UInt32) regarding its size
index += byteLength[BuiltInType.UInt32];
//Check every element of the array
for (int i = 0; i < arrayDimensions; i++)
{
//Get the string length
Int32 stringlength = BitConverter.ToInt32(byteResult, index);
index += byteLength[targetType];
if (stringlength > 0) //Decode the bytes to its string value
{
dataReferenceStringArray[2] = String.Concat(dataReferenceStringArray[2], Encoding.UTF8.GetString(byteResult, index, stringlength));
//Add a ; to the string value to seperate the value from the other array values
dataReferenceStringArray[2] = String.Concat(dataReferenceStringArray[2], ";");
index += stringlength;
}
else
{
dataReferenceStringArray[2] = dataReferenceStringArray[2] + ";";
}
}
}
else if (targetType == BuiltInType.Byte && !(arrayDimensions > 0))
{
//Copy the byte value as string to the data reference array
dataReferenceStringArray[2] = byteResult[index].ToString();
index += byteLength[targetType];
}
else if (targetType == BuiltInType.Byte && arrayDimensions > 0)
{
//Skip array information (UInt32) regarding its size
index += byteLength[BuiltInType.UInt32];
Int32[] tempArray = new Int32[arrayDimensions];
//Check every element of the array
for (int i = 0; i < arrayDimensions; i++)
{
tempArray[i] = byteResult[index];
index += byteLength[targetType];
}
//Add the values as one value seperated by ; to the data reference array
dataReferenceStringArray[2] = String.Join(";", tempArray);
dataReferenceStringArray[2] = String.Concat(dataReferenceStringArray[2], ";");
}
else if (!(arrayDimensions > 0))
{
//Get the converter method depending on the system type
System.Reflection.MethodInfo methodInfo = typeof(BitConverter).GetMethod("To" + systemType.Name);
//Call the method
dataReferenceStringArray[2] = methodInfo.Invoke(null, new Object[] { byteResult, index }).ToString();
index += byteLength[targetType];
}
else if (arrayDimensions > 0)
{
//Skip array information (UInt32) regarding its size
index += byteLength[BuiltInType.UInt32];
//Get the converter method depending on the system type
System.Reflection.MethodInfo methodInfo = typeof(BitConverter).GetMethod("To" + systemType.Name);
dynamic[] tempArray = new dynamic[arrayDimensions];
//Check every element of the array
for (int i = 0; i < arrayDimensions; i++)
{
tempArray[i] = methodInfo.Invoke(null, new Object[] { byteResult, index }).ToString();
index += byteLength[targetType];
}
//Add the values as one value seperated by ; to the data reference array
dataReferenceStringArray[2] = String.Join(";", tempArray);
dataReferenceStringArray[2] = String.Concat(dataReferenceStringArray[1], ";");
}
else
{
Exception e = new Exception("Data type is too complex to be parsed." + System.Environment.NewLine);
throw e;
}
//Copy the target type as string to the data reference array
dataReferenceStringArray[3] = targetType.ToString();
//Add the data Reference string array to the result string list
resultStringList.Add(dataReferenceStringArray);
}
//Count the array index
arrayIndex += 1;
}
return resultStringList;
}
/// <summary>
/// Handles the certificate validation event. This event is triggered every time an
/// untrusted certificate is received from the server.
/// </summary>
private void CertificateValidation(CertificateValidator sender, CertificateValidationEventArgs e)
{
bool certificateAccepted = true;
// **** Implement a custom logic to decide if the certificate should be accepted or not
// and set certificateAccepted flag accordingly. The certificate can be retrieved from
// the e.Certificate field ***
ServiceResult error = e.Error;
while (error != null)
{
lgError($"{error.StatusCode} | {error.Code} | {error.LocalizedText}");
error = error.InnerResult;
}
if (certificateAccepted)
{
lgInfo($"Untrusted Certificate accepted. SubjectName = {e.Certificate.SubjectName}");
}
e.AcceptAll = certificateAccepted;
}
/// <summary>
/// Handle DataChange notifications from Server
/// </summary>
private void OnMonitoredItemNotification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
{
try
{
// Log MonitoredItem Notification event
MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;
// verifico se ricevo null, superata soglia genero eccezione
if ($"{notification.Value}".Contains("null"))
{
currNullReceiv++;
lgTrace($"NUll data read | Variable: {monitoredItem.StartNodeId.Identifier} | Value: {notification.Value}");
}
else
{
currNullReceiv--;
currNullReceiv = currNullReceiv > 0 ? currNullReceiv : 0;
// sollevo evento notifica vaziazione MonitoredItem
if (eh_MonItChange != null)
{
eh_MonItChange(this, new opcUaMonitItemChange(monitoredItem, notification));
}
}
lgTrace($"Notification Received | Variable: {monitoredItem.StartNodeId.Identifier} | Value: {notification.Value}");
// se ho ricevuto troppi nul --> disconnetto
if (currNullReceiv > maxNullAllowed)
{
if (eh_nullExceed != null)
{
eh_nullExceed(this, currNullReceiv);
}
// reset
currNullReceiv = 0;
}
}
catch (Exception ex)
{
lgError($"OnMonitoredItemNotification error: {ex.Message}");
}
}
#endregion Private Methods
}
}