4. OPC UA 与工业通信:架构、模型与安全机制

OPC UA(OPC Unified Architecture,统一架构)是工业 4.0 时代最核心的通信标准,它解决了传统 OPC Classic(如 OPC DA、OPC HDA)对 Windows 平台和 DCOM 协议的强依赖问题。本章将深入剖析 OPC UA 的层次化架构、信息模型、客户端/服务器交互模式以及纵深安全机制,为后续的厂务数据采集实战打下坚实的理论基础。

4.1 OPC UA 架构分层与核心组件

OPC UA 采用面向服务的架构(SOA),将通信栈、信息模型与应用层解耦。其核心架构分为传输层、通信层、服务接口层和信息模型层。

架构设计要点: OPC UA 不绑定任何特定的传输协议,支持二进制 TCP(UA Binary)和 Web 服务(SOAP/HTTP)两种传输映射,兼顾了实时性与跨平台互操作性。
OPC UA 分层架构图 OPC UA 分层架构 信息模型层 (Information Model) 对象、变量、方法、类型系统、地址空间 服务接口层 (Services) 读取、写入、订阅、浏览、调用方法 通信协议层 (Protocol) UA Binary (TCP) / UA XML (HTTP/SOAP) 传输层 (Transport) TCP/IP、TLS 加密、安全通道 说明: 上层依赖下层提供服务,下层对上透明。信息模型层定义了工厂数据的语义,服务层定义了交互行为,通信层和传输层保证数据安全可靠地到达。

4.2 数据模型与地址空间

OPC UA 的核心创新在于其面向对象的信息模型。地址空间(AddressSpace)是 OPC UA 服务器的“数据字典”,所有可访问的数据和结构都以节点(Node)的形式组织在地址空间中。

4.2.1 节点与引用

  • 节点(Node): 地址空间的基本单元。每个节点有唯一的 NodeId 和属性集合。
  • 引用(Reference): 节点之间的有向连接,用于构建层次结构(如对象包含变量)或非层次关系(如类型继承)。

4.2.2 核心节点类型

节点类型 描述 典型用途
Object 代表物理或逻辑实体 电机、传感器、控制器
Variable 存储数值或状态 温度值、转速、开关状态
Method 可调用的函数 启动设备、复位报警
ObjectType 对象的模板/类定义 定义“水泵”类型应包含哪些变量和方法
DataType 定义数据结构 模拟量类型、结构体类型
实战提示: 在厂务数据采集中,我们通常通过浏览地址空间来发现设备。例如,一个“空调机组”对象下会包含“送风温度”、“回风湿度”等变量节点,以及“启动/停止”方法节点。
OPC UA 地址空间节点关系示例 地址空间节点关系示例(厂务空调机组) Root (Objects) AHU-01 (Object) SupplyTemp (Variable) ReturnHumid (Variable) StartStop (Method) 引用类型:Organizes(组织)、HasComponent(包含)、HasProperty(属性)。通过浏览引用,客户端可以遍历整个地址空间。

4.3 客户端/服务器模式

OPC UA 采用经典的 C/S 架构。服务器负责维护地址空间并提供数据;客户端负责发起连接、浏览节点、读写数据或订阅事件。

4.3.1 会话建立流程

  1. 发现(Discovery): 客户端通过 Discovery Endpoint 获取服务器支持的协议列表。
  2. 创建安全通道(Secure Channel): 基于 TLS 或 UA Binary 协议协商加密算法和密钥。
  3. 创建会话(Session): 客户端提供身份认证信息(如用户名/密码、证书),服务器验证后建立会话。
  4. 数据交互: 客户端通过服务接口(Read/Write/Subscribe)与服务器交互。
  5. 关闭会话: 客户端主动关闭或超时断开。

4.3.2 订阅机制

订阅(Subscription)是 OPC UA 实现高效数据采集的关键。客户端创建订阅后,服务器按设定的采样间隔(Sampling Interval)检测变量变化,并在发布间隔(Publishing Interval)内将变化的数据打包发送给客户端,极大减少了网络开销。

常见误区: 不要将 OPC UA 的订阅与 MQTT 的发布/订阅混淆。OPC UA 订阅是服务器主动推送变化数据给客户端,而 MQTT 是 Broker 模式。在厂务数据采集中,OPC UA 更适合实时性要求高的 PLC 数据采集。
OPC UA 客户端/服务器交互流程 OPC UA 客户端/服务器交互流程 OPC UA 客户端 数据采集器 SCADA 系统 边缘网关 OPC UA 服务器 PLC 数据源 DCS 系统 设备控制器 ① 发现 & 创建安全通道 ② 创建会话 (认证) ③ 读写/订阅数据 ④ 推送变化数据 / 响应

