4. 地址空间基础

OPC UA 的地址空间(AddressSpace)是服务器对外暴露的“信息世界地图”。客户端并不是直接读取 PLC 寄存器,而是通过浏览地址空间发现对象、变量、方法及其语义关系。理解地址空间的结构、节点标识、命名空间以及浏览与查询机制,是设计可维护信息模型的前提。

一、地址空间结构:节点与引用构成的图

地址空间本质上是一张有向图:图中的顶点是节点(Node),边是引用(Reference)。每个节点由 NodeId 唯一标识,并至少属于一个节点类别(NodeClass),常见类别包括 Object、Variable、Method、ObjectType、VariableType、DataType、ReferenceType、View。引用则描述节点之间的语义关系,例如 Organizes、HasComponent、HasProperty、HasTypeDefinition、HasSubtype 等。

地址空间不是简单的树。虽然从 Objects 文件夹出发通常呈现层级结构,但同一节点可以被多个父节点引用,也可以被类型定义、事件源、视图等交叉引用。因此设计时应把它当作“带类型的图”,而不是文件目录。

地址空间:节点 + 引用构成的有向图 Objects FolderType 实例 Machine_01 Object Temperature Variable Start Method BaseObjectType ObjectType MachineType ObjectType BaseDataVariableType VariableType HasComponent ReferenceType HasTypeDefinition ReferenceType HasSubtype HasSubtype HasSubtype Organizes HasComponent HasComponent HasTypeDefinition HasTypeDefinition 实线:层级/组织引用 虚线:类型定义引用

二、节点标识:NodeId 的四种编码形式

NodeId 是节点在地址空间中的“身份证”,由命名空间索引(NamespaceIndex)、标识符类型和标识符值三部分组成。OPC UA 定义了四种标识符类型:数值(Numeric)、字符串(String)、GUID、字节串(ByteString)。其中数值和字符串最常用。

NodeId 语法示例:
ns=2;i=1001          → 命名空间2,数值标识符1001
ns=2;s=Machine_01    → 命名空间2,字符串标识符Machine_01
ns=1;g=...           → GUID 标识符
ns=1;b=...           → ByteString 标识符

标准节点(命名空间0):
i=85                 → Objects 文件夹
i=2253               → Server 对象
i=47                 → HasComponent 引用类型

设计时应遵循一个原则:类型定义和标准引用使用命名空间0,厂商自定义实例使用独立命名空间。数值标识符适合自动生成、紧凑高效;字符串标识符适合人工可读、便于调试,但需注意长度和编码一致性。

三、命名空间:避免语义冲突的隔离层

命名空间(Namespace)是地址空间的逻辑分区,用 NamespaceIndex 在 NodeId 中引用,用 NamespaceUri 在服务器元数据中声明。命名空间0 固定为 OPC UA 规范定义的标准节点;命名空间1 通常保留给服务器本地模型;厂商模型从命名空间2 开始分配。

命名空间索引 典型用途 示例 设计建议
ns=0 OPC UA 标准节点 i=85 Objects、i=47 HasComponent 只读引用,不可修改
ns=1 服务器本地模型 Server 诊断、本地设备 由服务器实现决定
ns=2 厂商/项目模型 MachineType、Temperature 按项目或设备分配
ns=3+ 扩展模型或子模型 行业 Companion Spec 避免索引硬编码
常见误区:客户端代码中硬编码 NamespaceIndex。NamespaceIndex 是服务器本地分配的顺序号,不同服务器可能不同。正确做法是通过 NamespaceUri 查询得到当前索引,再构造 NodeId。

四、浏览与查询:从发现到定位

浏览(Browse)是客户端在地址空间中“沿引用行走”的机制。核心服务是 Browse,输入起始 NodeId、浏览方向(Forward/Inverse)、引用类型过滤器和结果掩码,输出 ReferenceDescription 列表,包含目标 NodeId、BrowseName、DisplayName、NodeClass 和类型定义。

查询(Query)则用于跨节点条件检索,包括 QueryFirst、QueryNext 以及基于视图的过滤。实际工程中,浏览用于层级发现,查询用于按条件筛选,例如“找出所有 NodeClass=Variable 且 BrowseName 以 Temperature 开头的节点”。

浏览与查询的分工:Browse 解决“从已知节点出发能找到什么”;Query 解决“在整个地址空间中满足条件的节点有哪些”。前者是导航,后者是检索。

五、案例场景:从 Objects 定位到设备温度

假设客户端需要读取 Machine_01 的温度值。典型流程如下:

  1. 从 i=85(Objects)开始 Browse,方向 Forward,引用类型 Organizes,找到 ns=2;s=Machine_01。
  2. 对 Machine_01 继续 Browse,引用类型 HasComponent,找到 ns=2;s=Machine_01.Temperature。
  3. 读取该 Variable 的 Value 属性,同时可读取 DataType、AccessLevel、Historizing 等元数据。
  4. 若需要调用启动方法,Browse 找到 ns=2;s=Machine_01.Start,再调用 Call 服务。

如果客户端直接硬编码 ns=2;s=Machine_01.Temperature,一旦服务器调整命名空间顺序或节点标识符,通信就会失败。更稳健的方式是:先读取服务器 NamespaceArray,解析出目标 NamespaceUri 对应的索引,再拼接 NodeId。

六、设计复盘与检查清单

  • 节点标识是否稳定?数值标识符一旦发布不应随意变更;字符串标识符应避免包含易变信息。
  • 命名空间是否隔离?厂商模型不要混入 ns=0;不同项目模型应使用独立命名空间。
  • 引用类型是否语义正确?组成关系用 HasComponent,属性用 HasProperty,组织关系用 Organizes,类型定义用 HasTypeDefinition。
  • 是否支持浏览发现?关键节点应可从 Objects 或设备对象沿标准引用到达,避免“孤岛节点”。
  • 是否避免硬编码?客户端应通过 NamespaceUri 和 BrowsePath 动态解析 NodeId。

地址空间设计的目标不是“把节点堆进去”,而是让客户端能够以标准、可预测、可发现的方式理解设备语义。结构清晰、标识稳定、命名空间隔离、浏览路径完整,这四点决定了信息模型能否被长期维护和跨系统复用。