2. OPC UA基础概念
学习 OPC UA 客户端与服务端开发,第一步不是急着写连接代码,而是先建立一套“信息模型”的思维框架。OPC UA 与传统 OPC 最大的区别,在于它不再只是搬运数值,而是把设备、参数、状态、方法都抽象成一套可浏览、可寻址、可扩展的地址空间(AddressSpace)。客户端看到的不是一堆零散标签,而是一棵有层级、有语义、有类型约束的节点树。理解这套术语,是后续读写、订阅、方法调用和自定义信息模型的基础。
一、地址空间:服务端对外暴露的“信息地图”
地址空间是 OPC UA 服务端中所有节点及其引用构成的集合,可以把它理解成服务端对外公开的一棵“信息地图”。客户端通过 Browse 服务逐层浏览,通过 Read/Write 服务访问节点值,通过 Call 服务调用方法。地址空间不是简单的文件夹树,而是一张有向图:节点之间通过引用连接,同一个节点可以被多个父节点引用,也可以被多个视图筛选呈现。
地址空间通常由标准命名空间(如 http://opcfoundation.org/UA/)和厂商自定义命名空间组成。标准命名空间定义了 ObjectTypes、VariableTypes、DataTypes、ReferenceTypes 等基础类型;厂商命名空间则承载具体设备模型。命名空间索引(NamespaceIndex)是节点 ID 的重要组成部分,开发中必须区分清楚。
二、节点、属性与引用:地址空间的三块基石
节点(Node)是地址空间的最小寻址单位,每个节点由 NodeId 唯一标识,例如 ns=2;s=Device1.Temperature。NodeId 包含命名空间索引和标识符,标识符可以是数值、字符串、GUID 或字节串。节点本身不直接“存值”,它通过属性承载元数据,通过引用连接其他节点。
属性(Attribute)是节点的固有特征,每个节点都有一组标准属性,如 NodeId、NodeClass、BrowseName、DisplayName、Description、WriteMask、UserWriteMask 等。变量节点还额外拥有 Value、DataType、ValueRank、AccessLevel、Historizing 等属性。属性不是引用,不能被浏览成子节点,只能通过 Read/Write 服务按 AttributeId 访问。
引用(Reference)描述节点之间的关系,是有向的。常见引用类型包括 Organizes、HasComponent、HasProperty、HasTypeDefinition、HasSubtype、HasEventSource 等。引用让地址空间形成层次和语义:Organizes 表示组织关系,HasComponent 表示组成关系,HasTypeDefinition 指向类型定义。客户端浏览时,正是沿着引用从一个节点走到另一个节点。
三、变量、方法、对象与视图:节点类的具体形态
OPC UA 用 NodeClass 区分节点的种类,常见的有 Object、Variable、Method、ObjectType、VariableType、ReferenceType、DataType、View 八类。开发中最常打交道的是前四种:
- 对象(Object):表示设备、模块、系统等实体,本身不存值,通过 HasComponent 或 HasProperty 挂载变量和方法。例如
Device1是一个对象,其下挂Temperature变量和Start方法。 - 变量(Variable):承载具体数据值,拥有 Value、DataType、ValueRank、AccessLevel 等属性。变量可以是标量、数组或矩阵,AccessLevel 决定客户端能否读写。
- 方法(Method):表示可被客户端调用的操作,拥有 InputArguments 和 OutputArguments 属性描述参数。方法调用通过 Call 服务完成,可带会话和用户权限校验。
- 视图(View):是地址空间的子集投影,用于按业务视角筛选节点。视图本身也是节点,客户端可以指定 View 进行 Browse,只看到与该视图相关的节点和引用。
对象与变量之间通过 HasComponent 连接,对象与对象类型之间通过 HasTypeDefinition 连接。类型节点(ObjectType、VariableType)定义模板,实例节点继承其结构。这种“类型—实例”分离的设计,使服务端可以批量建模,客户端也能按类型做通用处理。
四、核心术语速查清单
| 术语 | 英文 | 作用 | 典型示例 |
|---|---|---|---|
| 地址空间 | AddressSpace | 服务端所有节点与引用的集合 | 整棵信息模型树 |
| 节点 | Node | 最小寻址单位,由 NodeId 标识 | ns=2;s=Device1 |
| 属性 | Attribute | 节点的固有元数据 | BrowseName、Value、DataType |
| 引用 | Reference | 节点之间的有向关系 | HasComponent、Organizes |
| 变量 | Variable | 承载可读写数据值 | Temperature = 25.6 |
| 方法 | Method | 可被客户端调用的操作 | Start()、Stop() |
| 对象 | Object | 设备或模块实例 | Device1、Line1 |
| 视图 | View | 地址空间的筛选子集 | OperatorView、MaintenanceView |
五、案例:从客户端视角看一次温度读取
假设服务端建模如下:Device1 是 Object,其下通过 HasComponent 挂载 Temperature Variable,该变量 HasTypeDefinition 指向 AnalogItemType,DataType 为 Double,AccessLevel 为 Read。客户端读取流程可以简化为:
1. 建立 Session,获取 NamespaceTable
2. Browse(Device1) → 返回 Temperature 节点引用
3. Read(ns=2;s=Device1.Temperature, AttributeId=Value)
4. 服务端返回 DataValue:25.6,StatusCode=Good
5. 若需订阅,则创建 MonitoredItem 监听该节点 Value 变化
这个过程中,客户端并不关心底层是 PLC 还是网关,它只认 NodeId、AttributeId 和引用关系。这正是 OPC UA 信息模型的价值:把设备语义标准化,让客户端代码可以通用化。
六、常见误区与复盘
- 误区一:把属性当子节点浏览。属性不是引用目标,Browse 看不到 Value 属性,必须用 Read 按 AttributeId 读取。
- 误区二:忽略 NodeId 的命名空间索引。同一个字符串标识符在不同命名空间下是不同的节点,跨服务端迁移时尤其要注意。
- 误区三:认为对象一定存值。对象只是容器,值在变量上;方法也不存值,它描述可执行操作。
- 误区四:视图等于权限。视图只是浏览筛选,不等于访问控制;真正的权限由 Session、UserIdentity 和 AccessLevel 共同决定。
复盘来看,地址空间、节点、引用、属性、变量、方法、对象、视图这八个术语并不是孤立概念,而是一套层层递进的信息模型:地址空间是容器,节点是单元,属性是描述,引用是关系,变量和方法是行为与数据的载体,对象是实例,视图是视角。把这套关系理顺,后续的读写服务、订阅服务、方法调用和自定义信息模型开发都会顺畅很多。