在以前的一篇文章中介绍过OPC UA协议,目前这个协议在国外工业4.0中、国内的物联网领域也是开始流行起来了。目前,该项目已经交付,所以打算腾出手来写一篇总结的文章。
入门介绍,请看《
OPC UA统一架构(1):简介
》。
1、序言
简单的说,OPC UA是在原来OPC协议上的一个升级,如下图所示。原来OPC 协议需要依赖微软的COM/DCOM技术,这给该协议的应用扩展带来麻烦。 比如,OPC虽然通过配置COM/DOM来提供数据加密和签名功能,配置防火墙,用户权限来让数据访问变得更加安全,但是会增加额外的工作量,尤其是对非IT的工程师来说。对于OPC UA协议,数据加密和签名,防火墙等都是默认的功能。
目前,OPC官方提供的标准的OPC UA协议一共有14部分,由于本次我们是作为服务端开发,主要涉及其中的几项:安全认证、地址空间创建、映射、发布-订阅模型等。
2、安全性
OPC UA在通过防火墙时通过提供一套控制方案来解决安全问题:
传输:定义了许多协议,提供了诸如超快OPC二进制传输或更通用的SOAP-HTTPS等选项
会话加密:信息以128位或256位加密级别安全地传输。
信息签名:信息接收时的签名与发送时必须完全相同。
测序数据包:通过排序消除了已发现的信息重放攻击。
认证:每个UA的客户端和服务器都要通过OpenSSL证书标识,提供控制应用程序和系统彼此连接的功能。
用户控制:应用程序可以要求用户进行身份验证(登录凭据,证书等),并且可以进一步限制或增强用户访问权限和地址空间“视图”的能力。
本次服务端开发也支持匿名访问、用户名密码访问、证书密钥方式进行访问服务端。可以在配置文件中配置服务端的访问形式(OPC.TCP方式或http方式),安全证书存放目录、安全策略等。如下,展示了配置的示例。具体的配置,可以参考SDK的文档。
使用标志的OPC UA客户端工具(比如UAexpert)去访问服务端,首先选择安全方式,安全认证完成后可以查看地址空间,订阅节点。如下所示:
3、地址空间的创建与映射
在OPC UA中,所有的节点是通过引用(ReferenceType)进行连接起来,每个节点可以理解为一个对象,属性包括了Node ID,这个要求全局唯一的。还有BrowseName,这个只用于浏览节点,不用于显示节点。DisplayName、Description是属于本地化的结构,用于描述节点的信息。
创建地址空间是服务端开发最重要的环节,地址空间也就是上图的OPC UA Address Space,一般需要从root节点开始添加,然后创建root下面的节点,通过这种Reference关系连接其节点间关系,最终形成一种网状结构。当然,在客户端可以完整的浏览到整个地址空间,可以查看每一个节点。
上图中的Real Objects是只外部的信息实体,比如每一个具体传感器设备的状态。这样需要和上面的地址空间中的Node进行一一映射。在本次中,我们通过TCP协议将各种状态进行编码传输过来,然后解析每一个码位,最后通过映射关系更新结果到映射的Node中。
4、“发布-订阅”模型
地址空间创建出来后,也与外部实体进行了一一映射。在客户端可以通过Browse查看每个节点的状态,这种方式是一种“请求-响应”方式。
在实际场景下,客户端操作人员不可能实时去查看每个节点状态,这时候就需要一种能够主动提醒的模式。这就是所谓的“发布-订阅”模式。
UA的第十四部分,定义了发布订阅模式,官方定义Pub-Sub为提供了一个统一的模型,用于将OPCUA信息源的数据和事件分发到感兴趣的观察系统中。
订阅用来向客户端报告通知,其行为可以被总结如下:
阅包含一组由客户端分配的监控项。监控项可以生成通知,这些通知(Notification),由订阅发送给客户端。
订阅拥有一个发布间隔,订阅的发布间隔定义了订阅执行的循环率。每次执行,订阅均试图发送一条NotificationMessages,NotificationMessages中包含了还没有报告给客户端的通知。
订阅以回应发布请求的方式向客户端发送NotificationMessages。发布请求通常以接收顺序存储到Session中,当有通知需要发送时,在每次发布循环时,会从队列中取出相应的请求,并发送通知到客户端,如果没有待发送通知,请求则不会从队列中被删除。
在循环的开始,如果已经存在待发送通知但还没有发布请求,服务器将会进入等待状态,一旦接收到发布请求,则立即向客户端发送通知,无需等待另外一次循。
订阅有一个存活计数器,保存了没有发送通知的周期循环次数,当循环次数达到用户在创建订阅时配置的预置,则会发送一条存活消息到客户端,同时从队列中取出一条发布请求,用于表示该订阅仍然处于存活状态。存活NotificationMessage中不包含通知,但包含下一条消息的序列号.。
订阅是否发布通知可以由客户端在创建时指定,也可以后续通过SetPublishModef方法进行设置,设置为fasle时,订阅将停止向客户端推送通知消息。
订阅包含一个寿命计数器,保存了在没有发布请求时经历的循环次数,当达到阈值时,会删除这个订阅以及与订阅相关的监控项。在删除订阅时,会发送一条StateChangeNotification消息,并携带状态码Bad_Timeout。
Session维护了已发送通知的转发队列,只有当客户端确认消息接收后,才会从队列宗移除,服务器应当保存有两倍于发送请求数量的消息,这种能力可以由服务器的profile定义,如果转发队列满,则删除最旧的消息。当订阅被转移到另外的session时,转发队列同时也需要迁移过去。
5、测试验证
当创建好了地址空间,然后和外部的点进行建立映射关系。如下图所示,可以通过标准客户端的View 查看某个节点的状态值。
客户端也可以通过订阅某节点,当节点变化的时候会通知客户端变化。
比如,订阅某节点时,该节点的值为”hello world100”,时间戳为16:16:54.
当该节点值发送变化,在客户端会收到通知,然后更新结果。如下所示,当节点值变为”hello world300”,时间戳也更新了。这样客户端可以不用每次都通过请求进行查看某节点状态变化了。
注:本文只是对整个服务端流程做了个总结,具体在开发过程中选择的SDK有区别,不过区别不大。有些SDK可能帮我们处理了大部分细节。如果选择直接基于官方的stack开发,难度可能大一些。
领取专属 10元无门槛券
私享最新 技术干货