You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用open62541的UA_Client_Service_read返回空响应求助

问题:open62541批量读取OPC UA节点返回空响应

我用C语言基于open62541库编写程序,通过OPC/UA轮询数据并转发到Kafka。单独读取每个节点值正常,但使用UA_ReadRequest批量读取指定NodeID的多个值时,仅收到空响应——返回状态码为UA_STATUSCODE_GOOD,但resultsSize为0。OPC UA服务器由Python的freeopcua库搭建。

批量读取节点的代码

void retrieveOPCData(void)
{
    UA_ReadRequest request;
    UA_ReadRequest_init(&request);
    UA_ReadValueId ids[nodeCount];
    
    for (int i = 0; i < nodeCount; i++)
    {
        UA_ReadValueId_init(&ids[i]);
        ids[i].attributeId = UA_ATTRIBUTEID_VALUE;
        ids[i].nodeId = nodesToRead[i];
    }

    request.nodesToRead = ids;

    for (int i = 0; i < nodeCount; i++)
    {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "ID%i: %s, %i", i,
            request.nodesToRead[i].nodeId.identifier.string.data,
            request.nodesToRead[i].nodeId.namespaceIndex);
    }

    UA_ReadResponse response = UA_Client_Service_read(client, request);

    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Status: %i",
        response.responseHeader.serviceResult);
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Responses: %li", response.resultsSize);
}

正常工作的单个节点读取代码

void readNodeAtIndex(int index)
{
    if (index >= nodeCount)
    {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Index out of Range");
        return;
    }

    UA_Variant variant;
    UA_Variant_init(&variant);

    const UA_NodeId nodeId = nodesToRead[index];

    UA_StatusCode retval = UA_Client_readValueAttribute(client, nodeId, &variant);

    if (retval == UA_STATUSCODE_GOOD && UA_Variant_hasScalarType(&variant,
        &UA_TYPES[UA_TYPES_DOUBLE]))
    {
        UA_Double value = *(UA_Double*)variant.data;

        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Double-Value: %f", value);
    }
    else if (retval == UA_STATUSCODE_GOOD && UA_Variant_hasScalarType(&variant,
        &UA_TYPES[UA_TYPES_BOOLEAN]))
    {
        UA_Boolean value = *(UA_Boolean*)variant.data;

        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Boolean-Value: %i", value);
    }
    

    UA_Variant_clear(&variant);
}

OPC UA服务器初始化代码

server = Server()
space_url = "opc.tcp://localhost:61032"
server.set_endpoint(space_url)
server.set_security_policy([ua.SecurityPolicyType.NoSecurity])
node = server.get_objects_node()
问题原因与修复

核心问题是未设置UA_ReadRequest的nodesToReadSize字段。open62541的结构体中,所有列表类型的字段都需要同时指定指针(nodesToRead)和长度(nodesToReadSize),否则服务器无法识别要处理的节点数量,最终返回空结果。

修改后的批量读取函数如下(关键修改已标注):

void retrieveOPCData(void)
{
    UA_ReadRequest request;
    UA_ReadRequest_init(&request);
    UA_ReadValueId ids[nodeCount];
    
    for (int i = 0; i < nodeCount; i++)
    {
        UA_ReadValueId_init(&ids[i]);
        ids[i].attributeId = UA_ATTRIBUTEID_VALUE;
        ids[i].nodeId = nodesToRead[i];
    }

    request.nodesToRead = ids;
    // 关键:设置要读取的节点数量
    request.nodesToReadSize = nodeCount;

    for (int i = 0; i < nodeCount; i++)
    {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "ID%i: %s, %i", i,
            request.nodesToRead[i].nodeId.identifier.string.data,
            request.nodesToRead[i].nodeId.namespaceIndex);
    }

    UA_ReadResponse response = UA_Client_Service_read(client, request);

    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Status: %i",
        response.responseHeader.serviceResult);
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Responses: %li", response.resultsSize);
    
    // 补充:清理响应资源,避免内存泄漏
    UA_ReadResponse_clear(&response);
}

额外说明:

  • 单个节点读取的UA_Client_readValueAttribute是封装好的高层API,内部已自动处理节点数量逻辑,因此不会出现该问题。
  • 调用UA_Client_Service_read后必须用UA_ReadResponse_clear释放响应资源,防止内存泄漏。

内容的提问来源于stack exchange,提问作者Sebastian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 01:04:58