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 是整个模型的“身份证”,一旦确定就不应随意更改。

① 新建项目 设置命名空间 URI urn:MyCompany:Boiler ② 创建类型 BoilerType 继承 BaseObjectType ③ 声明实例 Temperature Pressure / Status ④ 创建实例 Boiler_01 绑定 BoilerType ⑤ 导出验证 NodeSet2 XML UaExpert 浏览 命名空间约束(贯穿全流程) • URI 唯一且稳定,如 urn:MyCompany:Boiler:2024 • 节点 ID 由工具自动分配,避免手工冲突 图 4-1 UaModeler 建模五阶段数据流与命名空间约束

二、创建第一个设备类型: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"。这些值在服务器启动时会被加载,但实际运行时通常由服务器代码动态更新。

提示:如果工具支持“Instantiation”功能,建议使用“从类型实例化”而不是手工创建节点。手工创建容易遗漏引用或写错 NodeId,而实例化可以保证结构与类型完全一致。

四、导出 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 或已有自定义类型类型层级断裂
变量 ModellingRuleMandatory / Optional实例化时节点缺失
引用方向父 → 子(HasComponent)浏览树不显示子节点
NodeId 唯一性工具自动分配,不手工覆盖节点冲突或覆盖
注意:NodeSet2 文件一旦发布,命名空间 URI 和 NodeId 应视为稳定接口。后续版本可以新增节点,但不要删除或重命名已有节点,否则会破坏已有客户端的兼容性。