跳到主要内容

信息模型 (Information Model)

OPC UA 服务端的全部"可见数据"都组织在一棵叫 Address Space 的节点树里. 客户端不直接访问内存, 而是访问 Address Space 里的节点.

配套阅读

Address Space 三个核心目录

OPC Foundation 规定服务端 Address Space 必须包含三个根目录:

Root (i=84)
├── Objects (i=85) ← 业务数据 / Server 状态 / 自定义节点都挂这里
├── Types (i=86) ← ObjectType / VariableType / DataType / ReferenceType 定义
└── Views (i=87) ← 视图 (子集) 定义, 较少用

Server 节点 (i=2253) 就挂在 Objects 下, 里面有 ServerStatus / ServerCapabilities / ServerArray / NamespaceArray 等.

NodeId

每个节点有一个全局唯一的 NodeId, 由命名空间索引 + 标识符构成:

NodeId 格式:                       例子
i=N (ns=0, 数字) i=2258 → CurrentTime
ns=N;i=M (命名空间 N, 数字) ns=2;i=1001
ns=N;s=... (字符串) ns=2;s=Temperature
ns=N;g={...} (GUID) ns=3;g={550e8400-...}
ns=N;b=base64 (ByteString) ns=4;b=YWJjZA==
  • ns=0 是 OPC Foundation 标准命名空间, 客户端可硬编码 (i=2258 永远是 CurrentTime)
  • ns=1+ 是服务端自定义命名空间, 客户端通过 Namespaces 集合查 NamespaceUri ↔ Index 映射

NamespaceArray

服务端的 NamespaceArray (i=2255) 列出所有命名空间 URI:

[0] http://opcfoundation.org/UA/
[1] urn:server:hostname (服务端本地)
[2] urn:my-company:plc1
[3] http://my-company.com/UA/

客户端连接后, SDK 自动加载 NamespaceArray, 用 ua.Namespaces.GetIndex("urn:my-company:plc1") 即可拿到该 URI 的 Index. 注意: NamespaceIndex 不稳定 (Server 重启可能变), 但 NamespaceUri 稳定 — 所以生产代码不能硬编码 ns=2, 要先反查.

Attributes (属性)

每个节点都有一组标准 Attribute, AttributeId 由 OPC Foundation 固定:

AttributeId含义适用 NodeClass
1 (NodeId)节点 ID全部
2 (NodeClass)节点类型全部
3 (BrowseName)浏览名全部
4 (DisplayName)显示名全部
5 (Description)描述全部
13 (Value)变量的值Variable
14 (DataType)数据类型Variable, VariableType
17 (AccessLevel)访问级别Variable
21 (Executable)是否可执行Method

Read / Write 默认操作 Attribute 13 (Value), 但也可以读 NodeId / DisplayName / DataType 等任何 Attribute.

一个完整例子

Objects (i=85)
└── Calculator (ns=2;s=Calculator)
NodeClass: Object
BrowseName: 2:Calculator
DisplayName: Calculator

├── Add (ns=2;s=Calculator.Add)
│ NodeClass: Method
│ InputArguments: [a:Int32, b:Int32]
│ OutputArguments: [sum:Int32]

└── LastResult (ns=2;s=Calculator.LastResult)
NodeClass: Variable
DataType: Int32
AccessLevel: CurrentRead | CurrentWrite
Value: 7

客户端可以:

  • Browse("ns=2;s=Calculator") → 列出 Add / LastResult 两个子节点
  • Read("ns=2;s=Calculator.LastResult") → 拿到 7
  • Call("ns=2;s=Calculator", "ns=2;s=Calculator.Add", 3, 4) → 返回 7

为什么要这样设计

把"数据"包装成"带元数据 (DataType / Description / 单位 / 范围) 的节点", 客户端无需先知道数据格式就能浏览发现. 这是 OPC UA 与传统 Modbus / S7 等"地址=寄存器号"协议的根本区别.


相关链接