|
|
@@ -9,6 +9,7 @@ using YSAI.Core.virtualAddress;
|
|
|
using YSAI.Log;
|
|
|
using YSAI.Opc.ua.client.unility;
|
|
|
using YSAI.Unility;
|
|
|
+using static System.Net.Mime.MediaTypeNames;
|
|
|
using static YSAI.Opc.ua.client.OpcUaClientData;
|
|
|
|
|
|
namespace YSAI.Opc.ua.client
|
|
|
@@ -230,6 +231,8 @@ namespace YSAI.Opc.ua.client
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
+ #region 公共函数
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// 获取当前服务ID
|
|
|
/// </summary>
|
|
|
@@ -667,7 +670,8 @@ namespace YSAI.Opc.ua.client
|
|
|
return dataValues[1].WrappedValue.TypeInfo.BuiltInType;
|
|
|
}
|
|
|
return BuiltInType.Null;
|
|
|
- }
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
|
|
|
/// <summary>
|
|
|
/// 释放
|
|
|
@@ -759,6 +763,13 @@ namespace YSAI.Opc.ua.client
|
|
|
{
|
|
|
return Break("AddSubscribe", false, "节点已存在标签中,无须重复订阅");
|
|
|
}
|
|
|
+ //订阅节点集合
|
|
|
+ List<MonitoredItem> monitoredItems = null;
|
|
|
+ if (Nodes.Count > 0)
|
|
|
+ {
|
|
|
+ //实例化对象
|
|
|
+ monitoredItems = new List<MonitoredItem>();
|
|
|
+ }
|
|
|
//循环添加
|
|
|
foreach (var item in Nodes)
|
|
|
{
|
|
|
@@ -773,35 +784,36 @@ namespace YSAI.Opc.ua.client
|
|
|
monitoredItem.QueueSize = uint.MaxValue; //队列大小
|
|
|
monitoredItem.DiscardOldest = true; //当队列满了是否丢弃早期数据
|
|
|
monitoredItem.Handle = this; //本地监听地址
|
|
|
- allSubscriptions[Tag].AddItem(monitoredItem); //添加订阅
|
|
|
monitoredItem.Notification += delegate (MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { OnMonitoredItemNotification(monitoredItem, e, item); }; //重写事件添加一个参数
|
|
|
+ monitoredItems?.Add(monitoredItem); //添加至集合
|
|
|
}
|
|
|
|
|
|
if (Nodes.Count > 0)
|
|
|
{
|
|
|
+ //添加监控项
|
|
|
+ allSubscriptions[Tag].AddItems(monitoredItems);
|
|
|
//添加订阅
|
|
|
clientSession?.AddSubscription(allSubscriptions[Tag]);
|
|
|
- //在服务器端创建订阅
|
|
|
+ //在服务器上创建订阅并添加所有监视项
|
|
|
if (allSubscriptions[Tag].CreateAsync().Wait(basics.Timeout))
|
|
|
{
|
|
|
- // 在服务器端创建监控项
|
|
|
- if (allSubscriptions[Tag].ApplyChangesAsync().Wait(basics.Timeout))
|
|
|
+ // 将任何更改应用于订阅项
|
|
|
+ if (!allSubscriptions[Tag].ApplyChangesAsync().Wait(basics.Timeout))
|
|
|
{
|
|
|
- return Break("AddSubscribe", true);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- return Break("AddSubscribe", false, "在服务器端创建监控项超时");
|
|
|
+ allSubscriptions?.Remove(Tag, out _);
|
|
|
+ return Break("AddSubscribe", false, "将任何更改应用于订阅项超时");
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- return Break("AddSubscribe", false, "在服务器端创建订阅超时");
|
|
|
+ allSubscriptions?.Remove(Tag, out _);
|
|
|
+ return Break("AddSubscribe", false, "在服务器上创建订阅并添加所有监视项超时");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
+
|
|
|
//此标签不存在,直接创建
|
|
|
//订阅
|
|
|
Subscription subscription = new Subscription(clientSession?.DefaultSubscription);
|
|
|
@@ -813,6 +825,9 @@ namespace YSAI.Opc.ua.client
|
|
|
subscription.Priority = 1; //分配给订阅的优先级
|
|
|
subscription.DisplayName = Tag; //订阅的显示名称
|
|
|
subscription.TimestampsToReturn = TimestampsToReturn.Both; //与通知消息一起返回的时间戳
|
|
|
+ subscription.StateChanged +=delegate(Subscription subscription, SubscriptionStateChangedEventArgs e) { Subscription_StateChanged(subscription, e, Tag); }; //订阅状态
|
|
|
+ //监控项集合 //订阅节点集合
|
|
|
+ List<MonitoredItem> monitoredItems = new List<MonitoredItem>();
|
|
|
//循环添加
|
|
|
foreach (var item in Nodes)
|
|
|
{
|
|
|
@@ -827,29 +842,29 @@ namespace YSAI.Opc.ua.client
|
|
|
monitoredItem.QueueSize = uint.MaxValue; //队列大小
|
|
|
monitoredItem.DiscardOldest = true; //当队列满了是否丢弃早期数据
|
|
|
monitoredItem.Handle = this; //本地监听地址
|
|
|
- subscription.AddItem(monitoredItem); //添加订阅通知项
|
|
|
monitoredItem.Notification += delegate (MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { OnMonitoredItemNotification(monitoredItem, e, item); }; //重写事件添加一个参数
|
|
|
+ monitoredItems?.Add(monitoredItem); //添加至集合
|
|
|
}
|
|
|
+ //添加监控项
|
|
|
+ subscription.AddItems(monitoredItems);
|
|
|
//把此订阅添加到集合,方便后续移除订阅(当存在此键则更新值,不存在则添加)
|
|
|
allSubscriptions?.AddOrUpdate(Tag, subscription, (k, v) => subscription);
|
|
|
//添加订阅
|
|
|
clientSession?.AddSubscription(allSubscriptions[Tag]);
|
|
|
- //在服务器端创建订阅
|
|
|
+ //在服务器上创建订阅并添加所有监视项
|
|
|
if (allSubscriptions[Tag].CreateAsync().Wait(basics.Timeout))
|
|
|
{
|
|
|
- // 在服务器端创建监控项
|
|
|
- if (allSubscriptions[Tag].ApplyChangesAsync().Wait(basics.Timeout))
|
|
|
- {
|
|
|
- return Break("AddSubscribe", true);
|
|
|
- }
|
|
|
- else
|
|
|
+ // 将任何更改应用于订阅项
|
|
|
+ if (!allSubscriptions[Tag].ApplyChangesAsync().Wait(basics.Timeout))
|
|
|
{
|
|
|
- return Break("AddSubscribe", false, "在服务器端创建监控项超时");
|
|
|
+ allSubscriptions?.Remove(Tag, out _);
|
|
|
+ return Break("AddSubscribe", false, "将任何更改应用于订阅项超时");
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- return Break("AddSubscribe", false, "在服务器端创建订阅超时");
|
|
|
+ allSubscriptions?.Remove(Tag, out _);
|
|
|
+ return Break("AddSubscribe", false, "在服务器上创建订阅并添加所有监视项超时");
|
|
|
}
|
|
|
|
|
|
}
|
|
|
@@ -868,6 +883,55 @@ namespace YSAI.Opc.ua.client
|
|
|
return Break("AddSubscribe", false, ex.Message, Exc: ex);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// OPCUA 订阅状态集合
|
|
|
+ /// </summary>
|
|
|
+ private Dictionary<SubscriptionChangeMask, string> SubscribeStatus = new Dictionary<SubscriptionChangeMask, string>
|
|
|
+ {
|
|
|
+ { SubscriptionChangeMask.None, "订阅没有改变" },
|
|
|
+ { SubscriptionChangeMask.Created,"在服务器上创建订阅" },
|
|
|
+ { SubscriptionChangeMask.Deleted,"在服务器上删除订阅" },
|
|
|
+ { SubscriptionChangeMask.Modified,"在服务器上修改订阅" },
|
|
|
+ { SubscriptionChangeMask.ItemsAdded,"被监视的项目被添加到订阅中(但没有在服务器上创建)" },
|
|
|
+ { SubscriptionChangeMask.ItemsRemoved,"被监控的项目被删除到订阅(但没有在服务器上删除)" },
|
|
|
+ { SubscriptionChangeMask.ItemsCreated,"在服务器上创建监视项" },
|
|
|
+ { SubscriptionChangeMask.ItemsDeleted,"在服务器上删除监控项" },
|
|
|
+ { SubscriptionChangeMask.ItemsModified,"在服务器上修改监控项" },
|
|
|
+ { SubscriptionChangeMask.Transferred,"订阅在服务器上被转移" }
|
|
|
+ };
|
|
|
+ /// <summary>
|
|
|
+ /// 订阅状态枚举解析
|
|
|
+ /// </summary>
|
|
|
+ /// <param name=""></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private string EnumParse(string enumStr)
|
|
|
+ {
|
|
|
+ if (enumStr.Contains(","))
|
|
|
+ {
|
|
|
+ string[] Es = enumStr.Split(",");
|
|
|
+ string Info = string.Empty;
|
|
|
+ foreach (var item in Es)
|
|
|
+ {
|
|
|
+ Info += $"( {SubscribeStatus[(SubscriptionChangeMask)Enum.Parse(typeof(SubscriptionChangeMask), item)]} )";
|
|
|
+ }
|
|
|
+ return Info.Replace(")(", ") (");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return SubscribeStatus[(SubscriptionChangeMask)Enum.Parse(typeof(SubscriptionChangeMask), enumStr)];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //订阅状态
|
|
|
+ private void Subscription_StateChanged(Subscription subscription, SubscriptionStateChangedEventArgs e, string tag)
|
|
|
+ {
|
|
|
+ string Message = EnumParse(e.Status.ToString());
|
|
|
+ string MessageJson = "{" + $"\"Uri\":\"{basics.ServerUrl}\",\"CustomName\":\"{basics.CustomName}\",\"StatusCode\":\"{e.Status}\",\"StatusMessage\":\"{Message}\"" + "}";
|
|
|
+ LogHelper.Info(MessageJson.JsonFormatting(), ClassName + ".log");
|
|
|
+ OnEventHandler(this, new EventResult(true, Message, MessageJson));
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// 移除订阅 通过节点来移除订阅,移除指定节点的订阅,没有也返回成功
|
|
|
/// </summary>
|
|
|
@@ -1051,6 +1115,7 @@ namespace YSAI.Opc.ua.client
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
OnEventHandler(this, new EventResult(false, $"[ {Steps.订阅通知} ]异常:{ex.Message}"));
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1207,6 +1272,17 @@ namespace YSAI.Opc.ua.client
|
|
|
return Break("Off", false, "未连接");
|
|
|
}
|
|
|
|
|
|
+ if (allSubscriptions != null)
|
|
|
+ {
|
|
|
+ foreach (var item in allSubscriptions)
|
|
|
+ {
|
|
|
+ item.Value.Session.Close();
|
|
|
+ item.Value.Session.Dispose();
|
|
|
+ item.Value.Dispose();
|
|
|
+ }
|
|
|
+ allSubscriptions.Clear();
|
|
|
+ }
|
|
|
+
|
|
|
if (sessionReconnectHandler != null)
|
|
|
{
|
|
|
sessionReconnectHandler.Dispose();
|
|
|
@@ -1215,7 +1291,7 @@ namespace YSAI.Opc.ua.client
|
|
|
|
|
|
if (clientSession != null)
|
|
|
{
|
|
|
- if (StatusCode.IsGood(clientSession.Close(true)))
|
|
|
+ if (StatusCode.IsGood(clientSession.Close()))
|
|
|
{
|
|
|
clientSession.Dispose();
|
|
|
clientSession = null;
|
|
|
@@ -1232,11 +1308,12 @@ namespace YSAI.Opc.ua.client
|
|
|
foreach (var item in TaskArray)
|
|
|
{
|
|
|
item.Key.Cancel();
|
|
|
+ }
|
|
|
+ foreach (var item in TaskArray)
|
|
|
+ {
|
|
|
item.Value.Wait();
|
|
|
item.Value.Dispose();
|
|
|
}
|
|
|
- TaskArray.Clear();
|
|
|
- TaskArray = null;
|
|
|
}
|
|
|
//队列数据清空
|
|
|
if (DataQueue != null)
|
|
|
@@ -1245,6 +1322,8 @@ namespace YSAI.Opc.ua.client
|
|
|
DataQueue = null;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
IsConnected = false;
|
|
|
|
|
|
return Break("Off", true);
|