3. 信息模型基础
OPC UA 的信息模型不是“把变量堆进地址空间”,而是用一套统一的建模原语,把现实设备、系统与业务语义表达成可浏览、可查询、可扩展的图结构。理解这套原语,是设计地址空间、定义类型、规划实例化路径的前提。本节围绕节点、引用、属性、类型定义、实例化、继承与组合七个核心概念展开,并用一张关系图把它们串起来。
一、节点:地址空间的最小建模单元
节点是 OPC UA 地址空间中的基本对象,任何可被浏览、读取、订阅或调用的东西,最终都表现为一个节点。节点由 NodeId 唯一标识,并携带一组标准属性。最常见的节点类别包括:对象(Object)、变量(Variable)、方法(Method)、对象类型(ObjectType)、变量类型(VariableType)、引用类型(ReferenceType)、数据类型(DataType)和视图(View)。
节点本身不直接“存值”,值由变量节点承载;对象节点用于组织语义;方法节点表示可调用行为;类型节点用于定义模板。设计地址空间时,先判断“这个建模元素应该是什么类别”,再决定它的 NodeId 命名空间与浏览名,这是后续所有工作的基础。
二、属性:节点自带的元数据
每个节点都有一组标准属性,用于描述自身。最常用的包括 NodeId、BrowseName、DisplayName、Description、NodeClass、DataType、ValueRank、AccessLevel、Historizing 等。属性不是“随便挂的字段”,而是由节点类别决定的固定集合。例如变量节点才有 Value、DataType、AccessLevel;对象节点没有 Value 属性。
属性与“属性引用”容易混淆:属性是节点自身的元数据,而 HasProperty 引用用于把某个变量节点声明为另一个节点的属性。前者是节点内部结构,后者是节点之间的边。
三、引用:把节点连成有向图
引用是节点之间的有向连接,决定地址空间的拓扑结构。引用有类型、源节点和目标节点,并且是双向可浏览的:正向引用与反向引用成对出现。常见引用类型如下表所示。
| 引用类型 | 语义 | 典型用法 |
|---|---|---|
| HasComponent | 组合关系,整体包含部分 | 设备对象包含变量、方法 |
| HasProperty | 节点拥有属性 | 对象拥有工程单位、量程 |
| HasSubtype | 类型继承关系 | MotorType 继承 BaseObjectType |
| HasTypeDefinition | 实例指向其类型 | Motor_01 → MotorType |
| Organizes | 组织关系,弱包含 | 文件夹组织设备列表 |
四、类型定义、实例化、继承与组合
类型定义(TypeDefinition)规定了一类节点“应该长什么样”:包含哪些子节点、哪些属性、哪些方法,以及默认的数据类型和访问权限。实例化就是按照类型定义,在地址空间中生成具体节点,并通过 HasTypeDefinition 引用回指类型。实例化不是复制粘贴,而是“类型约束下的具体化”:实例可以覆盖部分默认值,但必须满足类型规定的结构。
继承用于类型之间的复用与扩展。子类型自动拥有父类型的成员,并可增加新成员或细化约束。例如 MotorType 继承 BaseObjectType 后,再定义 Speed、Temperature、Start() 等成员;ServoMotorType 继承 MotorType 后,可增加 Position、Homing() 等成员。继承是“is-a”关系,组合是“has-a”关系。HasComponent 表达组合,HasSubtype 表达继承,两者在地址空间中形成不同的边,不能混用。
五、案例:从类型到实例的建模路径
假设要为一条产线建模三台电机。推荐路径是:先定义 MotorType,声明 Speed、Temperature、Running 三个变量成员和 Start()、Stop() 两个方法成员;再定义 Motor_01、Motor_02、Motor_03 三个对象实例,通过 HasTypeDefinition 指向 MotorType;最后用 Organizes 或 HasComponent 把它们挂到产线对象下。这样,客户端只需识别 MotorType,就能批量解析所有电机实例,而不必逐台硬编码。
MotorType (ObjectType)
├─ HasProperty: Manufacturer
├─ HasComponent: Speed (Variable, Double)
├─ HasComponent: Temperature (Variable, Double)
├─ HasComponent: Running (Variable, Boolean)
├─ HasComponent: Start (Method)
└─ HasComponent: Stop (Method)
Motor_01 (Object) --HasTypeDefinition--> MotorType
├─ Speed = 1450.0
├─ Temperature = 62.5
└─ Running = true
六、常见误区与复盘
- 把属性当子节点:属性是节点自带的元数据,不应通过 HasComponent 再挂一个“属性对象”。需要额外语义时,用 HasProperty 挂变量节点。
- 继承与组合混用:用 HasComponent 表达“是一种”会导致类型系统失效;用 HasSubtype 表达“包含一个”会导致实例结构无法展开。
- 实例化后随意改结构:实例可以改值,但不应随意增删类型规定的成员,否则客户端按类型解析时会失败。
- NodeId 命名空间规划缺失:自定义类型和实例应使用独立命名空间,避免与标准节点冲突,也便于后续版本演进。
- 忽略反向引用:引用是双向的,设计时只考虑正向浏览,可能导致客户端无法从子节点回溯父节点。
复盘时可以按“节点类别 → 属性集合 → 引用类型 → 类型定义 → 实例化路径 → 继承/组合边界”六步检查。只要这六步在建模初期对齐,地址空间就会保持结构清晰、语义一致、可扩展,并为后续的信息模型映射与服务器实现打下稳定基础。