using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace OpcUaHelper
{
///
/// 一个二次封装了的OPC UA库,支持从opc ua服务器读写节点数据,批量读写,订阅,批量订阅,历史数据读取,方法调用操作。
///
public class OpcUaClient
{
#region Constructors
///
/// 默认的构造函数,实例化一个新的OPC UA类
///
public OpcUaClient()
{
dic_subscriptions = new Dictionary();
var certificateValidator = new CertificateValidator();
certificateValidator.CertificateValidation += (sender, eventArgs) =>
{
if (ServiceResult.IsGood(eventArgs.Error))
eventArgs.Accept = true;
else if (eventArgs.Error.StatusCode.Code == StatusCodes.BadCertificateUntrusted)
eventArgs.Accept = true;
else
throw new Exception(string.Format("Failed to validate certificate with error code {0}: {1}", eventArgs.Error.Code, eventArgs.Error.AdditionalInfo));
};
SecurityConfiguration securityConfigurationcv = new SecurityConfiguration
{
AutoAcceptUntrustedCertificates = true,
RejectSHA1SignedCertificates = false,
MinimumCertificateKeySize = 1024,
};
certificateValidator.Update(securityConfigurationcv);
// Build the application configuration
var configuration = new ApplicationConfiguration
{
ApplicationName = OpcUaName,
ApplicationType = ApplicationType.Client,
CertificateValidator = certificateValidator,
ApplicationUri = "urn:MyClient", //Kepp this syntax
ProductUri = "OpcUaClient",
ServerConfiguration = new ServerConfiguration
{
MaxSubscriptionCount = 100000,
MaxMessageQueueSize = 1000000,
MaxNotificationQueueSize = 1000000,
MaxPublishRequestCount = 10000000,
},
SecurityConfiguration = new SecurityConfiguration
{
AutoAcceptUntrustedCertificates = true,
RejectSHA1SignedCertificates = false,
MinimumCertificateKeySize = 1024,
SuppressNonceValidationErrors = true,
ApplicationCertificate = new CertificateIdentifier
{
StoreType = CertificateStoreType.X509Store,
StorePath = "CurrentUser\\My",
SubjectName = OpcUaName,
},
TrustedIssuerCertificates = new CertificateTrustList
{
StoreType = CertificateStoreType.X509Store,
StorePath = "CurrentUser\\Root",
},
TrustedPeerCertificates = new CertificateTrustList
{
StoreType = CertificateStoreType.X509Store,
StorePath = "CurrentUser\\Root",
}
},
TransportQuotas = new TransportQuotas
{
OperationTimeout = 6000000,
MaxStringLength = int.MaxValue,
MaxByteStringLength = int.MaxValue,
MaxArrayLength = 65535,
MaxMessageSize = 419430400,
MaxBufferSize = 65535,
ChannelLifetime = -1,
SecurityTokenLifetime = -1
},
ClientConfiguration = new ClientConfiguration
{
DefaultSessionTimeout = -1,
MinSubscriptionLifetime = -1,
},
DisableHiResClock = true
};
configuration.Validate(ApplicationType.Client);
m_configuration = configuration;
}
#endregion Constructors
#region Connect And Disconnect
///
/// connect to server
///
/// remote url
public async Task ConnectServer(string serverUrl)
{
m_session = await Connect(serverUrl);
}
///
/// Creates a new session.
///
/// The new session object.
private async Task Connect(string serverUrl)
{
// disconnect from existing session.
Disconnect();
if (m_configuration == null)
{
throw new ArgumentNullException("_configuration");
}
// select the best endpoint.
EndpointDescription endpointDescription = CoreClientUtils.SelectEndpoint(serverUrl, UseSecurity);
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(m_configuration);
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);
m_session = await Session.Create(
m_configuration,
endpoint,
false,
false,
(string.IsNullOrEmpty(OpcUaName)) ? m_configuration.ApplicationName : OpcUaName,
60000,
UserIdentity,
new string[] { });
// set up keep alive callback.
m_session.KeepAlive += new KeepAliveEventHandler(Session_KeepAlive);
// update the client status
m_IsConnected = true;
// raise an event.
DoConnectComplete(null);
// return the new session.
return m_session;
}
///
/// Disconnects from the server.
///
public void Disconnect()
{
UpdateStatus(false, DateTime.UtcNow, "Disconnected");
// stop any reconnect operation.
if (m_reConnectHandler != null)
{
m_reConnectHandler.Dispose();
m_reConnectHandler = null;
}
// disconnect any existing session.
if (m_session != null)
{
m_session.Close(10000);
m_session = null;
}
// update the client status
m_IsConnected = false;
// raise an event.
DoConnectComplete(null);
}
#endregion Connect And Disconnect
#region Event Handlers
///
/// Report the client status
///
/// Whether the status represents an error.
/// The time associated with the status.
/// The status message.
/// Arguments used to format the status message.
private void UpdateStatus(bool error, DateTime time, string status, params object[] args)
{
m_OpcStatusChange?.Invoke(this, new OpcUaStatusEventArgs()
{
Error = error,
Time = time.ToLocalTime(),
Text = String.Format(status, args),
});
}
///
/// Handles a keep alive event from a session.
///
private void Session_KeepAlive(Session session, KeepAliveEventArgs e)
{
try
{
// check for events from discarded sessions.
if (!Object.ReferenceEquals(session, m_session))
{
return;
}
// start reconnect sequence on communication error.
if (ServiceResult.IsBad(e.Status))
{
if (m_reconnectPeriod <= 0)
{
UpdateStatus(true, e.CurrentTime, "Communication Error ({0})", e.Status);
return;
}
UpdateStatus(true, e.CurrentTime, "Reconnecting in {0}s", m_reconnectPeriod);
if (m_reConnectHandler == null)
{
m_ReconnectStarting?.Invoke(this, e);
m_reConnectHandler = new SessionReconnectHandler();
m_reConnectHandler.BeginReconnect(m_session, m_reconnectPeriod * 1000, Server_ReconnectComplete);
}
return;
}
// update status.
UpdateStatus(false, e.CurrentTime, "Connected [{0}]", session.Endpoint.EndpointUrl);
// raise any additional notifications.
m_KeepAliveComplete?.Invoke(this, e);
}
catch (Exception exception)
{
ClientUtils.HandleException(OpcUaName, exception);
}
}
///
/// Handles a reconnect event complete from the reconnect handler.
///
private void Server_ReconnectComplete(object sender, EventArgs e)
{
try
{
// ignore callbacks from discarded objects.
if (!Object.ReferenceEquals(sender, m_reConnectHandler))
{
return;
}
m_session = m_reConnectHandler.Session;
m_reConnectHandler.Dispose();
m_reConnectHandler = null;
// raise any additional notifications.
m_ReconnectComplete?.Invoke(this, e);
}
catch (Exception exception)
{
#if !NETSTANDARD2_0 && !NETSTANDARD2_1
ClientUtils.HandleException(OpcUaName, exception);
#else
throw;
#endif
}
}
#endregion Event Handlers
#region LogOut Setting
///
/// 设置OPC客户端的日志输出
///
/// 完整的文件路径
/// 是否删除原文件
public void SetLogPathName(string filePath, bool deleteExisting)
{
Utils.SetTraceLog(filePath, deleteExisting);
Utils.SetTraceMask(515);
}
#endregion LogOut Setting
#region Public Members
///
/// a name of application name show on server
///
public string OpcUaName { get; set; } = "Opc Ua Helper";
///
/// Whether to use security when connecting.
///
public bool UseSecurity
{
get { return m_useSecurity; }
set { m_useSecurity = value; }
}
///
/// The user identity to use when creating the session.
///
public IUserIdentity UserIdentity { get; set; }
///
/// The currently active session.
///
public Session Session
{
get { return m_session; }
}
///
/// Indicate the connect status
///
public bool Connected
{
get { return m_IsConnected; }
}
///
/// The number of seconds between reconnect attempts (0 means reconnect is disabled).
///
public int ReconnectPeriod
{
get { return m_reconnectPeriod; }
set { m_reconnectPeriod = value; }
}
///
/// Raised when a good keep alive from the server arrives.
///
public event EventHandler KeepAliveComplete
{
add { m_KeepAliveComplete += value; }
remove { m_KeepAliveComplete -= value; }
}
///
/// Raised when a reconnect operation starts.
///
public event EventHandler ReconnectStarting
{
add { m_ReconnectStarting += value; }
remove { m_ReconnectStarting -= value; }
}
///
/// Raised when a reconnect operation completes.
///
public event EventHandler ReconnectComplete
{
add { m_ReconnectComplete += value; }
remove { m_ReconnectComplete -= value; }
}
///
/// Raised after successfully connecting to or disconnecing from a server.
///
public event EventHandler ConnectComplete
{
add { m_ConnectComplete += value; }
remove { m_ConnectComplete -= value; }
}
///
/// Raised after the client status change
///
public event EventHandler OpcStatusChange
{
add { m_OpcStatusChange += value; }
remove { m_OpcStatusChange -= value; }
}
///
/// 配置信息
///
public ApplicationConfiguration AppConfig => m_configuration;
#endregion Public Members
#region Node Write/Read Support
///
/// Read a value node from server
///
/// node id
/// DataValue
public DataValue ReadNode(NodeId nodeId)
{
ReadValueIdCollection nodesToRead = new ReadValueIdCollection
{
new ReadValueId( )
{
NodeId = nodeId,
AttributeId = Attributes.Value
}
};
// read the current value
m_session.Read(
null,
0,
TimestampsToReturn.Neither,
nodesToRead,
out DataValueCollection results,
out DiagnosticInfoCollection diagnosticInfos);
ClientBase.ValidateResponse(results, nodesToRead);
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);
return results[0];
}
///
/// Read a value node from server
///
/// type of value
/// node id
/// 实际值
public T ReadNode(string tag)
{
DataValue dataValue = ReadNode(new NodeId(tag));
return (T)dataValue.Value;
}
///
/// Read a tag asynchronously
///
/// The type of tag to read
/// tag值
/// The value retrieved from the OPC
public Task ReadNodeAsync(string tag)
{
ReadValueIdCollection nodesToRead = new ReadValueIdCollection
{
new ReadValueId()
{
NodeId = new NodeId(tag),
AttributeId = Attributes.Value
}
};
// Wrap the ReadAsync logic in a TaskCompletionSource, so we can use C# async/await syntax to call it:
var taskCompletionSource = new TaskCompletionSource();
m_session.BeginRead(
requestHeader: null,
maxAge: 0,
timestampsToReturn: TimestampsToReturn.Neither,
nodesToRead: nodesToRead,
callback: ar =>
{
DataValueCollection results;
DiagnosticInfoCollection diag;
var response = m_session.EndRead(
result: ar,
results: out results,
diagnosticInfos: out diag);
try
{
CheckReturnValue(response.ServiceResult);
CheckReturnValue(results[0].StatusCode);
var val = results[0];
taskCompletionSource.TrySetResult((T)val.Value);
}
catch (Exception ex)
{
taskCompletionSource.TrySetException(ex);
}
},
asyncState: null);
return taskCompletionSource.Task;
}
///
/// read several value nodes from server
///
/// all NodeIds
/// all values
public List ReadNodes(NodeId[] nodeIds)
{
ReadValueIdCollection nodesToRead = new ReadValueIdCollection();
for (int i = 0; i < nodeIds.Length; i++)
{
nodesToRead.Add(new ReadValueId()
{
NodeId = nodeIds[i],
AttributeId = Attributes.Value
});
}
// 读取当前的值
m_session.Read(
null,
0,
TimestampsToReturn.Neither,
nodesToRead,
out DataValueCollection results,
out DiagnosticInfoCollection diagnosticInfos);
ClientBase.ValidateResponse(results, nodesToRead);
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);
return results.ToList();
}
///
/// read several value nodes from server
///
/// all NodeIds
/// all values
public Task> ReadNodesAsync(NodeId[] nodeIds)
{
ReadValueIdCollection nodesToRead = new ReadValueIdCollection();
for (int i = 0; i < nodeIds.Length; i++)
{
nodesToRead.Add(new ReadValueId()
{
NodeId = nodeIds[i],
AttributeId = Attributes.Value
});
}
var taskCompletionSource = new TaskCompletionSource>();
// 读取当前的值
m_session.BeginRead(
null,
0,
TimestampsToReturn.Neither,
nodesToRead,
callback: ar =>
{
DataValueCollection results;
DiagnosticInfoCollection diag;
var response = m_session.EndRead(
result: ar,
results: out results,
diagnosticInfos: out diag);
try
{
CheckReturnValue(response.ServiceResult);
taskCompletionSource.TrySetResult(results.ToList());
}
catch (Exception ex)
{
taskCompletionSource.TrySetException(ex);
}
},
asyncState: null);
return taskCompletionSource.Task;
}
///
/// read several value nodes from server
///
/// 所以的节点数组信息
/// all values
public List ReadNodes(string[] tags)
{
List result = new List();
ReadValueIdCollection nodesToRead = new ReadValueIdCollection();
for (int i = 0; i < tags.Length; i++)
{
nodesToRead.Add(new ReadValueId()
{
NodeId = new NodeId(tags[i]),
AttributeId = Attributes.Value
});
}
// 读取当前的值
m_session.Read(
null,
0,
TimestampsToReturn.Neither,
nodesToRead,
out DataValueCollection results,
out DiagnosticInfoCollection diagnosticInfos);
ClientBase.ValidateResponse(results, nodesToRead);
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);
foreach (var item in results)
{
result.Add((T)item.Value);
}
return result;
}
///
/// read several value nodes from server
///
/// all NodeIds
/// all values
public Task> ReadNodesAsync(string[] tags)
{
ReadValueIdCollection nodesToRead = new ReadValueIdCollection();
for (int i = 0; i < tags.Length; i++)
{
nodesToRead.Add(new ReadValueId()
{
NodeId = new NodeId(tags[i]),
AttributeId = Attributes.Value
});
}
var taskCompletionSource = new TaskCompletionSource>();
// 读取当前的值
m_session.BeginRead(
null,
0,
TimestampsToReturn.Neither,
nodesToRead,
callback: ar =>
{
DataValueCollection results;
DiagnosticInfoCollection diag;
var response = m_session.EndRead(
result: ar,
results: out results,
diagnosticInfos: out diag);
try
{
CheckReturnValue(response.ServiceResult);
List result = new List();
foreach (var item in results)
{
result.Add((T)item.Value);
}
taskCompletionSource.TrySetResult(result);
}
catch (Exception ex)
{
taskCompletionSource.TrySetException(ex);
}
},
asyncState: null);
return taskCompletionSource.Task;
}
///
/// write a note to server(you should use try catch)
///
/// The type of tag to write on
/// 节点名称
/// 值
/// if success True,otherwise False
public bool WriteNode(string tag, T value)
{
WriteValue valueToWrite = new WriteValue()
{
NodeId = new NodeId(tag),
AttributeId = Attributes.Value
};
valueToWrite.Value.Value = value;
valueToWrite.Value.StatusCode = StatusCodes.Good;
valueToWrite.Value.ServerTimestamp = DateTime.MinValue;
valueToWrite.Value.SourceTimestamp = DateTime.MinValue;
WriteValueCollection valuesToWrite = new WriteValueCollection
{
valueToWrite
};
// 写入当前的值
m_session.Write(
null,
valuesToWrite,
out StatusCodeCollection results,
out DiagnosticInfoCollection diagnosticInfos);
ClientBase.ValidateResponse(results, valuesToWrite);
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, valuesToWrite);
if (StatusCode.IsBad(results[0]))
{
throw new ServiceResultException(results[0]);
}
return !StatusCode.IsBad(results[0]);
}
///
/// Write a value on the specified opc tag asynchronously
///
/// The type of tag to write on
/// The fully-qualified identifier of the tag. You can specify a subfolder by using a comma delimited name. E.g: the tag `foo.bar` writes on the tag `bar` on the folder `foo`
/// The value for the item to write
public Task WriteNodeAsync(string tag, T value)
{
WriteValue valueToWrite = new WriteValue()
{
NodeId = new NodeId(tag),
AttributeId = Attributes.Value,
};
valueToWrite.Value.Value = value;
valueToWrite.Value.StatusCode = StatusCodes.Good;
valueToWrite.Value.ServerTimestamp = DateTime.MinValue;
valueToWrite.Value.SourceTimestamp = DateTime.MinValue;
WriteValueCollection valuesToWrite = new WriteValueCollection
{
valueToWrite
};
// Wrap the WriteAsync logic in a TaskCompletionSource, so we can use C# async/await syntax to call it:
var taskCompletionSource = new TaskCompletionSource();
m_session.BeginWrite(
requestHeader: null,
nodesToWrite: valuesToWrite,
callback: ar =>
{
var response = m_session.EndWrite(
result: ar,
results: out StatusCodeCollection results,
diagnosticInfos: out DiagnosticInfoCollection diag);
try
{
ClientBase.ValidateResponse(results, valuesToWrite);
ClientBase.ValidateDiagnosticInfos(diag, valuesToWrite);
taskCompletionSource.SetResult(StatusCode.IsGood(results[0]));
}
catch (Exception ex)
{
taskCompletionSource.TrySetException(ex);
}
},
asyncState: null);
return taskCompletionSource.Task;
}
///
/// 所有的节点都写入成功,返回True,否则返回False
///
/// 节点名称数组
/// 节点的值数据
/// 所有的是否都写入成功
public bool WriteNodes(string[] tags, object[] values)
{
WriteValueCollection valuesToWrite = new WriteValueCollection();
for (int i = 0; i < tags.Length; i++)
{
if (i < values.Length)
{
WriteValue valueToWrite = new WriteValue()
{
NodeId = new NodeId(tags[i]),
AttributeId = Attributes.Value
};
valueToWrite.Value.Value = values[i];
valueToWrite.Value.StatusCode = StatusCodes.Good;
valueToWrite.Value.ServerTimestamp = DateTime.MinValue;
valueToWrite.Value.SourceTimestamp = DateTime.MinValue;
valuesToWrite.Add(valueToWrite);
}
}
// 写入当前的值
m_session.Write(
null,
valuesToWrite,
out StatusCodeCollection results,
out DiagnosticInfoCollection diagnosticInfos);
ClientBase.ValidateResponse(results, valuesToWrite);
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, valuesToWrite);
bool result = true;
foreach (var r in results)
{
if (StatusCode.IsBad(r))
{
result = false;
break;
}
}
return result;
}
#endregion Node Write/Read Support
#region DeleteNode Support
///
/// 删除一个节点的操作,除非服务器配置允许,否则引发异常,成功返回True,否则返回False
///
/// 节点文本描述
/// 是否删除成功
public bool DeleteExsistNode(string tag)
{
DeleteNodesItemCollection waitDelete = new DeleteNodesItemCollection();
DeleteNodesItem nodesItem = new DeleteNodesItem()
{
NodeId = new NodeId(tag),
};
m_session.DeleteNodes(
null,
waitDelete,
out StatusCodeCollection results,
out DiagnosticInfoCollection diagnosticInfos);
ClientBase.ValidateResponse(results, waitDelete);
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, waitDelete);
return !StatusCode.IsBad(results[0]);
}
#endregion DeleteNode Support
#region Test Function
///
/// 新增一个节点数据
///
/// 父节点tag名称
[Obsolete("还未经过测试,无法使用")]
public void AddNewNode(NodeId parent)
{
// Create a Variable node.
AddNodesItem node2 = new AddNodesItem();
node2.ParentNodeId = new NodeId(parent);
node2.ReferenceTypeId = ReferenceTypes.HasComponent;
node2.RequestedNewNodeId = null;
node2.BrowseName = new QualifiedName("DataVariable1");
node2.NodeClass = NodeClass.Variable;
node2.NodeAttributes = null;
node2.TypeDefinition = VariableTypeIds.BaseDataVariableType;
//specify node attributes.
VariableAttributes node2Attribtues = new VariableAttributes();
node2Attribtues.DisplayName = "DataVariable1";
node2Attribtues.Description = "DataVariable1 Description";
node2Attribtues.Value = new Variant(123);
node2Attribtues.DataType = (uint)BuiltInType.Int32;
node2Attribtues.ValueRank = ValueRanks.Scalar;
node2Attribtues.ArrayDimensions = new UInt32Collection();
node2Attribtues.AccessLevel = AccessLevels.CurrentReadOrWrite;
node2Attribtues.UserAccessLevel = AccessLevels.CurrentReadOrWrite;
node2Attribtues.MinimumSamplingInterval = 0;
node2Attribtues.Historizing = false;
node2Attribtues.WriteMask = (uint)AttributeWriteMask.None;
node2Attribtues.UserWriteMask = (uint)AttributeWriteMask.None;
node2Attribtues.SpecifiedAttributes = (uint)NodeAttributesMask.All;
node2.NodeAttributes = new ExtensionObject(node2Attribtues);
AddNodesItemCollection nodesToAdd = new AddNodesItemCollection { node2 };
m_session.AddNodes(
null,
nodesToAdd,
out AddNodesResultCollection results,
out DiagnosticInfoCollection diagnosticInfos);
ClientBase.ValidateResponse(results, nodesToAdd);
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToAdd);
}
#endregion Test Function
#region Monitor Support
///
/// 新增一个订阅,需要指定订阅的关键字,订阅的tag名,以及回调方法
///
/// 关键字
/// tag
/// 回调方法
public void AddSubscription(string key, string tag, Action callback)
{
AddSubscription(key, new string[] { tag }, callback);
}
///
/// 新增一批订阅,需要指定订阅的关键字,订阅的tag名数组,以及回调方法
///
/// 关键字
/// 节点名称数组
/// 回调方法
public void AddSubscription(string key, string[] tags, Action callback)
{
Subscription m_subscription = new Subscription(m_session.DefaultSubscription);
m_subscription.PublishingEnabled = true;
m_subscription.PublishingInterval = 0;
m_subscription.KeepAliveCount = uint.MaxValue;
m_subscription.LifetimeCount = uint.MaxValue;
m_subscription.MaxNotificationsPerPublish = uint.MaxValue;
m_subscription.Priority = 100;
m_subscription.DisplayName = key;
for (int i = 0; i < tags.Length; i++)
{
var item = new MonitoredItem
{
StartNodeId = new NodeId(tags[i]),
AttributeId = Attributes.Value,
DisplayName = tags[i],
SamplingInterval = 100,
};
item.Notification += (MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args) =>
{
callback?.Invoke(key, monitoredItem, args);
};
m_subscription.AddItem(item);
}
m_session.AddSubscription(m_subscription);
m_subscription.Create();
lock (dic_subscriptions)
{
if (dic_subscriptions.ContainsKey(key))
{
// remove
dic_subscriptions[key].Delete(true);
m_session.RemoveSubscription(dic_subscriptions[key]);
dic_subscriptions[key].Dispose();
dic_subscriptions[key] = m_subscription;
}
else
{
dic_subscriptions.Add(key, m_subscription);
}
}
}
///
/// 移除订阅消息,如果该订阅消息是批量的,也直接移除
///
/// 订阅关键值
public void RemoveSubscription(string key)
{
lock (dic_subscriptions)
{
if (dic_subscriptions.ContainsKey(key))
{
// remove
dic_subscriptions[key].Delete(true);
m_session.RemoveSubscription(dic_subscriptions[key]);
dic_subscriptions[key].Dispose();
dic_subscriptions.Remove(key);
}
}
}
///
/// 移除所有的订阅消息
///
public void RemoveAllSubscription()
{
lock (dic_subscriptions)
{
foreach (var item in dic_subscriptions)
{
item.Value.Delete(true);
m_session.RemoveSubscription(item.Value);
item.Value.Dispose();
}
dic_subscriptions.Clear();
}
}
#endregion Monitor Support
#region ReadHistory Support
///
/// read History data
///
/// 节点的索引
/// 开始时间
/// 结束时间
/// 读取的个数
/// 是否包含边界
/// 读取的数据列表
public IEnumerable ReadHistoryRawDataValues(string tag, DateTime start, DateTime end, uint count = 1, bool containBound = false)
{
HistoryReadValueId m_nodeToContinue = new HistoryReadValueId()
{
NodeId = new NodeId(tag),
};
ReadRawModifiedDetails m_details = new ReadRawModifiedDetails
{
StartTime = start,
EndTime = end,
NumValuesPerNode = count,
IsReadModified = false,
ReturnBounds = containBound
};
HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection();
nodesToRead.Add(m_nodeToContinue);
m_session.HistoryRead(
null,
new ExtensionObject(m_details),
TimestampsToReturn.Both,
false,
nodesToRead,
out HistoryReadResultCollection results,
out DiagnosticInfoCollection diagnosticInfos);
ClientBase.ValidateResponse(results, nodesToRead);
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);
if (StatusCode.IsBad(results[0].StatusCode))
{
throw new ServiceResultException(results[0].StatusCode);
}
HistoryData values = ExtensionObject.ToEncodeable(results[0].HistoryData) as HistoryData;
foreach (var value in values.DataValues)
{
yield return value;
}
}
///
/// 读取一连串的历史数据,并将其转化成指定的类型
///
/// 节点的索引
/// 开始时间
/// 结束时间
/// 读取的个数
/// 是否包含边界
/// 读取的数据列表
public IEnumerable ReadHistoryRawDataValues(string tag, DateTime start, DateTime end, uint count = 1, bool containBound = false)
{
HistoryReadValueId m_nodeToContinue = new HistoryReadValueId()
{
NodeId = new NodeId(tag),
};
ReadRawModifiedDetails m_details = new ReadRawModifiedDetails
{
StartTime = start.ToUniversalTime(),
EndTime = end.ToUniversalTime(),
NumValuesPerNode = count,
IsReadModified = false,
ReturnBounds = containBound
};
HistoryReadValueIdCollection nodesToRead = new HistoryReadValueIdCollection();
nodesToRead.Add(m_nodeToContinue);
m_session.HistoryRead(
null,
new ExtensionObject(m_details),
TimestampsToReturn.Both,
false,
nodesToRead,
out HistoryReadResultCollection results,
out DiagnosticInfoCollection diagnosticInfos);
ClientBase.ValidateResponse(results, nodesToRead);
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);
if (StatusCode.IsBad(results[0].StatusCode))
{
throw new ServiceResultException(results[0].StatusCode);
}
HistoryData values = ExtensionObject.ToEncodeable(results[0].HistoryData) as HistoryData;
foreach (var value in values.DataValues)
{
yield return (T)value.Value;
}
}
#endregion ReadHistory Support
#region BrowseNode Support
///
/// 浏览一个节点的引用
///
/// 节点值
/// 引用节点描述
public ReferenceDescription[] BrowseNodeReference(string tag)
{
NodeId sourceId = new NodeId(tag);
// 该节点可以读取到方法
BrowseDescription nodeToBrowse1 = new BrowseDescription();
nodeToBrowse1.NodeId = sourceId;
nodeToBrowse1.BrowseDirection = BrowseDirection.Forward;
nodeToBrowse1.ReferenceTypeId = ReferenceTypeIds.Aggregates;
nodeToBrowse1.IncludeSubtypes = true;
nodeToBrowse1.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable | NodeClass.Method);
nodeToBrowse1.ResultMask = (uint)BrowseResultMask.All;
// 该节点无论怎么样都读取不到方法
// find all nodes organized by the node.
BrowseDescription nodeToBrowse2 = new BrowseDescription();
nodeToBrowse2.NodeId = sourceId;
nodeToBrowse2.BrowseDirection = BrowseDirection.Forward;
nodeToBrowse2.ReferenceTypeId = ReferenceTypeIds.Organizes;
nodeToBrowse2.IncludeSubtypes = true;
nodeToBrowse2.NodeClassMask = (uint)(NodeClass.Object | NodeClass.Variable);
nodeToBrowse2.ResultMask = (uint)BrowseResultMask.All;
BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection();
nodesToBrowse.Add(nodeToBrowse1);
nodesToBrowse.Add(nodeToBrowse2);
// fetch references from the server.
ReferenceDescriptionCollection references = FormUtils.Browse(m_session, nodesToBrowse, false);
return references.ToArray();
}
#endregion BrowseNode Support
#region Read Attributes Support
///
/// 读取一个节点的所有属性
///
/// 节点信息
/// 节点的特性值
public OpcNodeAttribute[] ReadNoteAttributes(string tag)
{
NodeId sourceId = new NodeId(tag);
ReadValueIdCollection nodesToRead = new ReadValueIdCollection();
// attempt to read all possible attributes.
// 尝试着去读取所有可能的特性
for (uint ii = Attributes.NodeClass; ii <= Attributes.UserExecutable; ii++)
{
ReadValueId nodeToRead = new ReadValueId();
nodeToRead.NodeId = sourceId;
nodeToRead.AttributeId = ii;
nodesToRead.Add(nodeToRead);
}
int startOfProperties = nodesToRead.Count;
// find all of the pror of the node.
BrowseDescription nodeToBrowse1 = new BrowseDescription();
nodeToBrowse1.NodeId = sourceId;
nodeToBrowse1.BrowseDirection = BrowseDirection.Forward;
nodeToBrowse1.ReferenceTypeId = ReferenceTypeIds.HasProperty;
nodeToBrowse1.IncludeSubtypes = true;
nodeToBrowse1.NodeClassMask = 0;
nodeToBrowse1.ResultMask = (uint)BrowseResultMask.All;
BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection();
nodesToBrowse.Add(nodeToBrowse1);
// fetch property references from the server.
ReferenceDescriptionCollection references = FormUtils.Browse(m_session, nodesToBrowse, false);
if (references == null)
{
return new OpcNodeAttribute[0];
}
for (int ii = 0; ii < references.Count; ii++)
{
// ignore external references.
if (references[ii].NodeId.IsAbsolute)
{
continue;
}
ReadValueId nodeToRead = new ReadValueId();
nodeToRead.NodeId = (NodeId)references[ii].NodeId;
nodeToRead.AttributeId = Attributes.Value;
nodesToRead.Add(nodeToRead);
}
// read all values.
DataValueCollection results = null;
DiagnosticInfoCollection diagnosticInfos = null;
m_session.Read(
null,
0,
TimestampsToReturn.Neither,
nodesToRead,
out results,
out diagnosticInfos);
ClientBase.ValidateResponse(results, nodesToRead);
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);
// process results.
List nodeAttribute = new List();
for (int ii = 0; ii < results.Count; ii++)
{
OpcNodeAttribute item = new OpcNodeAttribute();
// process attribute value.
if (ii < startOfProperties)
{
// ignore attributes which are invalid for the node.
if (results[ii].StatusCode == StatusCodes.BadAttributeIdInvalid)
{
continue;
}
// get the name of the attribute.
item.Name = Attributes.GetBrowseName(nodesToRead[ii].AttributeId);
// display any unexpected error.
if (StatusCode.IsBad(results[ii].StatusCode))
{
item.Type = Utils.Format("{0}", Attributes.GetDataTypeId(nodesToRead[ii].AttributeId));
item.Value = Utils.Format("{0}", results[ii].StatusCode);
}
// display the value.
else
{
TypeInfo typeInfo = TypeInfo.Construct(results[ii].Value);
item.Type = typeInfo.BuiltInType.ToString();
if (typeInfo.ValueRank >= ValueRanks.OneOrMoreDimensions)
{
item.Type += "[]";
}
item.Value = results[ii].Value;//Utils.Format("{0}", results[ii].Value);
}
}
// process property value.
else
{
// ignore properties which are invalid for the node.
if (results[ii].StatusCode == StatusCodes.BadNodeIdUnknown)
{
continue;
}
// get the name of the property.
item.Name = Utils.Format("{0}", references[ii - startOfProperties]);
// display any unexpected error.
if (StatusCode.IsBad(results[ii].StatusCode))
{
item.Type = String.Empty;
item.Value = Utils.Format("{0}", results[ii].StatusCode);
}
// display the value.
else
{
TypeInfo typeInfo = TypeInfo.Construct(results[ii].Value);
item.Type = typeInfo.BuiltInType.ToString();
if (typeInfo.ValueRank >= ValueRanks.OneOrMoreDimensions)
{
item.Type += "[]";
}
item.Value = results[ii].Value; //Utils.Format("{0}", results[ii].Value);
}
}
nodeAttribute.Add(item);
}
return nodeAttribute.ToArray();
}
///
/// 读取一个节点的所有属性
///
/// 节点值
/// 所有的数据
public DataValue[] ReadNoteDataValueAttributes(string tag)
{
NodeId sourceId = new NodeId(tag);
ReadValueIdCollection nodesToRead = new ReadValueIdCollection();
// attempt to read all possible attributes.
// 尝试着去读取所有可能的特性
for (uint ii = Attributes.NodeId; ii <= Attributes.UserExecutable; ii++)
{
ReadValueId nodeToRead = new ReadValueId();
nodeToRead.NodeId = sourceId;
nodeToRead.AttributeId = ii;
nodesToRead.Add(nodeToRead);
}
int startOfProperties = nodesToRead.Count;
// find all of the pror of the node.
BrowseDescription nodeToBrowse1 = new BrowseDescription();
nodeToBrowse1.NodeId = sourceId;
nodeToBrowse1.BrowseDirection = BrowseDirection.Forward;
nodeToBrowse1.ReferenceTypeId = ReferenceTypeIds.HasProperty;
nodeToBrowse1.IncludeSubtypes = true;
nodeToBrowse1.NodeClassMask = 0;
nodeToBrowse1.ResultMask = (uint)BrowseResultMask.All;
BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection();
nodesToBrowse.Add(nodeToBrowse1);
// fetch property references from the server.
ReferenceDescriptionCollection references = FormUtils.Browse(m_session, nodesToBrowse, false);
if (references == null)
{
return new DataValue[0];
}
for (int ii = 0; ii < references.Count; ii++)
{
// ignore external references.
if (references[ii].NodeId.IsAbsolute)
{
continue;
}
ReadValueId nodeToRead = new ReadValueId();
nodeToRead.NodeId = (NodeId)references[ii].NodeId;
nodeToRead.AttributeId = Attributes.Value;
nodesToRead.Add(nodeToRead);
}
// read all values.
DataValueCollection results = null;
DiagnosticInfoCollection diagnosticInfos = null;
m_session.Read(
null,
0,
TimestampsToReturn.Neither,
nodesToRead,
out results,
out diagnosticInfos);
ClientBase.ValidateResponse(results, nodesToRead);
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);
return results.ToArray();
}
#endregion Read Attributes Support
#region Method Call Support
///
/// call a server method
///
/// 方法的父节点tag
/// 方法的节点tag
/// 传递的参数
/// 输出的结果值
public object[] CallMethodByNodeId(string tagParent, string tag, params object[] args)
{
if (m_session == null)
{
return null;
}
IList