OPCUA 探讨(五)——客户端代码解读:监控变量值与报警事件

本系列文章:
OPCUA 探讨(一)——测试与开发环境搭建
OPCUA 探讨(二)——服务器节点初探
OPCUA 探讨(三)——客户端代码解读:基本配置与会话连接
OPCUA 探讨(四)——客户端代码解读:浏览服务器节点树
OPCUA 探讨(五)——客户端代码解读:监控变量值与报警事件

前文中我们探讨了如何浏览OPCUA服务器上的节点以及如何获取节点详细信息。
OPCUA 探讨(四)——客户端代码解读:浏览服务器节点树
该项目源码地址:https://gitee.com/zuoquangong/opcuaapi

本文我们将讨论如何实现服务器节点监控(monitor)功能。
这个功能可以实现变量值的定时自动刷新,不需要写循环和定时器。但是相比于读取功能,这个功能使用频率不高,因为其机制相对复杂,第三方开发的数据采集平台通常不会调用这个功能。

监控值

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

除了监控变量值,该功能还能用于监控数控系统的报警信息:

报警监控

一、关于“监控”功能的三层结构

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

capture_251121_135531

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

二、订阅(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.ValueAttributes.EventNotifier代替,即监控的属性不再是变量值,而是所谓“事件通知器”。
在Sinumerik系统中,如果想要监控到数控系统报警事件,需要对Object节点下的Sinumerik节点进行事件监控。

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

capture_251121_180956

总结

本文介绍了如何实现服务器节点监控(monitor)功能,包括变量值监控、事件监控。
下一步将介绍OPCUA的文件操作功能,即利用OPCUA对西门子Sinumerik数控系统中的加工文件进行传输、读写等操作。

*附言

由于作者水平有限,可能在文章中出现错误或不当描述,如有发现此类情况希望您能及时提供反馈,非常感谢!
如果感觉本文对您有所帮助,希望为文章点个推荐,谢谢。
作者联系方式,163邮箱:zuoquangong@163.com;微信:gongzq1414213
本系列文章:
OPCUA 探讨(一)——测试与开发环境搭建
OPCUA 探讨(二)——服务器节点初探
OPCUA 探讨(三)——客户端代码解读:基本配置与会话连接
OPCUA 探讨(四)——客户端代码解读:浏览服务器节点树
OPCUA 探讨(五)——客户端代码解读:监控变量值与报警事件

posted @ 2025-11-21 18:21  一条工作犬  阅读(2)  评论(0)    收藏  举报