OPCUA 探讨(五)——客户端代码解读:监控变量值与报警事件
本系列文章:
OPCUA 探讨(一)——测试与开发环境搭建
OPCUA 探讨(二)——服务器节点初探
OPCUA 探讨(三)——客户端代码解读:基本配置与会话连接
OPCUA 探讨(四)——客户端代码解读:浏览服务器节点树
OPCUA 探讨(五)——客户端代码解读:监控变量值与报警事件
前文中我们探讨了如何浏览OPCUA服务器上的节点以及如何获取节点详细信息。
OPCUA 探讨(四)——客户端代码解读:浏览服务器节点树
该项目源码地址:https://gitee.com/zuoquangong/opcuaapi
本文我们将讨论如何实现服务器节点监控(monitor)功能。
这个功能可以实现变量值的定时自动刷新,不需要写循环和定时器。但是相比于读取功能,这个功能使用频率不高,因为其机制相对复杂,第三方开发的数据采集平台通常不会调用这个功能。

其刷新时间的最小值受OPCUA服务器端制约,对于Sinumerik828D而言,最小时间间隔为100ms(0.1秒),而且在该间隔下,最多能监控的变量数也有限制,通常为一百余个(和服务器版本有关),具体情况可以自行测试。
除了监控变量值,该功能还能用于监控数控系统的报警信息:

一、关于“监控”功能的三层结构
在实现该功能之前,我们要意识到OPCUA内部存在如下的三层关系结构:

客户端内可以建立多个会话(Session),每个会话可以创建多个订阅(Subscription),每个订阅里可以添加多个监控项目(MonitoredItem)。
以下是一个示例:

