工业网关 OPC UA 客户端与服务端信息模型(Information Model)构建实战

封面信息图

在工业 4.0、智能制造与现代工厂自动化体系中,OPC UA(OPC Unified Architecture / IEC 62541) 已经彻底取代了陈旧的 Modbus 和严重绑定 Windows DCOM 的传统 OPC DA,成为了跨厂商、跨操作系统(Linux/RTOS/Windows)实现设备间语义互操作的事实标准协议。

与 Modbus 纯粹基于“无语义的 16 位裸数字寄存器地址(如 40001 代表什么完全靠人工看 Excel 表格盲猜)”截然不同,OPC UA 的核心杀手锏是其强大的 面向对象信息模型(Information Model) 与 地址空间(Address Space):

  • 每一个工业物理实体(如一台数控机床、一个变频电机、一组传感器阵列)在 OPC UA 中都被抽象为一个包含层级对象(Objects)、带工程单位与范围限制的变量(Variables)、可调用的远程控制方法(Methods)以及事件(Events) 的标准面向对象节点树;
  • SCADA 系统或上层 MES/ERP 软件只要连上边缘网关,就能通过自描述机制全自动浏览并读取到带有完整语义的物理模型!

基于开源高性能 C 语言协议栈 open62541,在嵌入式 Linux 工业网关上从零构建一套符合工业标准的 OPC UA 服务端与信息模型节点树,是实现工业现场 OT 与 IT 数字化融合的必备工程硬功。

OPC UA 地址空间与节点模型(Node Model)微观拓扑

在 OPC UA 服务端中,所有的信息都组织在一张由 节点(Nodes) 与 引用关系(References) 构成的有向网状图中:

OPC UA 工业网关地址空间节点树拓扑:

[ Root 根节点 (ns=0;i=84) ]
       │
       ▼ (Organizes 引用)
[ ObjectsFolder 对象总目录 (ns=0;i=85) ]
       │
       ▼ (Organizes 引用)
+=========================================================================+
| 【IndustrialGateway 工业网关对象节点 (ns=1;s="Gateway_Node_01")】        |
|                                                                         |
|   ├── [ Variable: FirmwareVersion ] ──► "v2.4.1" (String / 只读属性)    |
|   ├── [ Variable: CPU_Temperature ] ──► 52.4 ℃ (Float / 包含 EURange: 0~100℃)|
|   │                                                                     |
|   ├── 【PumpMotorSubsystem 抽水电机子系统对象 (Object)】                |
|   │     ├── [ Variable: RPM ] ────────► 1450.0 转/分 (Float / 读写变量) |
|   │     ├── [ Variable: CurrentState ] ► "RUNNING" (String)             |
|   │     └── [ Method: EmergencyStop() ] ► (可供上层直接调用的远程急停RPC)|
|   │                                                                     |
|   └── 【EnvironmentSensorArray 环境传感器对象】                         |
|         ├── [ Variable: AmbientHumidity ] ► 65.2 %RH                    |
|         └── [ Variable: AmbientPressure ] ► 101.3 kPa                   |
+=========================================================================+
四大核心节点属性:
  1. NodeId(全局唯一节点标识符):如 ns=1;s="PumpMotor.RPM"(命名空间索引 + 字符串/整型标识);
  2. BrowseName / DisplayName:人类可读的语义显示名称;
  3. Value(实时变量数据):支持复杂的强类型数据结构(Int32, Float, String, Custom Struct);
  4. AccessLevel(访问权限位图):定义该变量是只读、可读写还是仅供订阅。

基于 open62541 构建工业级 OPC UA 服务端实战

编写一个在嵌入式 Linux 上运行的高性能轻量级 OPC UA 服务端,动态构建机床信息模型并暴露远程急停方法:

#include <stdio.h>
#include <signal.h>
#include <open62541/server.h>
#include <open62541/server_config_default.h>

static volatile UA_Boolean g_running = true;

static void stop_handler(int sig) {
    g_running = false;
}

// 远程调用方法回调函数: EmergencyStop (紧急停车 RPC)
static UA_StatusCode EmergencyStopMethodCallback(
    UA_Server *server, const UA_NodeId *sessionId, void *sessionHandle,
    const UA_NodeId *methodId, void *methodContext,
    const UA_NodeId *objectId, void *objectContext,
    size_t inputSize, const UA_Variant *input,
    size_t outputSize, UA_Variant *output) 
{
    pr_warn("[OPC UA RPC] EmergencyStop() Method Triggered by Remote Client!\n");
    // 立即向硬件总线发射急停脉冲...
    return UA_STATUSCODE_GOOD;
}

