OPC UA

面向第三方服务器的 OPC UA 客户端,带以证书把守的信任、按节点 id 寻址,以及地址空间的设备发现。

以 Markdown 查看

OPC UA 驱动是一个面向第三方 OPC UA 服务器的客户端,走 opc.tcp,对象是 PLC、网关、历史数据库和别的 SCADA 系统。它是那个对外发布本工作站自己地址空间的内置 OPC UA 服务器(在设置下面讲)在出站方向上的对应物;两者共用工作站的应用身份,但各自独立地信任对端。

设备

字段 含义 默认值
主机 服务器的 DNS 名称、IPv4 或 IPv6 地址。 留空时为 localhost
端口 端点的 TCP 端口。 4840(配置成 0 时退回 4840)
资源路径 有些服务器要求在主机和端口之后再接一段端点路径,拼出来是 opc.tcp://host:port/path。服务器在根上应答时就留空。
会话安全 关闭时,会话经过签名和加密,拿不出更安全方式的现场设备会被拒绝。允许没有安全保护的会话则照样把会话打开,以明文进行,用于不公布任何安全端点的现场设备。 关闭
用户名 / 密码 这次会话所用的用户身份。用户名为空表示匿名会话。密码在静态存储时由 Windows 的每用户 DPAPI 保护,只能在输入过它的那台机器和那个 Windows 账户上读出来。 匿名
轮询间隔(毫秒) 没有自己另作规定的点位所用的默认轮询速率。 1000

安全与证书

连接是从服务器的端点列表开始的:客户端挑服务器公布的那些端点里最安全的那一个,并用本工作站自己的证书为应用做认证,也就是内置服务器所用的那同一个 OPC UA 应用身份。许多服务器要求在它们那一侧先信任这份证书,才接受会话;那一步在现场设备上做。

一台除了没有安全保护的端点再拿不出别的现场设备会被拒绝,设备会说明原因。那样的会话会把读数和凭据以明文送出去,也证明不了现场设备的身份,所以它只在你于那台设备上提出要求的地方才打开:连接卡片上的会话安全允许没有安全保护的会话。这个选择属于那一台设备,以 ?security=none 的形式随它的地址走,读取会话和发现设备时的浏览一样适用。把它打开也绝不会让本来提供安全的现场设备被降级:拿到的仍然是公布出来的那些端点里最安全的一个。

反方向上,信任是默认拒绝、必须明确给出的。现场设备的证书在会话打开之前就被检查:

  • 一份本工作站从未批准过的证书被拒绝,并记下来等你审阅,设备面板显示一条通知,点名它在等哪一次批准:这台设备不是“离线”,它是在等一个决定。
  • 你在设置 → 现场设备访问里审阅证书,那是证书信任的出站那一半:一份被记下来的证书待在等待批准里,直到你批准它(新会话可以打开)或者阻止它(被拒绝,而且不会反复弹提示)。那是现场设备的清单;同一页面上的客户端访问是另一个方向,也就是谁可以入站连到本工作站自己的服务器上。
  • 已批准的那一行上有撤回批准,它会关掉基于那份证书跑着的实时会话,并把它送回审阅列表,于是下一次连接会再问你一遍。
  • 一份证书都不出示的现场设备,证明不了应答的是谁,也就没有什么可批准的,那次会话会被拒绝,并把它的地址写进日志。它只有在同一个允许没有安全保护的会话之下才连得上,那时这个选择就代替了批准。

批准一台设备服务器,绝不等于授权那份证书入站连到本工作站自己的服务器上:两个方向刻意分开存放。

会话的行为

点位是被轮询的:每一次读取都向服务器要这个节点当前的值。服务器自己的判断随每一次采样一起过来:被标为 Bad 或 Uncertain 的值会被拒绝,点位读作质量不可用,所以一个可疑的来源绝不会被洗成一次好的读数。服务器上已经不存在的节点读作没有值,而不是让整台设备失败。

会话由保活报文照看着;连着三次保活没到,客户端就发起一次自动重连并继续下去。初次连接由应用后台的重试循环负责,所以一台够不着的服务器在启动时不花什么代价。

点位寻址

一个点位靠它的节点 id 寻址,以自由文本输入,原样发出去。接受的形式就是 OPC UA 那几种标准记法:

例子 含义
ns=2;s=Temperature 命名空间索引 2 里的字符串标识符
i=2258 命名空间 0 里的数字标识符(这里是服务器的 CurrentTime)
ns=3;i=1042 命名空间索引 3 里的数字标识符
nsu=urn:vendor:app;s=Line1.Speed 展开形式,用 URI 而不是索引来点名命名空间,对重启之后索引变化更稳健

点位读写的是这个节点的 Value 属性。

数值类型

提供的类型
Boolean、Byte、Int16、Int32、Int64、Float、Double、String

只能是标量:数组、结构和其他复杂值会被当作读不了而拒绝,不会被压扁成一个误导人的数字,点位读作质量不可用。这样的点位只是它自己失败;这台设备其他的点位照读不误,也不会为它重新连接什么。空字符串是一个值,不是一次失败。提供范围之外的类型(无符号宽类型、DateTime、Guid、ByteString)在这个驱动上不可用。

因此,扫描一台服务器给出的通道会比服务器实际持有的少:声明为一列值的节点,以及类型在提供范围之外的节点,都被留在外面。不过它们不是被悄悄丢掉的。扫描会把它们数出来,收尾的消息会说有多少个、分别是哪一类,例如“有 5 个通道被留在外面:3 个是 UInt32 类型,2 个持有的是一列值而不是一个值。”,这样一份短列表读起来就是一份短列表,而不是一台空空的控制器。

写入

可写的点位通过 OPC UA 的 Write 服务写这个节点的 Value 属性。服务器的应答就是判决:被驳回的写入表现为一次失败,缓存的值只在被接受时才往前走。和别处一样,写入字段接收的是工程值,原始值发出去之前会先把数值转换链反过来走一遍。

设备发现

发现设备这条命令从旁边那个字段里取一个目标:一个 OPC UA 服务器地址,例如 opc.tcp://plc-host:4840。扫描分两个阶段,进度行会点名正在跑的是哪一个:

  1. 端点:向目标的标准发现服务查询,公布出来的每一个发现 URL 都成为一个候选端点。自定义端口和服务器路径会原样回到候选项的连接字段里。
  2. 通道:从 Objects 文件夹往下浏览每个候选项的地址空间,每一个可读的变量都作为候选点位列出来,带着它的类型和读写方式。服务器的诊断区被跳过。

浏览是以匿名身份登录的(用同样那套“挑最安全端点”的做法,同样拒绝没有安全保护的会话,也过同样那道现场设备证书的关卡)。一台拒绝匿名会话的服务器、一台不公布任何安全端点的服务器,或者一次因任何原因失败的浏览,仍然会留下一个点位数为零、可以添加的端点:把它加进来,在设备上填好凭据和会话安全,再手工配置点位。在你添加某个候选项之前什么都不会被存下来,而取消会保留那些已经完整的候选项。

值得知道的限制

  • 读取是对 Value 属性的轮询;驱动不会在远端服务器上创建订阅。
  • 只支持标量值:一个点位没法去寻一个数组元素或者一个结构成员。
  • 批准这道关卡是按证书算的:轮换了证书的现场设备会回到审阅列表,等一个新的决定。