|
@@ -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.Dynamic;
|
|
|
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;
|
|
@@ -9,6 +10,7 @@ using YSAI.Core.@interface;
|
|
|
using YSAI.Core.virtualAddress;
|
|
using YSAI.Core.virtualAddress;
|
|
|
using YSAI.Log;
|
|
using YSAI.Log;
|
|
|
using YSAI.Opc.ua.client.unility;
|
|
using YSAI.Opc.ua.client.unility;
|
|
|
|
|
+using YSAI.Opc.unility;
|
|
|
using YSAI.Unility;
|
|
using YSAI.Unility;
|
|
|
using static YSAI.Opc.ua.client.OpcUaClientData;
|
|
using static YSAI.Opc.ua.client.OpcUaClientData;
|
|
|
|
|
|
|
@@ -70,6 +72,8 @@ namespace YSAI.Opc.ua.client
|
|
|
{
|
|
{
|
|
|
//设置参数
|
|
//设置参数
|
|
|
this.basics = basics;
|
|
this.basics = basics;
|
|
|
|
|
+
|
|
|
|
|
+ SubscribeStateCheck();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#region 公共属性
|
|
#region 公共属性
|
|
@@ -83,11 +87,6 @@ namespace YSAI.Opc.ua.client
|
|
|
|
|
|
|
|
#region 私有属性
|
|
#region 私有属性
|
|
|
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// 默认标签
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- private string DefaultTag = "YSAI";
|
|
|
|
|
-
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// 基础数据
|
|
/// 基础数据
|
|
|
/// </summary>
|
|
/// </summary>
|
|
@@ -461,6 +460,41 @@ namespace YSAI.Opc.ua.client
|
|
|
return readResponse.Results[0];
|
|
return readResponse.Results[0];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 订阅状态监测
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="token">生命周期</param>
|
|
|
|
|
+ /// <returns></returns>
|
|
|
|
|
+ private Task SubscribeStateCheck(CancellationToken? token = null)
|
|
|
|
|
+ {
|
|
|
|
|
+ return Task.Run(() =>
|
|
|
|
|
+ {
|
|
|
|
|
+ while (true)
|
|
|
|
|
+ {
|
|
|
|
|
+ //订阅数据为空则直接跳过
|
|
|
|
|
+ if (allSubscriptions == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ Thread.Sleep(30000);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //需要重新订阅的地址
|
|
|
|
|
+ foreach (var item in allSubscriptions)
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (var monitoredItem in item.Value.MonitoredItems)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!monitoredItem.Status.Created)
|
|
|
|
|
+ {
|
|
|
|
|
+ LogHelper.Error($"{monitoredItem.DisplayName} 地址未能在服务器上创建订阅,请检查地址是否正常", $"{TAG}/SubscribeCheckStatus.log");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Thread.Sleep(30000);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, token ?? CancellationToken.None);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
#endregion 私有函数
|
|
#endregion 私有函数
|
|
|
|
|
|
|
|
#region 公共函数
|
|
#region 公共函数
|
|
@@ -1088,7 +1122,8 @@ namespace YSAI.Opc.ua.client
|
|
|
monitoredItem.AttributeId = Attributes.Value; //要监视的属性
|
|
monitoredItem.AttributeId = Attributes.Value; //要监视的属性
|
|
|
monitoredItem.SamplingInterval = basics.SamplingInterval; //多少秒采集一次数据
|
|
monitoredItem.SamplingInterval = basics.SamplingInterval; //多少秒采集一次数据
|
|
|
monitoredItem.DiscardOldest = true; //当队列满时清空较早的队列数据
|
|
monitoredItem.DiscardOldest = true; //当队列满时清空较早的队列数据
|
|
|
- monitoredItem.QueueSize = 1000; //队列大小
|
|
|
|
|
|
|
+ monitoredItem.QueueSize = (uint)basics.QueueSize; //队列大小
|
|
|
|
|
+ monitoredItem.DisplayName = item.AddressName; //被监视项目的显示名称
|
|
|
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); //添加至集合
|
|
|
}
|
|
}
|
|
@@ -1119,15 +1154,14 @@ namespace YSAI.Opc.ua.client
|
|
|
subscription.Handle = this; //分配给订阅的本地句柄
|
|
subscription.Handle = this; //分配给订阅的本地句柄
|
|
|
subscription.PublishingEnabled = true;//是否启用发布
|
|
subscription.PublishingEnabled = true;//是否启用发布
|
|
|
subscription.PublishingInterval = basics.PublishingInterval; //出版间隔
|
|
subscription.PublishingInterval = basics.PublishingInterval; //出版间隔
|
|
|
- subscription.KeepAliveCount = 10; //保活计数
|
|
|
|
|
- subscription.LifetimeCount = 100; //寿命计数
|
|
|
|
|
|
|
+ subscription.KeepAliveCount = (uint)basics.KeepAliveCount; //保活计数
|
|
|
|
|
+ subscription.LifetimeCount = (uint)basics.LifetimeCount; //寿命计数
|
|
|
subscription.MaxNotificationsPerPublish = (uint)basics.PublishingInterval; //每个发布请求的最大通知数
|
|
subscription.MaxNotificationsPerPublish = (uint)basics.PublishingInterval; //每个发布请求的最大通知数
|
|
|
subscription.DisplayName = Tag; //订阅的显示名称
|
|
subscription.DisplayName = Tag; //订阅的显示名称
|
|
|
subscription.TimestampsToReturn = TimestampsToReturn.Both; //与通知消息一起返回的时间戳
|
|
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); }; //订阅状态
|
|
|
subscription.PublishStatusChanged += Subscription_PublishStatusChanged; //指示订阅的发布状态已停止或已恢复
|
|
subscription.PublishStatusChanged += Subscription_PublishStatusChanged; //指示订阅的发布状态已停止或已恢复
|
|
|
- //监控项集合
|
|
|
|
|
- List<MonitoredItem> monitoredItems = new List<MonitoredItem>();
|
|
|
|
|
|
|
+ List<MonitoredItem> monitoredItems = new List<MonitoredItem>();//监控项集合
|
|
|
|
|
|
|
|
//循环添加
|
|
//循环添加
|
|
|
foreach (var item in Nodes)
|
|
foreach (var item in Nodes)
|
|
@@ -1139,7 +1173,8 @@ namespace YSAI.Opc.ua.client
|
|
|
monitoredItem.AttributeId = Attributes.Value; //要监视的属性
|
|
monitoredItem.AttributeId = Attributes.Value; //要监视的属性
|
|
|
monitoredItem.SamplingInterval = basics.SamplingInterval; //多少秒采集一次数据
|
|
monitoredItem.SamplingInterval = basics.SamplingInterval; //多少秒采集一次数据
|
|
|
monitoredItem.DiscardOldest = true; //当队列满时清空较早的队列数据
|
|
monitoredItem.DiscardOldest = true; //当队列满时清空较早的队列数据
|
|
|
- monitoredItem.QueueSize = 1000; //队列大小
|
|
|
|
|
|
|
+ monitoredItem.QueueSize = (uint)basics.QueueSize; //队列大小
|
|
|
|
|
+ monitoredItem.DisplayName = item.AddressName; //被监视项目的显示名称
|
|
|
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); //添加至集合
|
|
|
}
|
|
}
|
|
@@ -1230,7 +1265,7 @@ namespace YSAI.Opc.ua.client
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
//在数组中移除此项
|
|
//在数组中移除此项
|
|
|
- allSubscriptions.Remove(alls.Key, out Subscription? subscription);
|
|
|
|
|
|
|
+ allSubscriptions.Remove(alls.Key, out _);
|
|
|
}
|
|
}
|
|
|
//移除后立马跳到上一级,防止性能消耗
|
|
//移除后立马跳到上一级,防止性能消耗
|
|
|
break;
|
|
break;
|
|
@@ -1368,7 +1403,7 @@ namespace YSAI.Opc.ua.client
|
|
|
ConcurrentDictionary<string, AddressValue> param = new ConcurrentDictionary<string, AddressValue>();
|
|
ConcurrentDictionary<string, AddressValue> param = new ConcurrentDictionary<string, AddressValue>();
|
|
|
//处理数据
|
|
//处理数据
|
|
|
AddressValue addressValue = YSAI.Core.handler.AddressHandler.ExecuteDispose(queueData.addressDetails, value); //数据
|
|
AddressValue addressValue = YSAI.Core.handler.AddressHandler.ExecuteDispose(queueData.addressDetails, value); //数据
|
|
|
- //添加或更新
|
|
|
|
|
|
|
+ //添加或更新
|
|
|
param.AddOrUpdate(queueData.addressDetails.AddressName, addressValue, (k, v) => addressValue);
|
|
param.AddOrUpdate(queueData.addressDetails.AddressName, addressValue, (k, v) => addressValue);
|
|
|
|
|
|
|
|
//响应
|
|
//响应
|
|
@@ -1489,7 +1524,7 @@ namespace YSAI.Opc.ua.client
|
|
|
endpoint.Update(endpointDescription);
|
|
endpoint.Update(endpointDescription);
|
|
|
|
|
|
|
|
//通过调用CreateSession服务创建与服务器的新通信会话
|
|
//通过调用CreateSession服务创建与服务器的新通信会话
|
|
|
- clientSession = Session.Create(AC, endpoint, true, false, $"{AC.ApplicationName}.{Guid.NewGuid().ToNString()}", (uint)basics.Timeout, UserIdentity, new string[] { }).WaitAsync(new TimeSpan(0, 0, 0, 0, basics.Timeout)).Result;
|
|
|
|
|
|
|
+ clientSession = Session.Create(AC, endpoint, true, false, $"{AC.ApplicationName}.{Guid.NewGuid().ToUpperNString()}", (uint)basics.Timeout, UserIdentity, new string[] { }).WaitAsync(new TimeSpan(0, 0, 0, 0, basics.Timeout)).Result;
|
|
|
|
|
|
|
|
//当会话关闭,则关闭订阅
|
|
//当会话关闭,则关闭订阅
|
|
|
clientSession.DeleteSubscriptionsOnClose = true;
|
|
clientSession.DeleteSubscriptionsOnClose = true;
|
|
@@ -1769,8 +1804,16 @@ namespace YSAI.Opc.ua.client
|
|
|
Depart("Subscribe");
|
|
Depart("Subscribe");
|
|
|
try
|
|
try
|
|
|
{
|
|
{
|
|
|
- ConcurrentDictionary<string, Address> PARAM = new ConcurrentDictionary<string, Address>();
|
|
|
|
|
- PARAM.TryAdd(DefaultTag, address);
|
|
|
|
|
|
|
+ ConcurrentDictionary<string, Address> PARAM = null;
|
|
|
|
|
+ if (basics.SubscribeSingleGroupMaxCount != -1)
|
|
|
|
|
+ {
|
|
|
|
|
+ PARAM = OpcHelper.SubscribeAddressHandle(address, basics.SubscribeSingleGroupMaxCount);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ PARAM = new ConcurrentDictionary<string, Address>();
|
|
|
|
|
+ PARAM.TryAdd(Guid.NewGuid().ToUpperNString(), address);
|
|
|
|
|
+ }
|
|
|
OperateResult operateResult = AddSubscribe(PARAM);
|
|
OperateResult operateResult = AddSubscribe(PARAM);
|
|
|
return Break("Subscribe", operateResult.State, operateResult.Message);
|
|
return Break("Subscribe", operateResult.State, operateResult.Message);
|
|
|
}
|
|
}
|