OPC UA的监控项、订阅、和通知
MonitoredItem
每个监控项均指明了要监控的项目(item)和用来发送通知的订阅。
item可以是一个节点的属性(node attribute)。
- MonitorItem可以监控一个属性,一个变量或者一个事件
- 可以通过MonitorItem定义的过滤器(fiter),来决定是否产生一个通知
- Queue attribute定义了可以被缓存的通知数量,可以通过配置策略决定丢弃新消息还是最老的消息
Triggering model
- MonitoredItem服务允许某item只有在关联Item被触发时才会被触发
- 这是通过在触发对象和被触发对象间建立连接来实现的
Notification
描述了数据变化和事件的数据结构,通知会被打包为NotificationMessage并传送给Client。
订阅会以客户定义的发布间隔周期性的发送NotificationMessage。
Subscription
订阅用来向客户端报告通知,其行为可以被总结如下:
- 订阅包含一组由客户端分配的监控项。监控项可以生成通知,这些通知,由订阅发送给客户端
- 订阅拥有一个发布间隔,订阅的发布间隔定义了订阅执行的循环率。每次执行,订阅均试图发送一条NotificationMessages,NotificationMessages中包含了还没有报告给客户端的通知
- 订阅以回应发布请求的方式向客户端发送NotificationMessages。发布请求通常以接收顺序存储到Session中,当有通知需要发送时,在每次发布循环时,会从队列中取出相应的请求,并发送通知到客户端,如果没有待发送通知,请求则不会从队列中被删除
- 在循环的开始,如果已经存在待发送通知但还没有发布请求,服务器将会进入等待状态,一旦接收到发布请求,则立即向客户端发送通知,无需等待另外一次循环
- 订阅有一个存活计数器,保存了没有发送通知的周期循环次数,当循环次数达到用户在创建订阅时配置的预置,则会发送一条存活消息到客户端,同时从队列中取出一条发布请求,用于表示该订阅仍然处于存活状态。存活NotificationMessage中不包含通知,但包含下一条消息的序列号
- 订阅是否发布通知可以由客户端在创建时指定,也可以后续通过SetPublishMode服务设置,设置为Fasle时,订阅将停止向客户端推送通知,但是订阅会继续执行,且持续发送存活消息
- 订阅包含一个寿命计数器,保存了在没有发布请求时经历的循环次数,当达到阈值时,会删除这个订阅以及与订阅相关的监控项。在删除订阅时,会发送一条StateChangeNotification消息,并携带状态码Bad_Timeout
- Session维护了已发送通知的转发队列,只有当客户端确认消息接收后,才会从队列宗移除,服务器应当保存有两倍于发送请求数量的消息,这种能力可以由服务器的profile定义,如果转发队列满,则删除最旧的消息。当订阅被转移到另外的session时,转发队列同时也需要迁移过去
序列号是一个UINT32,并从1开始向前滚动。
当订阅创建后,在第一个周期结束时,会向client发送一条消息,用于通知客户端订阅可用。如果有通知要发送,则发送NotificationMessage,如果没有,则发送存活消息,并设置序列号为1,这是唯一不需要等待到keep alive阈值的场景。
客户端接受消息要比发布周期更加频繁,因一个发布周期可能包含有多条消息,但客户端可以设置服务器不缓存发布通知来减轻压力,但会增加响应的延时。
在订阅的声明周期中,序列号不会被重置,也即在40亿消息中不会出现重复的消息ID。
消息序列号还可以用于消息重发。
订阅被设计为独立于具体的通信协议,短暂的连接丢失不会导致丢失数据和事件。在设计时,要确保该特性得以正确实现,也即能够处理较长时间的通讯中断和有计划断链。如果服务器支持该特性,服务器的缓存区将会设计的比较大。
HasEventSource
HasEventSource引用类型是一个具体的引用类型,并可以直接被使用,是HierarchicalReferences的子类。
这个引用类型的语义是将event source(事件源)以一种层次化的、非循环的方式关联起来。该类型以及该类型的所有子类是打算用来帮助发现服务器能够产生的事件。但对于服务器来说,并不需要强制的建立从事件源(EventSource,产生事件)到事件通知器的关系,例如server对象,其隐含的表示是所有事件源的通知器。
该关系的起点是一个事件订阅的源(一个对象,该对象的EventNotifer属性的SubscribeToEvent位有效)。
该关系的终点可以是任意NodeClass节点,只要该节点能够通过订阅产生事件通知,并传递给关系源端。
另外,从”A“点开始,沿着HasEventSource或其子关系不能再回到A点,但是允许存在多条路径都指向节点“B“。
HasNotifier
HasNotifier引用也是一个可以被直接使用的关系。
HasNotifier引用的语义是将作为通知器的节点与其他通知器对象节点关联起来。该引用的目的是建立事件通知对象(event notifying object)的层次化结构,而且HasNotifer是HasEventSource的子类。
该引用的起点需要是能够作为事件订阅源的对象或视图,终点同样。事件订阅源是指EventNotifier属性中“SubscribeToEvents”生效的对象。
如果HasNotifier引用的终点产生了一个事件,那么该引用的起点同样可以提供这个事件。也即通过HasNotifier引用,可以建立起事件通知的层次。
Alarm
告警是AcknowledgeConditions的特殊化,相比Condition告警增加了活动状态,以及搁置和抑制状态。
- 告警如果处于活动状态(Active)则表明条件所表示的状况正在发生,如果Active=False则表明处于正常状态
- 有些告警会引入Active状态的子状态,入high level state与critical high state
- 搁置状态(Shelved)可以由操作员通过OPC UA方法设置,抑制状态(Suppressed)由服务器出于内部原因设置,告警服务器通常会实现抑制、搁置和停止服务特性,以避免操作员被告警风暴淹没,而能够专注于真正严重的告警
- 搁置、抑制和停止服务状态与Disable状态不同,它们表明告警仍然生效,并继续通过订阅分发至客户端
下图描述了典型的告警时间线:
在值达到Alarm Limit时,产生了告警
- 在Ack Delay期间,告警处于Unacknowledged状态,也即在此时间段内,操作员没有感知到告警,因此未进行操作
- 当操作员感知到告警,并确认后,告警进入了Acknowledged状态,此时操作员需要时间确认这个告警并作出反应,此时处于Operator Response Delay
- 之后采取行动,进入到Process Delay状态,到操作生效为止
- 之后产生效果,值向下,并在经过告警死区之后消除并转入Normal状态
- 如果不进行操作,则值继续上升并产生后果
Condition Instance
Condition通常会有状态和子状态,因此在AddressSpace中保存Condition的实例是有意义的。如果Server选择暴露condition,那么condition通常是作为对象的一部分存在的,也即对象“拥有”这个条件。
如:
一个温度传感器内置了高温告警,则这个告警在地址空间中会以温度传感器实例的部件(hasComponent)关系的形式作为温度传感器的一部分,且这个告警是LimitAlarmType的实例。
有些时候可以选择不在地址空间中暴露condition,这样就没有办法修改condition的状态。
告警和状态的审计是系统中非常重要的安全功能,当用户调用方法更改Condition的状态时,会产生AuditConditionAcknowledgeEventType类型的事件。
HasCondition Reference
该引用的语义是关联ConditionSource和它的Condition,每个ConditionSource都应该是HasEventSource或其子引用的目标节点。
HasCondition关系可以用在VariabeType或者ObjectType之上,也可以用于它们的实例声明,HasCondition的目标节点则可以是ConditionType类型或者实例。
- 如果在类型中(ObjectType/VariableType)中有HasCondition关系,其实例也要继承
- 如果类型中引用的是ConditionType,其类型实例也要指向该ConditionType
- 如果类型上不允许配置HasCondition,则可以在实例上增加
AddressSpace Sample
总结来说:
- HasNotifer维护了一个通知器的层次,目的端产生的事件在源端可读,由此可以在不同的通知器层次有针对性的读取事件(如只读Area1或者只读Tank Farm)
- HasEventSource用于指明事件生成的源,只有HasEventSource及其子类型所指向的目标节点上可以由HasCondition关系
- HasCondition关系表明了能够具体生成的Condition/告警
- 通常的ConditionType对象是作为对象的一部分存在的,这里的MyAlarmTypeA实际上是从ConditionType继承而来
- 某些服务器中,HasCondition的target节点可以是ConditionType
另外,在类型中定义的ConditionType是可以在实例中得到继承的,如下图所示:
几个问题
- Event、Condition和Alarm是什么关系
- EventType是ConditionType的父类
- ConditionType是AlarmType的父类
- Event、Condition和Alarm是其实例
- Event最为泛化,主要是定义了事件的基本属性
- Condition则拥有状态
- Alarm则拥有更多可被设置的状态
- HasEventSource、HasNotifier有什么区别
- HasEventSource指明了事件产生的源
- HasNotifier是HasEventSource的子类
- HasNotifer组织的是一张通知器的网,关系的目标节点所产生的事件在源端也可以看到,可供用户定义自己查看事件的范围
- Condition只能加到HasEventSource的目标节点上
- GeneratesEvent、AlwaysGenerateEvent引用有什么区别
- GeneratesEvent表明ObjectType,VariableType可能产生事件,以及Method在调用时可能产生事件
- AlwaysGenerateEvent的起点只能是Method,且在每次调用时必然产生事件
- GeneratesEvent和AlwaysGenerateEvent的终点只能是表示EventType及其子类
参考资料
- 《OPC统一架构From马克》2.10,4.9,5.7章节
- 《OPC UA Part 3 - Address Space Model Release 1.04 Specification》4.6, 7.15, 7.16, 7.17, 7.18章节
- 《OPC UA Part 4 - Services Release 1.04 Specification》5.12, 5.13章节
- 《OPC UA Part 9 - Alarms and Conditions Release 1.04 Specification》 第4章,第5章,第6章,附录B.2节