OPC Client第9讲:OPC UA;KEPServerEX创建OPC服务器;C#创建OPC客户端;OpcUaHelper库;OPC客户端(Softing Opc Client和UaExpert)_c# opcua客户端
【WPF上位机】2025年最新C#WPF上位机通信OPC的上位机工业智能通信统一解决方案 | 进阶必学课程(C#/WPF/.NET) B1146_哔哩哔哩_bilibili
一、OPC和OPC UA
1、OPC
1>为什么要有OPC?
由于软件人员不懂硬件,那么如果想要操作硬件设备就很困难(同时每个硬件还 适用不同的协议)。所以发明了OPC通信协议,统一规定OPC客户端向OPC服务器的请求过程,以及OPC服务器相关的通信接口,让软件人员能方便地操作硬件设备。
如下图,所有设备/所有设备要访问的点位,都是以标签的形式存在OPC服务器中的。
2>什么是OPC?
通信统一化处理方案:OPC(Open Platform Communications)、OPC UA(OPC Unified Architecture)
- 之前OPC只能在Windows下运行,现在变成Open Platform开放平台,可以在多个平台上运行了。
2、OPC UA
3、疑问
1>OPC服务器有数据库吗?
OPC服务器本身并不直接包含数据库,但它们可以与数据库进行交互。
- OPC(OLE for Process Control)是一种工业标准,旨在实现不同供应商的设备和软件之间的数据交换。
- OPC服务器的主要功能是收集来自各种硬件设备的数据,并将其提供给客户端应用程序。虽然这些数据可能会暂时存储在内存中或以某种形式的日志记录下来,但这并不等同于传统意义上的数据库。
- 不过,通过适当的配置,OPC服务器可以被设置为将数据写入外部数据库中,以便长期存储和进一步分析。
2>OPC服务器与设备断开连接,之前的数据放在哪里?
3>OPC UA协议是建立在TCP协议的基础上的吗?与TCP协议有什么不同?
1》OPC UA(OPC Unified Architecture)协议确实可以使用TCP作为其底层传输协议之一,但它不仅仅局限于TCP。
- OPC UA是一个更加全面、安全且平台独立的通信协议,旨在允许系统间跨平台的数据交换。
- 它支持多种传输协议和编码格式,包括但不限于TCP、HTTP等,这使得它比单一的TCP协议更灵活。
- 看三、5、:TCP/UDP通信是通过套接字(Socket)接口来实现的
2》
TCP协议主要负责在网络层面上保证数据的可靠传输。
而OPC UA则构建在其上,提供了更高层次的服务,如安全模型、会话管理、订阅机制等,用于管理和优化工业自动化环境中的数据交换。
- 因此,尽管OPC UA可能利用TCP来完成其部分功能,但两者服务于不同的目的,并不能直接比较。
- OPC UA关注的是应用层的数据交换需求,而TCP专注于提供可靠的网络传输服务。
二、KEPServerEX:OPC服务器
20230531-基于OPC的上位机工业智能通信统一解决方案-03_哔哩哔哩_bilibili
上述链接03、04,是如何操作OPC服务器连接设备。
具体对OPC服务器原理的理解,见Matrikon Flex OPC UA SDK用户手册P18。
1、设置OPC UA:OPC服务器 与 OPC客户端之间的通信
2、配置通信协议:OPC服务器 与 设备之间的通信(例如以太网)
使用Siemens TCP/IP Ethemet通信协议,命名为“AAA”
3、配置设备(例如PLC)
设置设备IP地址等,命名为“200Smart”
4、配置标签(OPC客户端 两种数据获取方式)
配置了标签后,OPC客户端才能根据标签去访问到设备中这个地址的数据。因为OPC服务器与设备的通信过程已经设置完成(1、2、),所以OPC服务器已经把数据从设备处拿过来了。OPC服务器把数据给OPC客户端有两种方式:
- OPC客户端请求(Pull模式):OPC客户端主动发起请求到OPC服务器,要求获取特定标签对应的数据。OPC服务器收到请求后,将从自身的缓存中取出最近一次获取的数据返回给客户端。
- 这种方式适用于不需要实时监控数据变化的应用场景。
- OPC服务器推送(Push模式):当设置了监控订阅机制后,如果被监控的节点数据发生了变化,OPC服务器就会主动向已订阅了这些节点的OPC客户端发送更新数据。
- 这种模式通常用于需要实时或近实时监控设备状态变化的应用场景。
- 如何实现?【见三、8、】
- 在这个过程中,OPC客户端首先告诉OPC服务器它想要监控哪些节点的数据,以及数据变化超过怎样的阈值时才发送通知等条件;
- 之后,一旦这些节点的数据发生变化并且满足了预先设置的条件,OPC服务器就会立即向客户端推送最新的数据。
设置标签名为“sfwefve”,选取地址时会有“提示”如下图:
如果上图“提示”中找不到,可以点击“帮助”,出现下图:
成功配置标签,如下图:
三、C#创建OPC客户端
20230531-基于OPC的上位机工业智能通信统一解决方案-05_哔哩哔哩_bilibili
1、创建控制台应用,安装OPC UA开发库
安装OPC UA开发库
2、连接服务器:Session.Create(...)
1>服务器允许匿名登录【见二、1、】
可以多个客户端连接一个服务器,如何区分各个客户端?——使用Session
查看Session.Create()的函数原型,补充其参数。同时由于它是异步的,所以创建个异步函数再调用。
Session.Create()的参数:
- ApplicationConfiguration configuration:这是一个包含了应用程序配置的对象。它包括了如安全配置、证书管理、网络配置等,用于定义OPC UA客户端如何与服务器进行通信。
- ConfiguredEndpoint endpoint:这是已配置的OPC UA服务端点。它包含了连接到OPC UA服务器所需的所有信息,比如URL、使用的协议(如二进制TCP或HTTP)、安全策略等。
- bool updateBeforeConnect:这是一个布尔值,决定是否在连接之前更新终结点的配置。如果设置为true,则在尝试建立连接之前会先更新终结点的信息;如果设置为false,则直接使用现有的配置尝试连接。
- string sessionName:这是你想要给这个会话起的名字。这个名字可以用来标识这个会话,在调试或者日志记录时可能会很有用。
- uint sessionTimeout:会话超时时间,单位是毫秒。这是指如果在这个时间段内没有收到服务器的任何响应,会话就会被认为已经失效。
- IUserIdentity identity:这个参数代表了用于认证用户身份的信息。它可以是一个匿名用户、用户名密码组合、X509证书等形式的身份验证信息。
- IList preferredLocales:这是一个字符串列表,列出了客户端支持或偏好的语言环境(locales)。这允许服务器知道客户端希望接收哪种语言的消息或其他内容。
1》第二个参数:设置OPC UA服务端点
- ConfiguredEndpoint endpoint:这是已配置的OPC UA服务端点。它包含了连接到OPC UA服务器所需的所有信息,比如URL、使用的协议(如二进制TCP或HTTP)、安全策略等。
点击OPC服务器KEPServerEX的图标,复制端点的\"URL\"
如果给上图第一个红框顶部打上断点,运行代码时会发现停顿一下。停顿的那一下其实就是OPC客户端与服务器连接时做的tcp三次握手。
2》第一个参数:应用程序配置的对象
- ApplicationConfiguration configuration:这是一个包含了应用程序配置的对象。它包括了如安全配置、证书管理、网络配置等,用于定义OPC UA客户端如何与服务器进行通信。
3》匿名登录的问题
如果采用匿名登录,那么只要知道OPC服务器的ip地址,所有的客户端就都能连接,这很危险。
2>服务器不允许匿名登录:用户名+密码
1》OPC服务器的相关操作
在“KEPServerEX”的“设置”里,配置用户和用户密码。新增用户名为“user”的用户,密码为“123456”
2》OPC客户端代码
《1》第六个参数
- IUserIdentity identity:这个参数代表了用于认证用户身份的信息。它可以是一个匿名用户、用户名密码组合、X509证书等形式的身份验证信息。
《2》第一个参数:此处是简单的用户名+密码,所以过滤掉第一个参数的签名、证书等
- ApplicationConfiguration configuration:这是一个包含了应用程序配置的对象。它包括了如安全配置、证书管理、网络配置等,用于定义OPC UA客户端如何与服务器进行通信。
\"Validator\" 则通常指执行验证逻辑的一个工具、函数或类。它的主要职责是检查给定的数据是否“有效”(即是否为 valid)
3>服务器不允许匿名登录:证书【略,安全性比2>更高】
设置上上图的第一个参数
法1》服务器任意证书自动接收
法2》服务器颁发证书
3、Browser[可选]:浏览标签/节点【见二、4、】
下图中,ns=2
具体意味着你正在引用的节点位于OPC UA服务器上的第二个命名空间中;同时要指定好路径(可以直接点击“标记名称”查看,如下下图)。
- 路径的空格不能省略
4、同步读:session.Read(...)
-
RequestHeader requestHeader:这是一个请求头对象,包含了与请求相关的元数据。例如,它可能包含请求的时间戳、请求ID等信息,这些对于追踪请求或实现安全措施很有帮助。
-
double maxAge:这个参数指定了客户端愿意接受的最大缓存时间(以毫秒为单位)。如果服务器缓存中的数据年龄小于这个值,服务器可以直接返回缓存的数据而不是重新从设备获取最新数据。
-
设置为0表示总是希望获取最新的数据。
-
-
TimestampsToReturn timestampsToReturn:这个枚举类型定义了读取操作返回的时间戳类型。可以是:
Source
: 返回源时间戳。Server
: 返回服务器时间戳。Both
: 同时返回源和服务器时间戳。Neither
: 不返回时间戳。
-
ReadValueIdCollection nodesToRead:这是一个集合,包含了想要读取的所有节点的信息。每个
ReadValueId
对象通常至少包括以下内容:- 节点ID(NodeId),用于标识要读取的特定节点。
- 可选属性ID(AttributeId),用于指定要读取的节点的哪个属性(如值、数据类型等)。
-
out DataValueCollection results:这是一个输出参数,用于接收读取结果的集合。每个结果对应于输入
nodesToRead
集合中的一个节点,包含了读取到的数据值以及相关状态信息。 -
out DiagnosticInfoCollection diagnosticInfo:这也是一个输出参数,提供有关执行读取操作时产生的诊断信息。这可能包括错误消息、警告或其他有助于调试的信息。
1>读单值
1》第四个参数:一个集合,包含了想要读取的所有节点的信息
-
ReadValueIdCollection nodesToRead:这是一个集合,包含了想要读取的所有节点的信息。每个
ReadValueId
对象通常至少包括以下内容:- 节点ID(NodeId),用于标识要读取的特定节点。
- 可选属性ID(AttributeId),用于指定要读取的节点的哪个属性(如值、数据类型等)。
《1》读取节点“Word1”
下图中,ns=2
具体意味着你正在引用的节点位于OPC UA服务器上的第二个命名空间中
《2》集合中再加上节点“Word2”
点击上图的“Quick Client”,出现下图。这是一个测试的OPC Client,下图数据一直在变。
节点读取的所有数据,都会在第五个参数中。
《3》设置获取的两个节点属性(见2>)
2》第五个参数:接收读取结果的集合
- out DataValueCollection results:这是一个输出参数,用于接收读取结果的集合。每个结果对应于输入
nodesToRead
集合中的一个节点,包含了读取到的数据值以及相关状态信息。
《1》运行代码,results获取到1>中两个节点的数据都为null
- 此时StatusCode状态码为{BadAttributeldlnvalid}:说明属性的NodeId 不可信
- 因为每个节点都有各种不同的属性,现在需要获取的是节点的Value属性
-
OPC UA 中的 AttributeId:每个节点可以拥有多个属性(Attributes),例如:
- Value:节点的实际值。
- DataType:节点的数据类型。
- AccessLevel:指示节点的访问级别(如可读、可写)。
- UserAccessLevel:当前用户对节点的访问级别。
- Timestamp:最后一次修改的时间戳
-
《2》修改1>中的代码
2>读数组【完全类似1>】
运行代码,发现只是把数组当作字符串传递过来了,没有拿到数组里面一个一个的具体数据
1》读出数组中的每个元素【保证数据类型的匹配!】
由于value.wrappedValue.Value是object类型,所以需要按照当前数据类型进行强制转换。
- 在OPC服务器上的模拟客户端处(上上上图),可以看到数组是Word Array类型
- Word Array 中每个元素是 1 Word = 2 字节 = 16 位
- 在 C# 中对应的数据类型是
ushort
(即无符号 16 位整数)- 还有short;int16;uint16
- 在工业自动化领域,比如 OPC UA、Modbus、S7 协议等,\"Word\" 通常指的是 16 位(2 字节)
- 因为许多 PLC 和设备都基于 16 位或 32 位架构设计,并且很多传感器和执行器的数据也是以这种方式编码的
- 在不同的系统中,Word 的大小不同:
- 在 8 位系统中,Word 是 1 字节(8 bit)
- 在 16 位系统中,Word 是 2 字节(16 bit)
- 在 32 位系统中,Word 是 4 字节(32 bit)
- 在 64 位系统中,Word 是 8 字节(64 bit)
5、异步读:session.ReadAsync(...)或者BeginSend(...)
在4、的同步读中(如下图),线程会一直阻塞在session.Read(),等待数据读取完成。
这个库(1、)是基于TCP(传输控制协议)的,TCP/UDP通信是通过套接字(Socket)接口来实现的
- Socket对应的同步发送是Send,BeginSend和SendAsync都是异步发送。
- 所以这个库底层也是通过Socket来实现TCP通信,同/异步的取名都是一样的
对比4、同步读session.Read(...)的参数【下图上面红框】,可知其与异步读session. BeginRead(...)就是最后两个参数不同。
实现异步读如下图,类似Socket!
异步读的最后两个参数都是回调函数
1>第五个参数:AsyncCallback callback
- 当
BeginRead
异步完成时,将调用这个回调方法。 - 回调函数需要接受一个
IAsyncResult
参数。 - 由下图可知,session在OpcUa函数中,ReadCompleted函数在另一个函数中,所以需要通过参数获取session
1》result.AsyncState是什么?
见2>,需要在session. BeginRead(...)的最后一个参数中填写session。这样在ReadCompleted的参数中的result就是传入的session
2》为什么要用 as Session?
因为 AsyncState 是一个 object 类型,而你传进去的是 Session 类型的对象。
所以在使用它之前,你需要把它从 object 类型还原为原来的类型,这就是 类型转换。
3》结果存储在session.EndRead(...)中
如下图,需要填写session.EndRead(...)的三个参数
由上图可知,session.EndRead(...)的后面两个参数 和 4、同步读session.Read(...)的最后两个参数相同
-
out DataValueCollection results:这是一个输出参数,用于接收读取结果的集合。每个结果对应于输入
nodesToRead
集合中的一个节点,包含了读取到的数据值以及相关状态信息。 -
out DiagnosticInfoCollection diagnosticInfo:这也是一个输出参数,提供有关执行读取操作时产生的诊断信息。这可能包括错误消息、警告或其他有助于调试的信息。
2>第六个参数:object asyncState【见1>1》】
- 用途:用户定义的状态对象,可以在回调中通过
result.AsyncState
访问。 - 通常传入 session 或其他上下文对象,以便在回调中使用。
3>读单值/数组:同4、2>
6、同步写:session.Write(...)
当你调用session.Write()方法时,你实际上是在告诉OPC UA服务器:“根据提供的请求头信息,将这些特定的数据写入到指定的节点,并返回每个写操作的结果状态及任何可能的诊断信息。\"
-
RequestHeader requestHeader:
- 这个参数是一个请求头对象,包含了与本次请求相关的各种信息。比如,它可以包含一个请求ID(用于追踪请求),时间戳(表示请求发起的时间),认证令牌等。通过这种方式,可以为每个服务请求提供上下文信息。这在调试和审计过程中非常有用。
-
WriteValueCollection nodesToWrite:
nodesToWrite
是一个节点值集合(WriteValueCollection
),它代表了你想要写入数据的目标节点列表。每个节点都包含了一个标识符(NodeId),用来唯一识别服务器中的某个具体的数据点或变量,以及你希望写入该节点的新值。这个参数允许你同时对多个节点进行写操作,提高了效率。
-
out StatusCodeCollection results:
results
参数是一个输出参数,类型为状态码集合(StatusCodeCollection
)。当Write
操作执行完毕后,此集合将包含针对每个写操作的结果状态码。这些状态码指示了相应的写操作是否成功完成,或者如果失败,则提供了关于失败原因的信息。
-
out DiagnosticInfoCollection diagnosticInfos:
diagnosticInfos
同样是一个输出参数,类型为诊断信息集合(DiagnosticInfoCollection
)。它提供了更详细的错误信息,特别是在操作失败的情况下。这些诊断信息可能包括有关错误的更多细节,如错误发生的内部位置、相关联的状态码等,有助于更精确地定位问题所在。
注意也要保证数据类型的匹配!【完全类似4、同步读】
如下图,可以看到在OPC服务器处成功写入数据
下图的“192”(状态)代表上上图返回的“Good”
7、异步写:session.WriteAsync(...)或者BeginWrite(...)
完全类似5、异步读,Read的地方变为Write(参数不一样)
WriteCompleted函数中,注意EndRead变成了EndWrite,参数也不一样了。
- 核心与同/异步读是一样的,就是异步写的参数也要获取与同步写的参数一样。
- 下图蓝框是同步写需要获取的参数。
- 所以异步写也需要获取下图蓝框的两个参数,如下下图蓝框。
8、订阅
1>什么是订阅?
见二、4、,如下图。“订阅”就是“OPC服务器推送(Push模式)”
2>订阅结点
1》代码中订阅和监控项有什么区别?
-
订阅(Subscription):订阅是你告诉服务器你需要定期接收某些类型的通知的方式。
-
在技术层面上,它是一个包含多个配置参数的对象,比如
PublishingInterval
(发布间隔)、LifetimeCount
(生命周期计数)等。 -
这些参数决定了你希望从服务器接收更新的频率和其他行为特征。
-
-
监控项(MonitoredItem):监控项是具体指定你想要监视哪个节点以及如何监视的实体。
-
每个监控项都关联到一个特定的节点,并且可以设置其自己的采样间隔、队列大小等属性。
-
当节点的值发生变化时,如果满足监控项设定的条件,就会触发通知并通过订阅发送给客户端。
-
3>接收服务器主动发送的更新数据:一个event事件
订阅完结点后,如果服务器有更新数据,数据就会被发送到Mi_Notification函数中,如上图蓝框。
1》一个event事件
上图蓝框的Notification(通知)源代码如下图,是一个event事件。
在OPC服务器处修改订阅结点的数据,把“123”改成“666”,如下两图。
可以看到标签的值变化后,数据就会被发送到Mi_Notification函数中,如下图
2》接收服务器主动发送的更新数据:Mi_Notification函数
《1》参数e
如下图,查看Mi_Notification的参数e是什么东西
如下图,可以看到e默认情况下不是“MonitoreditemNotification”类型的,所以需要强制转换。这样才可以拿到里面的信息。
展开上图的NotificationValue,如下图。
《2》参数monitoredltem
同理,查看Mi_Notification的参数monitoredltem是什么东西?
《3》完成Mi_Notification函数
获取Mi_Notification两个参数的各个属性(《1》《2》)
3》运行代码
此时,如果在OPC服务器处再次修改订阅结点的值,那么OPC客户端处还会收到新的值
9、通过上述代码实现西门子的PLC仿真
20230601-基于OPC的上位机工业智能通信统一解决方案-09_哔哩哔哩_bilibili
四、简化三、:OpcUaHelper库
20230601-基于OPC的上位机工业智能通信统一解决方案-09_哔哩哔哩_bilibili
1、读单值(异步读)
2、读多个值(异步读)
如下图,有两种方法
3、订阅(略)
五、Softing Opc Client :OPC客户端(与三、的代码对比)
下述链接3:30s开始
20230601-基于OPC的上位机工业智能通信统一解决方案-07_哔哩哔哩_bilibili
1、通过Softing Opc Client 实现西门子的PLC仿真
20230601-基于OPC的上位机工业智能通信统一解决方案-08_哔哩哔哩_bilibili
2、思考自己项目OPC客户端没有的东西:
1>登录:OPC客户端用户与数据库操作用户分开
- OPC客户端使用专用账户连接OPC服务器。
- 数据库操作使用单独的数据库账户。
2>OPC客户端
1》下图的数据显示不是动态变化的
2》功能:在OPC客户端处 修改结点数据 有吗?
当你在OPC客户端处修改了数据,如果这些修改是为了更新底层设备或数据源的状态,则需要将这些数据通过OPC接口传回给OPC服务器。
- OPC服务器负责从底层设备或数据源读取数据,并向多个OPC客户端提供这些数据。
- OPC客户端则连接到一个或多个OPC服务器,以读取数据或写入数据到服务器。
六、UaExpert:OPC客户端
参考UaExpert,设计自己的OPC客户端
OPCUA-精通UaExpert-入门视频_哔哩哔哩_bilibili
UaExpert 介绍及演示_哔哩哔哩_bilibili