二、订阅(Subscription)
2.1 创建订阅
实现监控的前提是在已有会话(Session)中创建订阅(Subscription)。主要是创建一个订阅类实例,然后调用会话类的成员方法AddSubscription进行添加。
/// <summary>
/// 创建一个订阅
/// </summary>
/// <param name="name">订阅名称</param>
/// <returns></returns>
public Opc.Ua.Client.Subscription CreateNewSubscription(string name="")
{
if (current_session == null || Connected==false)
return null;
//new一个新的订阅subscription
Opc.Ua.Client.Subscription new_subscription = new Opc.Ua.Client.Subscription();
try
{
//current_subscription = new Opc.Ua.Client.Subscription(current_session.DefaultSubscription);
//给订阅的基本参数赋值
if(name=="")
{
new_subscription.DisplayName = "Subscription" + DateTime.Now;
}
else
{
new_subscription.DisplayName = name;
}
new_subscription.PublishingEnabled = true;
new_subscription.PublishingInterval = PublishingInterval;
//把订阅加入当前会话session
current_session.AddSubscription(new_subscription);
//在服务器端创建订阅
new_subscription.Create();
}
catch( Exception ex )
{
return null;
}
return new_subscription;
}
2.2 删除订阅
获取到要删除的会话实例,调用会话类的成员方法RemoveSubscription进行删除。
/// <summary>
/// 删除某一订阅
/// </summary>
/// <param name="subscription">要删除的订阅实例</param>
public void DeleteSubscription(Opc.Ua.Client.Subscription subscription)
{
current_session.RemoveSubscription(subscription);
return;
}
三、监控与监控项(MonitoredItem)
3.1 创建变量值监控
创建好订阅之后,可以在其中建立监控项目(MonitoredItem),关键是确定监控目标节点的ID(NodeId),这个信息可以通过浏览功能获取。基本流程就是创建MonitoredItem实例,设置好其属性,然后调用已有订阅的成员方法AddItem把该监控项加入订阅,然后执行订阅的另一个成员方法ApplyChanges,完成变更。
/// <summary>向一个已有订阅中添加监控项</summary>
/// <param name="subscription">指定的订阅,将包含该监控项</param>
/// <param name="nodeIdString">监控目标节点的ID</param>
/// <param name="itemName">添加的项的名称</param>
/// <param name="samplingInterval">采样间隔</param>
/// <returns>被添加的项</returns>
/// <exception cref="Exception">Throws and forwards any exception with short error description.</exception>
public MonitoredItem AddMonitoredItem(Subscription subscription,
string nodeIdString,
string itemName,
int samplingInterval,
MonitoredItemNotificationEventHandler Notification_MonitoredItem)
{
//创建一个监控项
MonitoredItem monitoredItem = new MonitoredItem();
//设置项目的名称,以便稍后分配项目和值;确保名称的不同
monitoredItem.DisplayName = itemName;
//设置监控项所监控节点的节点ID
monitoredItem.StartNodeId = nodeIdString;
//设置所监控的节点属性(此处监控的是节点的值)
monitoredItem.AttributeId = Attributes.Value;
//设置报告模式(reporting mode)
monitoredItem.MonitoringMode = MonitoringMode.Reporting;
//设置采样间隔 (1 = 尽可能快)
monitoredItem.SamplingInterval = samplingInterval;
//设置队列长度
monitoredItem.QueueSize = 1;
//是否在接收到新项时抛弃最老的项
monitoredItem.DiscardOldest = true;
//设置通知时间处理函数
monitoredItem.Notification += new MonitoredItemNotificationEventHandler(Notification_MonitoredItem);
//以上,监控项内容设置完毕
//将监控项加入指定订阅
subscription.AddItem(monitoredItem);
//应用变动
subscription.ApplyChanges();
return monitoredItem;
}
上述代码第 20 行有monitoredItem.AttributeId = Attributes.Value;,意味着这里监控的属性是变量值,其他诸如节点ID(NodeId)、节点类(NodeClass)、浏览名称(BrowseName)、展示名称(DisplayName)、描述(Description)等属性也可监控,但是很明显变量值才会实时变动,其他属性通常不会发生变化。
3.2 创建事件监控
除了变量值监控之外,另一个很重要的监控就是事件监控,它与我们数控系统的报警信息监控息息相关。在西门子Sinumerik数控系统里,报警信息可以通过事件监控的方式获取。
/// <summary>向一个已有订阅中添加事件监控项</summary>
/// <param name="subscription">指定的订阅,将包含该监控项</param>
/// <param name="nodeIdString">监控目标节点的ID</param>
/// <param name="itemName">添加的项的名称</param>
/// <param name="samplingInterval">采样间隔</param>
/// <returns>被添加的项</returns>
/// <exception cref="Exception">Throws and forwards any exception with short error description.</exception>
public MonitoredItem AddEventMonitoredItem(Subscription subscription,
string nodeIdString,
string itemName,
int samplingInterval,
MonitoredItemNotificationEventHandler Notification_MonitoredItem)
{
//创建一个监控项
MonitoredItem monitoredItem = new MonitoredItem();
//设置项目的名称,以便稍后分配项目和值;确保名称的不同
monitoredItem.DisplayName = itemName;
//设置监控项所监控节点的节点ID
monitoredItem.StartNodeId = nodeIdString;
//设置所监控的节点属性
//注意,这里的属性是“事件通知器”
monitoredItem.AttributeId = Attributes.EventNotifier;
//设置报告模式(reporting mode)
monitoredItem.MonitoringMode = MonitoringMode.Reporting;
//设置采样间隔 (1 = 尽可能快)
//monitoredItem.SamplingInterval = samplingInterval;
//设置队列长度
monitoredItem.QueueSize = 1000;
//是否在接收到新项时抛弃最老的项
monitoredItem.DiscardOldest = true;
//设置通知时间处理函数
monitoredItem.Notification += new MonitoredItemNotificationEventHandler(Notification_MonitoredItem);
EventFilter filter = new EventFilter();
//添加过滤子语,以筛选需要的事件内部信息,包含:
//"事件节点ID","事件类型","源节点", "事件源", "事件发生时间", "事件接收时间",
//"信息", "严重程度", "保持状态", "激活状态"
filter.AddSelectClause(ObjectTypeIds.ConditionType, string.Empty, Attributes.NodeId); //事件节点ID
filter.AddSelectClause(ObjectTypes.BaseEventType, BrowseNames.EventType); //事件类型
filter.AddSelectClause(ObjectTypes.BaseEventType, BrowseNames.SourceNode); //源节点
filter.AddSelectClause(ObjectTypes.BaseEventType, BrowseNames.SourceName); //事件源
filter.AddSelectClause(ObjectTypes.BaseEventType, BrowseNames.Time); //事件发生时间
filter.AddSelectClause(ObjectTypes.BaseEventType, BrowseNames.ReceiveTime); //事件接收时间
filter.AddSelectClause(ObjectTypes.BaseEventType, BrowseNames.Message); //事件信息
filter.AddSelectClause(ObjectTypes.BaseEventType, BrowseNames.Severity); //严重程度
filter.AddSelectClause(ObjectTypeIds.ConditionType, BrowseNames.Retain); //保持状态
filter.AddSelectClause(ObjectTypeIds.AlarmConditionType, "0:ActiveState/Id", Attributes.Value); //激活状态
monitoredItem.Filter = filter; //为监控项添加事件内容过滤器
//以上,监控项内容设置完毕
//将监控项加入指定订阅
subscription.AddItem(monitoredItem);
//应用变动
subscription.ApplyChanges();
return monitoredItem;
}
上述代码中原本的Attributes.Value被Attributes.EventNotifier代替,即监控的属性不再是变量值,而是所谓“事件通知器”。
在Sinumerik系统中,如果想要监控到数控系统报警事件,需要对Object节点下的Sinumerik节点进行事件监控。

在我们的实例中可以实现简单的报警信息监控:

总结
本文介绍了如何实现服务器节点监控(monitor)功能,包括变量值监控、事件监控。
下一步将介绍OPCUA的文件操作功能,即利用OPCUA对西门子Sinumerik数控系统中的加工文件进行传输、读写等操作。
*附言
由于作者水平有限,可能在文章中出现错误或不当描述,如有发现此类情况希望您能及时提供反馈,非常感谢!
如果感觉本文对您有所帮助,希望为文章点个推荐,谢谢。
作者联系方式,163邮箱:zuoquangong@163.com;微信:gongzq1414213
本系列文章:
OPCUA 探讨(一)——测试与开发环境搭建
OPCUA 探讨(二)——服务器节点初探
OPCUA 探讨(三)——客户端代码解读:基本配置与会话连接
OPCUA 探讨(四)——客户端代码解读:浏览服务器节点树
OPCUA 探讨(五)——客户端代码解读:监控变量值与报警事件

浙公网安备 33010602011771号