4. 信息模型建模工具实战
在上一节中,我们已经理解了 OPC UA 信息模型的核心概念:地址空间由节点组成,节点之间通过引用建立层级与语义关系,而设备类型则是通过“类型定义 + 实例声明”的方式被复用的。本节将把这些概念落到工具操作上,使用 UaModeler(或同类工具,如 OPC UA Modeler、SiOME、UA Modeler for Eclipse)从零创建一个可用的设备类型,并生成一个实例,最终导出为可被服务器加载的 NodeSet2 XML 文件。
一、工具定位与建模工作流
UaModeler 是 Unified Automation 公司推出的图形化 OPC UA 信息模型设计工具,它的核心价值在于:用图形界面代替手写 XML,自动维护节点 ID、引用关系和命名空间,并支持从模型直接生成 NodeSet2 文件、C#/ANSI C 代码骨架以及文档。对于设备厂商而言,这意味着可以把“设备类型”作为可交付资产来管理,而不是每次都在服务器代码里硬编码节点。
一个典型的建模工作流包含五个阶段:新建项目并设置命名空间、创建类型节点(ObjectType/VariableType)、在类型下声明实例节点(InstanceDeclaration)、创建实例(Object)并绑定类型、导出 NodeSet2 并验证。下面这张图展示了这五个阶段之间的数据流与依赖关系,其中命名空间 URI 是整个模型的“身份证”,一旦确定就不应随意更改。
二、创建第一个设备类型:BoilerType
假设我们要为一个工业锅炉建模。在 UaModeler 中新建项目后,第一步是设置命名空间。命名空间 URI 建议采用反向域名加设备类别的方式,例如 urn:MyCompany:Boiler:2024。这个 URI 不需要是真实可访问的网址,但必须全局唯一,且一旦发布就不应修改,否则所有引用该模型的客户端都会失效。
接下来创建类型节点。在地址空间浏览器中右键 Types → ObjectTypes,选择“新建 ObjectType”,命名为 BoilerType,并设置其父类型为 BaseObjectType。此时工具会自动生成一个类型节点,其 NodeId 形如 ns=1;i=1001,其中 ns=1 表示我们自定义的命名空间索引。
然后在该类型下声明实例节点。我们需要三个核心变量:
- Temperature:类型为
AnalogItemType(或BaseDataVariableType),数据类型Double,工程单位 °C,量程 0~300。 - Pressure:类型为
AnalogItemType,数据类型Double,工程单位 bar,量程 0~16。 - Status:类型为
BaseDataVariableType,数据类型String,用于表示运行状态。
在 UaModeler 中,这些变量通过“Instance Declaration”方式挂在 BoilerType 下,引用类型为 HasComponent。关键点在于:这些变量是“类型上的声明”,不是真实数据;它们定义了实例应该具有的结构。每个变量还需要设置 ModellingRule 为 Mandatory,表示所有实例都必须包含该变量。
三、创建实例并绑定类型
类型定义完成后,在 Objects 节点下新建一个 Object,命名为 Boiler_01,并将其类型设置为 BoilerType。此时工具会自动根据类型声明,在 Boiler_01 下生成 Temperature、Pressure、Status 三个子节点,并自动建立 HasComponent 引用。这就是 OPC UA 类型系统的核心优势:实例结构由类型驱动,不需要手工逐个复制节点。
实例创建后,可以为其变量设置初始值。例如在 UaModeler 的属性面板中,将 Boiler_01.Temperature 的初始值设为 85.0,Pressure 设为 3.2,Status 设为 "Running"。这些值在服务器启动时会被加载,但实际运行时通常由服务器代码动态更新。
四、导出 NodeSet2 与验证
模型设计完成后,选择“导出 NodeSet2 XML”。UaModeler 会生成一个符合 OPC UA 规范的 XML 文件,其中包含命名空间声明、所有节点、引用和数据类型定义。一个简化的导出片段如下:
<UANodeSet xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd">
<NamespaceUris>
<Uri>urn:MyCompany:Boiler:2024</Uri>
</NamespaceUris>
<UAObjectType NodeId="ns=1;i=1001" BrowseName="1:BoilerType">
<DisplayName>BoilerType</DisplayName>
<References>
<Reference ReferenceType="HasComponent" IsForward="false">i=58</Reference>
</References>
</UAObjectType>
<UAVariable NodeId="ns=1;i=1002" BrowseName="1:Temperature"
DataType="Double" ParentNodeId="ns=1;i=1001">
<DisplayName>Temperature</DisplayName>
<References>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=1001</Reference>
</References>
</UAVariable>
</UANodeSet>
导出后,用 UaExpert 或 UaModeler 自带的验证功能检查模型。验证要点包括:命名空间 URI 是否正确、所有 Mandatory 变量是否都存在、引用方向是否一致、NodeId 是否唯一。如果验证通过,就可以把 NodeSet2 文件交给服务器开发人员,在服务器启动时通过 ImportNodeSet 或类似接口加载。
五、常见问题与判断依据
初学者在建模时最容易出现三类问题。第一类是命名空间混乱:把自定义节点放到了 ns=0(OPC UA 标准命名空间),导致与标准节点冲突。判断方法是检查导出文件中自定义节点的 NodeId 是否以 ns=1 或更高索引开头。第二类是引用方向错误:HasComponent 引用从父节点指向子节点,如果方向反了,客户端浏览时看不到子节点。第三类是 ModellingRule 缺失:如果变量没有设置 Mandatory 或 Optional,实例化时可能不会生成对应节点。
一个实用的检查清单如下:
| 检查项 | 正确做法 | 错误后果 |
|---|---|---|
| 命名空间 URI | 唯一、稳定、反向域名 | 客户端无法解析节点 |
| 类型父节点 | BaseObjectType 或已有自定义类型 | 类型层级断裂 |
| 变量 ModellingRule | Mandatory / Optional | 实例化时节点缺失 |
| 引用方向 | 父 → 子(HasComponent) | 浏览树不显示子节点 |
| NodeId 唯一性 | 工具自动分配,不手工覆盖 | 节点冲突或覆盖 |