|
@@ -2,6 +2,7 @@
|
|
|
using Opc.Ua.Client;
|
|
using Opc.Ua.Client;
|
|
|
using Opc.Ua.Configuration;
|
|
using Opc.Ua.Configuration;
|
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Concurrent;
|
|
|
|
|
+using System.Reflection.Metadata;
|
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
|
using YSAI.Core.data;
|
|
using YSAI.Core.data;
|
|
|
using YSAI.Core.@enum;
|
|
using YSAI.Core.@enum;
|
|
@@ -10,6 +11,7 @@ using YSAI.Core.virtualAddress;
|
|
|
using YSAI.Log;
|
|
using YSAI.Log;
|
|
|
using YSAI.Opc.ua.client.unility;
|
|
using YSAI.Opc.ua.client.unility;
|
|
|
using YSAI.Unility;
|
|
using YSAI.Unility;
|
|
|
|
|
+using static Opc.Ua.Client.SessionReconnectHandler;
|
|
|
using static YSAI.Opc.ua.client.OpcUaClientData;
|
|
using static YSAI.Opc.ua.client.OpcUaClientData;
|
|
|
|
|
|
|
|
namespace YSAI.Opc.ua.client
|
|
namespace YSAI.Opc.ua.client
|
|
@@ -136,21 +138,24 @@ namespace YSAI.Opc.ua.client
|
|
|
{
|
|
{
|
|
|
if (!object.ReferenceEquals(session, this.clientSession))
|
|
if (!object.ReferenceEquals(session, this.clientSession))
|
|
|
{
|
|
{
|
|
|
- OnEventHandler(this, new EventResult(false, Steps.事件对象不匹配.ToString()));
|
|
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
if (ServiceResult.IsBad(e.Status))
|
|
if (ServiceResult.IsBad(e.Status))
|
|
|
{
|
|
{
|
|
|
if (reconnectPeriod <= 0)
|
|
if (reconnectPeriod <= 0)
|
|
|
{
|
|
{
|
|
|
|
|
+ OnEventHandler(this, new EventResult(false, $"[ {Steps.异常断开} ]连接错误 ({e.Status})"));
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ OnEventHandler(this, new EventResult(false, $"[ {Steps.异常断开} ]重连 {ConnectedCounet++} 次 ..."));
|
|
|
|
|
+
|
|
|
IsConnected = false;
|
|
IsConnected = false;
|
|
|
|
|
+
|
|
|
if (sessionReconnectHandler == null)
|
|
if (sessionReconnectHandler == null)
|
|
|
{
|
|
{
|
|
|
sessionReconnectHandler = new SessionReconnectHandler();
|
|
sessionReconnectHandler = new SessionReconnectHandler();
|
|
|
- sessionReconnectHandler.BeginReconnect(session, reconnectPeriod * 1000, Server_ReconnectComplete);
|
|
|
|
|
- OnEventHandler(this, new EventResult(false, $"[ {Steps.异常断开} ]启动重连"));
|
|
|
|
|
|
|
+ var state = sessionReconnectHandler.BeginReconnect(session, reconnectPeriod * 1000, Server_ReconnectComplete);
|
|
|
}
|
|
}
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -180,14 +185,61 @@ namespace YSAI.Opc.ua.client
|
|
|
{
|
|
{
|
|
|
if (!ReferenceEquals(sender, sessionReconnectHandler))
|
|
if (!ReferenceEquals(sender, sessionReconnectHandler))
|
|
|
{
|
|
{
|
|
|
- OnEventHandler(this, new EventResult(false, Steps.事件对象不匹配.ToString()));
|
|
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+ // 仅在需要重新连接时应用会话
|
|
|
|
|
+ if (sessionReconnectHandler.Session != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!ReferenceEquals(clientSession, sessionReconnectHandler.Session))
|
|
|
|
|
+ {
|
|
|
|
|
+ var session = clientSession;
|
|
|
|
|
+ session.KeepAlive -= Session_KeepAlive;
|
|
|
|
|
+ clientSession = sessionReconnectHandler.Session as Session;
|
|
|
|
|
+ clientSession.KeepAlive += Session_KeepAlive;
|
|
|
|
|
+ Utils.SilentDispose(session);
|
|
|
|
|
+
|
|
|
|
|
+ //说明存在订阅
|
|
|
|
|
+ if (allSubscriptions != null && allSubscriptions.Count > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ //先移除订阅
|
|
|
|
|
+ if (!clientSession.RemoveSubscriptions(allSubscriptions.Values.ToArray()))
|
|
|
|
|
+ {
|
|
|
|
|
+ OnEventHandler(this, new EventResult(false, Steps.订阅通知.ToString(), $"设备重连成功,移除会话中的订阅项失败"));
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (var item in allSubscriptions)
|
|
|
|
|
+ {
|
|
|
|
|
+ //重新添加订阅
|
|
|
|
|
+ if (!clientSession.AddSubscription(item.Value))
|
|
|
|
|
+ {
|
|
|
|
|
+ OnEventHandler(this, new EventResult(false, Steps.订阅通知.ToString(), $"设备重连成功,重新添加订阅失败,TAG = {item.Key}"));
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ //在服务器上创建订阅并添加所有监视项
|
|
|
|
|
+ item.Value.Create();
|
|
|
|
|
+
|
|
|
|
|
+ //将任何更改用于订阅
|
|
|
|
|
+ item.Value.ApplyChanges();
|
|
|
|
|
|
|
|
- clientSession = sessionReconnectHandler.Session;
|
|
|
|
|
- sessionReconnectHandler.Dispose();
|
|
|
|
|
- sessionReconnectHandler = null;
|
|
|
|
|
- OnEventHandler(this, new EventResult(true, Steps.重连成功.ToString()));
|
|
|
|
|
|
|
+ //初始重连次数
|
|
|
|
|
+ ConnectedCounet = 1;
|
|
|
|
|
+
|
|
|
|
|
+ OnEventHandler(this, new EventResult(true, Steps.重连成功.ToString()));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ //初始重连次数
|
|
|
|
|
+ ConnectedCounet = 1;
|
|
|
|
|
+
|
|
|
|
|
+ OnEventHandler(this, new EventResult(true, Steps.重连成功.ToString()));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
catch (Exception ex)
|
|
catch (Exception ex)
|
|
|
{
|
|
{
|
|
@@ -857,7 +909,9 @@ namespace YSAI.Opc.ua.client
|
|
|
MonitoredItem monitoredItem = new MonitoredItem(allSubscriptions[Tag].DefaultItem);
|
|
MonitoredItem monitoredItem = new MonitoredItem(allSubscriptions[Tag].DefaultItem);
|
|
|
monitoredItem.StartNodeId = new NodeId(item.AddressName); //标识要监视的节点的浏览路径的开始节点
|
|
monitoredItem.StartNodeId = new NodeId(item.AddressName); //标识要监视的节点的浏览路径的开始节点
|
|
|
monitoredItem.AttributeId = Attributes.Value; //要监视的属性
|
|
monitoredItem.AttributeId = Attributes.Value; //要监视的属性
|
|
|
- monitoredItem.SamplingInterval = basics.SamplingInterval; //采样间隔
|
|
|
|
|
|
|
+ monitoredItem.SamplingInterval = basics.SamplingInterval; //多少秒采集一次数据
|
|
|
|
|
+ monitoredItem.DiscardOldest = true; //当队列满时清空较早的队列数据
|
|
|
|
|
+ monitoredItem.QueueSize = 1000; //队列大小
|
|
|
monitoredItem.Notification += delegate (MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { OnMonitoredItemNotification(monitoredItem, e, item); }; //重写事件添加一个参数
|
|
monitoredItem.Notification += delegate (MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { OnMonitoredItemNotification(monitoredItem, e, item); }; //重写事件添加一个参数
|
|
|
monitoredItems?.Add(monitoredItem); //添加至集合
|
|
monitoredItems?.Add(monitoredItem); //添加至集合
|
|
|
}
|
|
}
|
|
@@ -875,6 +929,9 @@ namespace YSAI.Opc.ua.client
|
|
|
|
|
|
|
|
//在服务器上创建订阅并添加所有监视项
|
|
//在服务器上创建订阅并添加所有监视项
|
|
|
allSubscriptions[Tag].Create();
|
|
allSubscriptions[Tag].Create();
|
|
|
|
|
+
|
|
|
|
|
+ //将任何更改用于订阅
|
|
|
|
|
+ allSubscriptions[Tag].ApplyChanges();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
else
|
|
else
|
|
@@ -882,13 +939,14 @@ namespace YSAI.Opc.ua.client
|
|
|
//此标签不存在,直接创建
|
|
//此标签不存在,直接创建
|
|
|
//订阅
|
|
//订阅
|
|
|
Subscription subscription = new Subscription(clientSession?.DefaultSubscription);
|
|
Subscription subscription = new Subscription(clientSession?.DefaultSubscription);
|
|
|
|
|
+ subscription.Handle = this; //分配给订阅的本地句柄
|
|
|
subscription.PublishingEnabled = true;//是否启用发布
|
|
subscription.PublishingEnabled = true;//是否启用发布
|
|
|
subscription.PublishingInterval = basics.PublishingInterval; //出版间隔
|
|
subscription.PublishingInterval = basics.PublishingInterval; //出版间隔
|
|
|
- subscription.KeepAliveCount = uint.MaxValue; //保活计数
|
|
|
|
|
- subscription.LifetimeCount = uint.MaxValue; //寿命计数
|
|
|
|
|
- subscription.MaxNotificationsPerPublish = uint.MaxValue; //每个发布请求的最大通知数
|
|
|
|
|
- subscription.Priority = 100; //分配给订阅的优先级
|
|
|
|
|
|
|
+ subscription.KeepAliveCount = 10; //保活计数
|
|
|
|
|
+ subscription.LifetimeCount = 100; //寿命计数
|
|
|
|
|
+ subscription.MaxNotificationsPerPublish = (uint)basics.PublishingInterval; //每个发布请求的最大通知数
|
|
|
subscription.DisplayName = Tag; //订阅的显示名称
|
|
subscription.DisplayName = Tag; //订阅的显示名称
|
|
|
|
|
+ subscription.TimestampsToReturn = TimestampsToReturn.Both; //与通知消息一起返回的时间戳
|
|
|
subscription.StateChanged += delegate (Subscription subscription, SubscriptionStateChangedEventArgs e) { Subscription_StateChanged(subscription, e, Tag); }; //订阅状态
|
|
subscription.StateChanged += delegate (Subscription subscription, SubscriptionStateChangedEventArgs e) { Subscription_StateChanged(subscription, e, Tag); }; //订阅状态
|
|
|
|
|
|
|
|
//监控项集合
|
|
//监控项集合
|
|
@@ -902,7 +960,9 @@ namespace YSAI.Opc.ua.client
|
|
|
MonitoredItem monitoredItem = new MonitoredItem(subscription.DefaultItem);
|
|
MonitoredItem monitoredItem = new MonitoredItem(subscription.DefaultItem);
|
|
|
monitoredItem.StartNodeId = new NodeId(item.AddressName); //标识要监视的节点的浏览路径的开始节点
|
|
monitoredItem.StartNodeId = new NodeId(item.AddressName); //标识要监视的节点的浏览路径的开始节点
|
|
|
monitoredItem.AttributeId = Attributes.Value; //要监视的属性
|
|
monitoredItem.AttributeId = Attributes.Value; //要监视的属性
|
|
|
- monitoredItem.SamplingInterval = basics.SamplingInterval; //采样间隔
|
|
|
|
|
|
|
+ monitoredItem.SamplingInterval = basics.SamplingInterval; //多少秒采集一次数据
|
|
|
|
|
+ monitoredItem.DiscardOldest = true; //当队列满时清空较早的队列数据
|
|
|
|
|
+ monitoredItem.QueueSize = 1000; //队列大小
|
|
|
monitoredItem.Notification += delegate (MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { OnMonitoredItemNotification(monitoredItem, e, item); }; //重写事件添加一个参数
|
|
monitoredItem.Notification += delegate (MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { OnMonitoredItemNotification(monitoredItem, e, item); }; //重写事件添加一个参数
|
|
|
monitoredItems?.Add(monitoredItem); //添加至集合
|
|
monitoredItems?.Add(monitoredItem); //添加至集合
|
|
|
}
|
|
}
|
|
@@ -914,10 +974,12 @@ namespace YSAI.Opc.ua.client
|
|
|
{
|
|
{
|
|
|
return Break("AddSubscribe", false, "会话添加订阅失败");
|
|
return Break("AddSubscribe", false, "会话添加订阅失败");
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
//在服务器上创建订阅并添加所有监视项
|
|
//在服务器上创建订阅并添加所有监视项
|
|
|
subscription.Create();
|
|
subscription.Create();
|
|
|
|
|
|
|
|
|
|
+ //将任何更改用于订阅
|
|
|
|
|
+ subscription.ApplyChanges();
|
|
|
|
|
+
|
|
|
//把此订阅添加到集合,方便后续移除订阅(当存在此键则更新值,不存在则添加)
|
|
//把此订阅添加到集合,方便后续移除订阅(当存在此键则更新值,不存在则添加)
|
|
|
allSubscriptions?.AddOrUpdate(Tag, subscription, (k, v) => subscription);
|
|
allSubscriptions?.AddOrUpdate(Tag, subscription, (k, v) => subscription);
|
|
|
}
|
|
}
|
|
@@ -974,7 +1036,9 @@ namespace YSAI.Opc.ua.client
|
|
|
{
|
|
{
|
|
|
string Message = EnumParse(e.Status.ToString());
|
|
string Message = EnumParse(e.Status.ToString());
|
|
|
string MessageJson = "{" + $"\"Uri\":\"{basics.ServerUrl}\",\"CustomName\":\"{basics.CustomName}\",\"StatusCode\":\"{e.Status}\",\"StatusMessage\":\"{Message}\"" + "}";
|
|
string MessageJson = "{" + $"\"Uri\":\"{basics.ServerUrl}\",\"CustomName\":\"{basics.CustomName}\",\"StatusCode\":\"{e.Status}\",\"StatusMessage\":\"{Message}\"" + "}";
|
|
|
|
|
+#if DEBUG
|
|
|
OnEventHandler(this, new EventResult(true, Message, MessageJson));
|
|
OnEventHandler(this, new EventResult(true, Message, MessageJson));
|
|
|
|
|
+#endif
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|