OPC UA
OPC UA](https://opcfoundation.org/about/opc-technologies/opc-ua/)(OPC 统一架构)是一种面向服务、与平台无关的工业通信协议,于 2008 年发布,用作基于 COM 的 OPC Classic 规范的继任者。大量工业自动化设备、PLC 和 SCADA 系统都支持该协议——参见 与 OPC-UA 服务器的兼容性],了解本驱动已测试过的服务器实现。
支持的操作
| 名称 | 值 | 描述 |
|---|---|---|
read | 仅限 Java。支持以单条和批量请求方式读取 PLC4X 支持的全部数据类型。 | |
write | 仅限 Java。支持以单条和批量请求方式写入 PLC4X 支持的全部数据类型。 | |
subscribe | 仅限 Java。支持基于事件和循环两种方式的订阅——参见 OPC UA 数据变更通知] 和 OPC UA 事件通知]。 | |
browse | 仅限 Java。从标准的 Objects 文件夹开始,浏览所连接服务器公开的节点。 |
Go 驱动仅支持非加密通道(security-policy=None),不支持浏览节点,也不支持消息分块——请求或响应超过一个分块时将会失败。
连接字符串
OPC UA 驱动使用连接字符串
opcua:{transport}://{ip-address}:{port}?{options}此连接字符串的示例如下:
opcua:tcp://127.0.0.1:12686?discovery=true&username=admin&password=password注意,transport、port 和 options 字段为可选项。
连接字符串选项
Name
Type
Default Value
Required
Description
Name
Opcua
Code
opcua
Maven 依赖
<dependency>
<groupId>org.apache.plc4x</groupId>
<artifactId>plc4j-driver-opcua</artifactId>
<version>1.0.0</version>
</dependency>默认传输
tcp
支持的传输方式
tcp
配置选项:
protocol-code
STRING
transport-code
STRING
transport-config
STRING
discovery
BOOLEAN
true
控制 OPC UA 服务器发现端点的功能,每个服务器都会通过 '<address>/discovery' 端点发布该功能。这里最常见的问题是,大多数服务器配置不正确,发布的是错误的外部 IP 或 URL 地址。如果是这种情况,你可以将其配置为 false 值来禁用发现功能。
发现阶段始终使用 NONE 安全策略进行。
username
STRING
用于向 OPC UA 服务器进行身份验证的用户名。
password
STRING
用于向 OPCUA 服务器进行身份验证的密码。
security-policy
STRING
Basic256Sha256
应用于驱动与 OPC UA 服务器之间通信通道的安全策略。
可选项为 NONE、Basic128Rsa15、Basic256、Basic256Sha256、Aes128_Sha256_RsaOaep、Aes256_Sha256_RsaPss。NONE 表示通道既不签名也不加密,因此路径上的任何节点都可以读取和篡改交换的数据;同时服务器也未经过身份验证。既签名又加密的策略需要服务器证书的信任锚点——参见 tls.trust-store 和 server-certificate-file。
message-security
STRING
SIGN_ENCRYPT
握手阶段之后交换的消息所应用的安全策略。
可能的选项为 NONE、SIGN、SIGN_ENCRYPT。
此选项仅在 securityPolicy 开启加密(任何高于 NONE 的值)时有效。
tls.keystore
STRING
用于查找客户端证书及其私钥的密钥库(Keystore)文件。
tls.keystore-type
STRING
pkcs12
用于访问密钥库和私钥的密钥库类型,默认为 PKCS(适用于 Java 11+)。
可能的取值包括 jks、pkcs11、dks、jceks 等。
tls.keystore-password
STRING
用于访问密钥库和私钥的 Java 密钥库密码。
generated-key-size
INT
2048
当未配置 tls.keystore 时,驱动程序为证书生成的 RSA 密钥的位长度。若提供了密钥库,此设置将被忽略,因为密钥来自该密钥库。部分服务器要求最小长度,4096 是常见的要求。
server-certificate-file
STRING
服务器证书所在的文件系统路径,支持的格式为 DER 和 PEM。
tls.trust-store
STRING
用于验证服务器证书及其证书链的信任库文件。
tls.trust-store-type
STRING
pkcs12
用于访问密钥库和私钥的密钥库类型,默认为 PKCS(适用于 Java 11+)。
可能的取值包括 jks、pkcs11、dks、jceks 等。
tls.trust-store-password
STRING
用于打开信任库的密码。
allow-insecure-credentials
BOOLEAN
false
允许通过既不签名也不加密的通道发送用户名和密码。
如果不启用该选项,通过未受保护的通道配置了凭据的连接会直接失败,而不是将密码放到链路上,让路径上的任何环节都能读取。启用该选项时会产生警告。
browse-max-references-per-node
INT
65536
驱动程序在浏览单个节点时收集的最大引用数。
一次浏览会分批应答,每一批都会返回一个续传点以供获取下一批,驱动程序会持续跟进,直到服务器停止返回。否则,如果服务器永不停止,收集到的列表就会无限增长。该数值同样会作为服务器端的单节点最大值提交给服务器,使其能够在驱动程序被迫中止之前先行停止。设置为 0 表示不限制。
browse-max-total-nodes
INT
1000000
单次浏览(browse)可展开的最大节点数。浏览操作会遍历服务器所描述的任意树结构,而驱动在遍历之前无法得知该树有多大,因此该配置用于对规模不合理的树进行限制——如果服务器不断命名此前从未出现过的节点,树甚至可能是无穷的。设置为 0 表示不限制。
browse-max-depth
INT
64
浏览将递归进入节点树的深度。已经访问过的节点不会被重复展开,因此引用循环会自行终止;但如果服务器在每一层都使用新的节点名,就会描述出一棵没有底部的树。设为 0 表示不限制深度。
tls.verify
BOOLEAN
true
验证 OPC UA 服务器的证书。将其设置为 false,即可信任服务器出示的任何证书。
关闭此选项是不安全的:它会使连接暴露在中间人攻击之下,并破坏签名安全通道的完整性与真实性保证。仅在本地测试时才可这样做。在生产环境中,请改用 tls.trust-store(证书链校验)或 server-certificate-file(证书固定)来建立信任。
channel-lifetime-ms
LONG
3600000
协商的安全通道、其密钥以及会话保持打开的时间。取值单位为毫秒,默认为 60 分钟。
min-channel-lifetime-ms
LONG
5000
该客户端可接受的最短安全通道生存期,单位为毫秒。服务器可以将所请求的 channel-lifetime-ms 向下调低,而续订计划是依据其返回值制定的——因此生存期过短意味着续订非常频繁,且会运行在本 JVM 中所有 OPC UA 连接共享的执行器上。低于该值的服务器所提供生存期会被提升至该值,并记录一条警告。如果服务器确实需要更快的续订频率,可调低此值以接受它;默认值远低于任何符合规范的服务器所协商的生存期。
session-timeout-ms
LONG
120000
已打开安全会话的过期时间,单位为毫秒。默认值为 2 分钟。
handshake-timeout-ms
LONG
60000
在应用层操作被接受之前,所有协商步骤的超时时间——该超时适用于打开安全通道、创建会话和关闭调用。默认为 60 秒。
request-timeout-ms
LONG
30000
读/写/订阅调用的超时时间。单位为毫秒。
endpoint-host
STRING
用于建立安全通道连接的端点主机。当客户端连接到的服务器所公布的主机名与建立网络连接时所使用的主机名不一致时,使用该配置。
endpoint-port
INT
用于建立安全通道的端口。当客户端连接到服务器,而服务器公布的端口号与实际用于网络连接的端口号不同时使用。
subscription-queue-size
LONG
1
订阅中每个监控项的服务端队列深度。设置为 1(默认值)时,两次发布之间仅保留最新值;若设置为更大的值,则会保留中间的变化,适用于采样速率可能超过发布(周期)间隔的快速状态变化标签。
传输配置选项:
tcp
tcp.connect-timeout-ms
INT
5000
连接超时时间(毫秒)。
tcp.read-timeout-ms
INT
0
Socket 读取超时时间(毫秒)。0 表示不超时。
tcp.write-timeout-ms
INT
0
套接字写入超时时间(毫秒)。0 表示不超时。
tcp.no-delay
BOOLEAN
true
启用 TCP_NODELAY(禁用 Nagle 算法)。
tcp.keep-alive
BOOLEAN
false
启用 SO_KEEPALIVE。
tcp.send-buffer-size
INT
81920
发送缓冲区大小(字节)。0 表示使用系统默认值。
tcp.receive-buffer-size
INT
81920
以字节为单位的接收缓冲区大小。0 表示使用系统默认值。
tcp.local-address
STRING
要绑定的本地地址(可选)。若未设置,则使用默认值。
tcp.local-port
INT
0
用于绑定的本地端口(可选)。0 表示使用临时端口。
标签地址
地址的实现基于 Java。Go 驱动仅通过未加密的通道工作,且不支持浏览。各实现所支持的功能请参见协议支持矩阵。
通用格式
要读取、写入和订阅数据,OPC UA 驱动使用所连接 OPC UA 服务器的变量声明字符串。该字符串包含层级树的命名空间(ns),随后是标识符的类型——字符串(s)、数值(i)、二进制(b)或 guid(g)——以及其地址。
ns={namespace-index};[s|i|g|b]={Identifier};a=attributeId;{Data Type}a 参数表示对象的 attributeId,是可选的,默认为 Value(即 0x0d,也就是 13)。其值既可以给出数字 id,也可以给出 OPC-UA 属性名称(区分大小写)。
数据类型同样是可选的;如果省略,则根据返回值的数据类型选择默认的数据类型。对于无符号数据类型,必须显式给出。
地址的末尾可以附加数组元素的选取方式——单个索引、闭区间范围,以及可选的数组声明下界,位于类型之前。参见 数组寻址]。
注意,标识符 s、i、b 和 g 指定的是地址的格式,而非返回值的数据类型。
数据类型
支持以下数据类型:
- BOOL(布尔)
- SINT(int 8)
- USINT(uint 8)
- BYTE(uint 8)
- INT(int 16)
- UINT(uint 16)
- DINT(int 32)
- UDINT(uint 32)
- LINT(int 64)
- ULINT(uint 64)
- REAL(float)
- LREAL(double)
- CHAR(char)
- WCHAR(2 字节字符)
- STRING(utf-8)
- TIME(时长,毫秒)
- LTIME(时长,纳秒)
- DATE(日期)
- LDATE(日期)
- TIME_OF_DAY(一天中的时刻)
- LTIME_OF_DAY(一天中的时刻)
- DATE_AND_TIME(日期与时间)
- LDATE_AND_TIME(日期与时间)
示例
字符串地址:
ns=2;s=HelloWorld/ScalarTypes/Boolean
ns=2;s=HelloWorld/ScalarTypes/Boolean;BOOL
ns=2;s=HelloWorld/ScalarTypes/Boolean;a=Value;BOOL数字地址:
ns=1;i=1337
ns=1;i=1337;DINT
ns=1;i=1337;a=Value;DINTGUID 地址:
ns=2;g=09087e75-8e5e-499b-954f-f2a8624db28a
ns=2;g=09087e75-8e5e-499b-954f-f2a8624db28a;REAL
ns=2;g=09087e75-8e5e-499b-954f-f2a8624db28a;a=Value;REAL事件订阅:
ns=0;i=2253;a=EventNotifier;BOOL|EventId=x,EventType=x,Severity=x,Time=x,Message=xa 属性仅在事件订阅中需要,事件订阅还会额外用到标签配置。标签配置以逗号分隔的 key=value 列表形式,追加在 | 之后,用于指明要请求的事件字段。这些值只是占位符,会被忽略,但必须非空且为字母数字,并且必须在 | 之前有一个 ;{Data Type} 段——否则该配置会被静默丢弃。支持的事件字段参见 OPC UA Event Notification。
;a=<attributeId> 后缀是 Java 独有的——Go 驱动的地址模式中没有 ;a= 捕获组(也完全没有末尾的 ` | key=val,…标签配置捕获组),因此像ns=2;s=Foo;a=13` 这样的地址会被 Java 接受,却被 Go 拒绝。本页记录的是 Java 语法;如需了解 Go 接受的格式,请查阅 Go 驱动自身的源代码。 |
|---|
说明与提示
地址中的命名空间(例如 ns=2)取决于你所连接的服务器。出现在命名空间中的对象可能且必然会有所不同,因为命名空间用于划分对象的作用范围。
OPC-UA 服务器支持订阅,从而可以减少客户端与服务器之间交换的数据量。主要有三种订阅模式:数据变更通知(Data Change Notification)、状态变更通知(Status Change Notification)和事件通知(Event Notification)。Apache PLC4X 目前支持数据变更通知和事件通知。在底层,订阅仍然需要客户端通过 PublishRequest 来请求数据。
协议细节
安全通信
Apache PLC4X 项目中的安全通道实现已针对现有的开源服务器实现进行了测试,包括 Eclipse Milo(所有模式)以及 OPC Foundation 的 .NET 服务器(Basic128Rsa15 除外)。人工测试表明,该驱动能够与部署在 PLC 上的 OPC UA 服务器以及商业模拟器进行通信。
根据远端的配置,可能需要准备客户端证书。证书的准备工作超出了驱动的职责范围;不过,若未提供客户端证书,系统会自动生成一个用于建立会话。请按照我们的教程 准备客户端私钥和证书 进行操作。按照该教程,你将能够创建一张自签名证书,用于硬件和软件 OPC-UA 服务器。请注意,规范中证书有多种出现形式:有用于保护通信通道的应用实例证书,它通过下文所述的密钥库选项提供;还有用于标识用户的 X509 证书,需单独提供——参见 用户身份验证。
各种安全模式之间的区别在于所用签名和加密算法的强度。该驱动一次只能使用一种安全模式进行通信。除了安全策略之外,还可以指定 message-security 选项,该选项表示初始握手之后预期的安全设置。默认情况下,此选项设置为 SIGN_ENCRYPT,它会施加高安全设置并对交换的消息负载进行完全加密。当需要额外的诊断信息时,必须通过 TRACE 级别的日志条目来追踪负载。SIGN 模式使得可以在 Wireshark 等工具中浏览数据包。
证书验证
OPC UA 规范定义了其自身的证书验证流程。
驱动默认会验证服务器证书,并采取失败即关闭(fail closed)的策略:如果未配置信任锚点,服务器证书将被拒绝,连接也会失败。请配置以下选项之一:
tls.trust-store—— 根据给定的信任存储验证证书链。该接受机制依赖于常规的 TLS 检查(过期日期、证书路径等);它不会验证 OPC UA 特有的部分,例如应用程序 URI。server-certificate-file—— 将信任固定到该证书本身。只有从配置的文件中读取的证书才被信任;通过未经身份验证的发现通道获取的证书永远不会被用作信任锚点。
tls.verify=false 会完全禁用服务器证书验证。这会使连接容易受到中间人攻击,仅适用于本地测试。 |
|---|
客户端证书
如果未配置 tls.keystore,驱动会为会话生成一个自签名的应用实例证书。它是一把使用 SHA-256 签名的 2048 位 RSA 密钥;当服务器要求更大的密钥时,可使用 generated-key-size 来申请更大的密钥:
opcua:tcp://127.0.0.1:12686?security-policy=Basic256Sha256&message-security=SIGN_ENCRYPT&generated-key-size=4096每次连接生成的证书都是全新的,因此保存信任列表的服务器在将其加入信任列表之前会拒绝它。对于首次连接尝试之外的任何场景,请通过 tls.keystore 提供你自己的证书——参见客户端证书教程]。
用户身份认证
除了保护通道安全之外,OPC UA 还会对会话的使用者进行身份认证。该驱动支持规范定义的全部三种令牌类型:匿名、用户名/密码以及 X509 用户证书。
当未提供任何凭据时,即为匿名访问。用户名和密码可以从连接字符串中获取:
opcua:tcp://127.0.0.1:12686?username=admin&password=password或者,与证书变体一起,通过传递给驱动管理器的 PlcAuthentication 提供。传递给 getConnection 的对象决定了驱动向服务器请求哪种令牌类型,而以这种方式提供的凭据优先于连接字符串中的凭据:
PlcConnection connection = driverManager.getConnection(
"opcua:tcp://127.0.0.1:12686?security-policy=Basic256Sha256",
new PlcUsernamePasswordAuthentication("admin", "password"));用户证书
要使用 X509 证书进行用户身份验证,请传递一个 PlcCertificateAuthentication,其中包含存放用户证书及其私钥的密钥库:
KeyStore keyStore = KeyStore.getInstance("PKCS12");
try (InputStream in = new FileInputStream("user.p12")) {
keyStore.load(in, "changeit".toCharArray());
}
PlcConnection connection = driverManager.getConnection(
"opcua:tcp://127.0.0.1:12686?security-policy=Basic256Sha256",
new PlcCertificateAuthentication(keyStore, "changeit".toCharArray()));该驱动会取用密钥库中第一个包含私钥的条目。如果密钥库中存有多个标识,请指明要使用哪一个:
new PlcCertificateAuthentication(keyStore, "changeit".toCharArray(), "operator");该证书必须使用 RSA 密钥,且服务器必须信任它——服务器通常会为用户证书单独维护一份信任列表,与应用程序实例证书的信任列表相互独立。
这与通过 tls.keystore 配置的证书并非同一张证书。那张是应用程序实例证书:它用于保护信道,并标识是哪个安装实例在通信;而用户证书用于标识是"谁"在通信。只要服务器同时信任这两种用途,你完全可以对两者使用同一张证书。 |
|---|
驱动程序通过使用服务器为其证书用户令牌策略声明的安全策略,对服务器证书和服务器随机数进行签名,从而证明自己持有私钥。如果服务器提供的证书认证所使用的安全策略为 None,则没有可用于签名的算法;此时驱动程序会拒绝该端点,而不是发送无法验证的令牌。
协商流程
根据配置,驱动程序可能会也可能不会尝试从远程服务器发现端点。当 discovery 选项设置为 true 时,驱动程序会通过一次连接尝试来获取服务器证书。该发现选项还会启用对服务器端点安全设置的匹配性检查。
初始发现完成后,一旦驱动程序找到与其安全设置相匹配的端点,就会发起第二次连接尝试,并切换到所配置的安全模式。
驱动程序发起的每次连接尝试都遵循上文连接字符串选项中描述的取值范围;这些选项声明了客户端接受并期望的取值。当服务器返回其自身的取值范围(针对传入的 Hello 调用返回的 Acknowledge)后,驱动程序会从中选取数值——即本地接收缓冲区大小与远程发送缓冲区大小中的较小值,以及本地发送缓冲区大小与远程接收缓冲区大小中的较小值。
PLC 设备上 encoding.send-buffer-size 与 encoding.receive-buffer-size 的常见取值仍为 8196 字节。
| 由于协商与分块逻辑尚未完整实现,在 Apache PLC4X 0.11 版本之前的 OPC UA 驱动程序可能会提交超出服务器限制的调用。 |
|---|
OPC UA 数据变更通知
数据变更通知由一组标签创建,之后客户端需要发送发布请求。每当发出发布请求时,服务器都会返回一个响应,其中包含已为其创建订阅的所有标签的值。尽管 OPC UA 规范允许修改订阅内容,但 PLC4X 的 API 并不支持这一点。
默认情况下,PLC4X 客户端 API 中定义的 Cyclic 和 Change of State 订阅操作会产生数据变更通知。
OPC UA 事件通知
PLC4X API 定义的第三种订阅模式是 Event Subscription。这类订阅自 PLC4X 0.13 起可用,语法略有不同,所返回的数据也更为复杂。OPC-UA 事件可用于接收各类通知,这些通知在特定情况发生时产生。在其他协议中,这类行为可充当告警或警报。
要创建 OPC-UA 事件订阅,你需要:
- 定义
a=EventNotifier或a=12 - 通过标签配置指定要检索的事件字段
标签配置的键必须与 OPC-UA 事件字段匹配。标签配置的值不会被使用,因此无法用于指定字段值的过滤条件。受支持的字段在 OPC UA BaseEventType 中定义:
EventIdEventTypeSourceNodeSourceNameTimeReceiveTimeLocalTimeMessageSeverity
示例语法:ns=0;i=2253;a=EventNotifier;BOOL|EventId=x,EventType=x,Severity=x,Time=x,Message=x。
客户端在接收到通知时生成的订阅事件包含一个对象(PlcSTRUCT/Map),其中保存了所有请求的字段。请注意,Apache PLC4X 仅对数据进行基本的规范化处理,因此事件数据的实际处理需要由你的应用程序来完成。
标签元数据
该驱动支持标签元数据。除了 timestamp 和 timestamp_source 等标准元数据字段之外,它还提供以下键:
opcua_qualityopcua_server_timestampopcua_source_timestamp
这些字段是否出现是有条件的,仅当它们能够从客户端与服务器之间交换的数据包中获取时才会提供。这意味着,根据客户端执行的操作(读取/写入/订阅),元数据会有所不同。
与 OPC-UA 服务器的兼容性
存在多种 OPC-UA 服务器实现,每种实现都有其自身的特点,有时在规范中隐藏着更详细的安全处理方式或其他边缘情况。到目前为止,Apache PLC4X OPC-UA 客户端已确认可与以下服务器协同工作(按通过测试/确认兼容的时间顺序排列):
0.13 版本
- node-opcua
- 三菱电机 MX OPC Server UA
0.12 版本
- Prosys OPC-UA 模拟服务器
- Simatic OPC UA S7-1200 基础版
- OPC Foundation UA-.NET Standard
- Simocode OPC UA 服务器
0.12 之前的版本
更多信息
这种多层次的方法实现了最初的规格设计目标:
- 功能等价:所有 COM OPC Classic 规范均映射到 UA
- 平台无关:从嵌入式微控制器到基于云的基础设施
- 安全:加密、身份验证和审计
- 可扩展:能够在不影响现有应用的前提下添加新功能
- 全面的信息建模:用于定义复杂的信息
有关更多详细信息,请参阅 OPC Foundation 上的 OPC UA 页面。
评论
登录后参与评论
KnowForge