// 构建工业设备信息模型节点树
void Build_Industrial_Information_Model(UA_Server *server) {
    UA_NodeId gateway_obj_id;
    UA_NodeId pump_obj_id;
    UA_NodeId rpm_var_id;

    // 1. 创建顶级对象节点: IndustrialGateway
    UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
    oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "IndustrialGateway");
    
    UA_Server_addObjectNode(
        server, 
        UA_NODEID_STRING(1, "IndustrialGateway"),               // 自定义 NodeId
        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),           // 父节点: ObjectsFolder
        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),               // 引用类型: Organizes
        UA_QUALIFIEDNAME(1, "IndustrialGateway"), 
        UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), 
        oAttr, NULL, &gateway_obj_id
    );

    // 2. 在网关下挂载子对象: PumpMotor (抽水电机)
    oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "PumpMotor");
    UA_Server_addObjectNode(
        server,
        UA_NODEID_STRING(1, "PumpMotor"),
        gateway_obj_id,                                         // 父节点: IndustrialGateway
        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),            // 引用类型: HasComponent
        UA_QUALIFIEDNAME(1, "PumpMotor"),
        UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
        oAttr, NULL, &pump_obj_id
    );

    // 3. 在电机下挂载动态变量: RPM 转速 (支持客户端实时读写与数据订阅!)
    UA_VariableAttributes vAttr = UA_VariableAttributes_default;
    UA_Float initial_rpm = 1450.0f;
    UA_Variant_setScalar(&vAttr.value, &initial_rpm, &UA_TYPES[UA_TYPES_FLOAT]);
    vAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Motor_RPM");
    vAttr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE; // 允许读写

    UA_Server_addVariableNode(
        server,
        UA_NODEID_STRING(1, "PumpMotor.RPM"),
        pump_obj_id,                                            // 父节点: PumpMotor
        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
        UA_QUALIFIEDNAME(1, "Motor_RPM"),
        UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
        vAttr, NULL, &rpm_var_id
    );

    // 4. 挂载远程控制方法: EmergencyStop()
    UA_MethodAttributes mAttr = UA_MethodAttributes_default;
    mAttr.displayName = UA_LOCALIZEDTEXT("en-US", "EmergencyStop");
    mAttr.executable = true;
    mAttr.userExecutable = true;

    UA_Server_addMethodNode(
        server,
        UA_NODEID_STRING(1, "PumpMotor.EmergencyStop"),
        pump_obj_id,
        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
        UA_QUALIFIEDNAME(1, "EmergencyStop"),
        mAttr,
        &EmergencyStopMethodCallback,
        0, NULL, 0, NULL, NULL, NULL
    );

    pr_info("[OPC UA] Information Model tree constructed successfully!\n");
}

int main(void) {
    signal(SIGINT, stop_handler);
    signal(SIGTERM, stop_handler);

    // 1. 初始化 open62541 服务端配置 (默认监听标准端口 TCP 4840)
    UA_Server *server = UA_Server_new();
    UA_ServerConfig_setDefault(UA_Server_getConfig(server));

    // 2. 构建自描述信息模型节点树
    Build_Industrial_Information_Model(server);

    pr_info("[OPC UA SERVER] Server is running at opc.tcp://0.0.0.0:4840 ...\n");

    // 3. 运行服务端主循环
    UA_StatusCode status = UA_Server_run(server, &g_running);

    UA_Server_delete(server);
    return status == UA_STATUSCODE_GOOD ? 0 : 1;
}

工业实战收益:从“哑巴数据”到“数字孪生(Digital Twin)”

将这套服务端运行在工业网关上:

  1. 任何标准的第三方 OPC UA 客户端(如 UaExpert 或西门子 WinCC SCADA)连接后,无需任何预先的配置文件,点击“Browse”即可在界面上一键展开整个工厂的物理层级树;
  2. 客户端可以直接订阅 PumpMotor.RPM,并在数值发生变动时以 毫秒级发布/订阅机制(Pub/Sub) 异步接收推送;
  3. 上层系统通过直接调用 EmergencyStop() 即可触发底层硬件急停,消灭了传统 Modbus 繁琐的线圈置位与状态对账。

构建标准的 OPC UA 信息模型,工业边缘网关才能真正打破现场 OT 设备与云端 IT 系统之间的语义鸿沟,实现现代工业互联的高效互通。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