4.4 安全机制

OPC UA 将安全内建于协议栈,而非依赖外部网络环境。其安全模型涵盖三个核心层面:

安全层面 机制 说明
传输安全 TLS 1.2/1.3、UA Secure Conversation 加密通信通道,防止窃听和中间人攻击
身份认证 X.509 证书、用户名/密码、 验证客户端和服务器的身份合法性
授权与审计 角色权限、访问控制列表(ACL) 控制用户对特定节点(如写操作、方法调用)的访问权限

4.4.1 安全策略(Security Policy)

OPC UA 定义了多种安全策略,用于协商加密算法和密钥长度。常见策略包括:

  • None: 无加密,仅用于测试或隔离网络。
  • Basic128Rsa15: 使用 128 位 AES 加密,RSA 密钥长度为 1024 位。
  • Basic256: 使用 256 位 AES 加密,安全性更高。
  • Aes128_Sha256_RsaOaep: 现代推荐策略,结合 AES-128 和 SHA-256 签名。
生产环境建议: 在厂务数据采集项目中,务必启用至少 Basic128Rsa15 以上的安全策略,并配置有效的 X.509 证书。避免在生产网络中使用“None”策略,否则数据将以明文传输,存在严重安全隐患。

4.4.2 证书管理

OPC UA 使用证书进行双向认证。服务器和客户端各自持有证书,并在建立安全通道时交换。常见的证书管理流程包括:

  1. 生成自签名证书或由企业 CA 签发证书。
  2. 将服务器证书导入客户端的信任列表(Trust List)。
  3. 将客户端证书导入服务器的信任列表。
  4. 配置证书吊销列表(CRL)以管理过期或泄露的证书。
OPC UA 安全通道建立流程 OPC UA 安全通道建立流程 客户端 服务器 ① 发送 Hello 消息 + 支持的策略列表 ② 回复选择的安全策略 + 服务器证书 ③ 验证服务器证书,发送客户端证书 + 密钥交换 ④ 验证客户端证书,确认安全通道建立 安全通道建立后,所有后续的会话和数据传输都在加密隧道中进行,确保数据的机密性和完整性。

4.5 实战代码示例:使用 Python 连接 OPC UA 服务器

以下示例使用 opcua-asyncio 库(推荐用于异步编程)连接一个本地 OPC UA 服务器,并读取一个变量的值。

import asyncio
from opcua import Client, ua

async def read_opcua_variable():
    # 1. 创建客户端实例,指定服务器端点URL
    #    安全模式选择 SignAndEncrypt,策略 Basic256
    client = Client(
        url="opc.tcp://localhost:4840/freeopcua/server/",
        security_mode=ua.SecurityMode.SignAndEncrypt,
        security_policy=ua.SecurityPolicy.Basic256
    )

    # 2. 加载客户端证书(用于安全连接)
    client.load_client_certificate("client_cert.der")
    client.load_private_key("client_key.pem")

    try:
        # 3. 连接到服务器
        await client.connect()
        print("连接成功!")

        # 4. 获取根节点并浏览地址空间
        root = client.get_root_node()
        print(f"根节点: {root}")

        # 5. 读取指定变量(NodeId 为 "ns=2;i=1234")
        var_node = client.get_node("ns=2;i=1234")
        data_value = await var_node.read_value()
        print(f"变量值: {data_value}")

        # 6. 创建订阅,每100ms采样一次
        subscription = await client.create_subscription(100, handler)
        handle = await subscription.subscribe_data_change(var_node)

        # 保持连接一段时间以接收数据
        await asyncio.sleep(10)

        # 7. 清理
        await subscription.delete()
        await client.disconnect()
        print("断开连接。")

    except Exception as e:
        print(f"发生错误: {e}")

# 定义数据变化回调处理器
class SubHandler:
    async def datachange_notification(self, node, val, data):
        print(f"数据变化: {node} -> {val}")

handler = SubHandler()

# 运行异步主函数
asyncio.run(read_opcua_variable())
代码说明: 上述代码展示了完整的 OPC UA 客户端生命周期:连接、浏览、读取、订阅和断开。在实际厂务项目中,建议将连接参数(URL、证书路径、安全策略)配置在外部配置文件中,便于维护和切换环境。