信创电话录音盒二次开发设备事件读取标准流程 (Qt C++)

1. 核心流程概述

读取设备事件的完整生命周期分为三个阶段:

  1. 初始化阶段 :程序启动时,调用 agi_ub_evt_create_json_pipe 创建事件管道。
  2. 运行阶段 :在循环或定时器中,先获取缓冲大小,再读取事件数据,解析 JSON 并处理业务逻辑。注意:此阶段严禁释放管道。
  3. 销毁阶段 :程序退出前,调用 agi_ub_evt_destroy_json_pipe 释放管道资源。

涉及的 SDK API

表格

函数名 功能描述
agi_ub_evt_create_json_pipe 创建事件读取管道,指定编码格式(GBK/UTF-8)。
agi_ub_evt_get_json_buf_size 获取最新事件所需的缓冲长度(支持阻塞等待)。
agi_ub_evt_pop_json_buf_data 从管道中读取具体的事件数据到缓冲区。
agi_ub_evt_destroy_json_pipe 销毁并释放事件管道。

2. 类成员变量定义

建议在管理类或单例中定义以下成员变量,用于维持管道状态和缓冲数据。

复制代码
#include <QString>
#include <QJsonObject>
#include <string>

class DeviceEventReader {
public:
    // ... 其他成员函数 ...

private:
    int m_nEvt_pipe_id = 0;       // 事件管道 ID,0 表示未创建
    std::string m_s_evt_data;     // 事件数据缓冲区
};

3. 核心功能代码实现

3.1 辅助函数:GBK 转 JSON

由于 SDK 可能返回 GBK 编码的字符串,需将其转换为 UTF-8 后再由 Qt 解析为 QJsonObject

复制代码
#include <QJsonDocument>
#include <QJsonObject>
#include <QByteArray>
#include <QDebug>
#include <cstring>

/**
 * @brief 将 GBK 编码的 JSON 字符串转换为 QJsonObject
 * @param jsonString GBK 编码的 C 风格字符串
 * @return 解析后的 QJsonObject,若解析失败返回空对象
 */
QJsonObject GBKToJson(const char *jsonString) {
    QJsonObject jsonObject;
    if (!jsonString || strlen(jsonString) == 0) {
        return jsonObject;
    }

    // 假设 gbkToUtf8 是项目中已有的转换函数,若无需自行实现 QTextCodec 转换
    // 示例: QByteArray utf8 = QTextCodec::codecForName("GBK")->toUnicode(jsonString).toUtf8();
    QByteArray utf8 = gbkToUtf8(jsonString); 

    QJsonParseError parseError;
    QJsonDocument jsonDocument = QJsonDocument::fromJson(utf8, &parseError);

    if (jsonDocument.isNull()) {
        qDebug() << "[Error] Parse JSON failed:" << parseError.errorString() 
                 << "Raw Data:" << utf8.data();
        return jsonObject;
    }

    return jsonDocument.object();
}

3.2 初始化:创建事件管道

在程序启动或类初始化时调用。

复制代码
/**
 * @brief 初始化事件管道
 * @note 需在主线程或初始化线程中调用一次
 */
void DeviceEventReader::initEventPipe() {
    if (m_nEvt_pipe_id == 0) {
        // 参数说明:
        // 第1、2个参数通常为过滤条件,留空表示接收所有事件
        // 第3个参数指定编码:"gbk" 或 "utf-8"
        m_nEvt_pipe_id = agi_ub_evt_create_json_pipe("", "", "gbk");
        
        if (m_nEvt_pipe_id > 0) {
            qDebug() << "[Info] Event pipe created successfully. ID:" << m_nEvt_pipe_id;
        } else {
            qDebug() << "[Error] Failed to create event pipe.";
        }
    }
}

3.3 核心逻辑:读取并解析事件

这是高频调用的函数,建议放入单独的工作线程循环执行,或由 QTimer 触发。

复制代码
/**
 * @brief 从管道弹出并解析一个事件
 * @param wait_ms 阻塞等待时间(ms)。
 *                - 若在独立线程循环中:建议 100~1000ms,避免空转占用 CPU。
 *                - 若在 QTimer 中:建议设为 0,由定时器控制频率(如 100ms/次)。
 * @param s_class [输出] 事件类型/class
 * @param s_account [输出] 设备序列号/account
 * @param json_evt [输出] 完整的原始事件 JSON 对象
 * @return 0 表示成功读取并解析,-1 表示无事件或发生错误
 */
int DeviceEventReader::popEvtJsonData(int wait_ms, QString &s_class, QString &s_account, QJsonObject &json_evt) {
    int evt_buf_size = 0;

    // 1. 检查管道有效性并获取所需缓冲大小
    // 如果 wait_ms > 0 且当前无事件,SDK 会阻塞等待直到超时或有新事件
    if (m_nEvt_pipe_id > 0 && 
        (evt_buf_size = agi_ub_evt_get_json_buf_size(m_nEvt_pipe_id, wait_ms)) > 0) {
        
        // 2. 预分配缓冲区 (额外预留 2048 字节以防边界情况)
        m_s_evt_data.reserve(evt_buf_size + 2048);
        
        // 3. 读取事件数据到缓冲区
        // 注意:agi_ub_evt_pop_json_buf_data 返回 0 通常表示成功,具体请参考 SDK 文档确认返回值定义
        if (agi_ub_evt_pop_json_buf_data(m_nEvt_pipe_id, 0, const_cast<char*>(m_s_evt_data.c_str()), static_cast<int>(m_s_evt_data.capacity())) == 0) {
            
            // 4. 转换编码并解析 JSON
            json_evt = GBKToJson(m_s_evt_data.c_str());
            
            if (!json_evt.isEmpty()) {
                // 5. 提取关键字段
                s_class = json_evt["class"].toString();
                s_account = json_evt["account"].toString();
                return 0; // 成功
            } else {
                qDebug() << "[Warning] JSON parsed but is empty or invalid structure.";
            }
        } else {
            qDebug() << "[Error] agi_ub_evt_pop_json_buf_data failed.";
        }
    }
    
    return -1; // 无事件或失败
}

3.4 销毁:释放资源

在程序退出析构函数中调用。

复制代码
/**
 * @brief 销毁事件管道
 * @note 程序结束前必须调用,防止资源泄漏
 */
void DeviceEventReader::cleanupEventPipe() {
    if (m_nEvt_pipe_id != 0) {
        agi_ub_evt_destroy_json_pipe(m_nEvt_pipe_id);
        m_nEvt_pipe_id = 0;
        qDebug() << "[Info] Event pipe destroyed.";
    }
}

4. 使用场景示例

场景 A:在独立线程中循环读取(推荐)

适用于对实时性要求较高,且不希望受 UI 线程定时器精度影响的场景。

复制代码
// 在工作线程的 run() 函数中
void EventWorkerThread::run() {
    initEventPipe();
    
    while (!m_stopFlag) {
        QString sClass, sAccount;
        QJsonObject jsonEvt;
        
        // 阻塞等待 200ms,既保证实时性又降低 CPU 占用
        if (popEvtJsonData(200, sClass, sAccount, jsonEvt) == 0) {
            // 处理业务逻辑,例如发送信号到 UI 线程
            emit eventReceived(sClass, sAccount, jsonEvt);
        }
    }
    
    cleanupEventPipe();
}

场景 B:在 QTimer 定时器中读取

适用于轻量级应用,逻辑简单,集成在现有 UI 线程或单线程模型中。

复制代码
// 初始化定时器
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, [=]() {
    QString sClass, sAccount;
    QJsonObject jsonEvt;
    
    // 设置为 0,不阻塞,立即返回
    if (popEvtJsonData(0, sClass, sAccount, jsonEvt) == 0) {
        // 处理事件
        handleDeviceEvent(sClass, sAccount, jsonEvt);
    }
});
timer->start(150); // 每 150ms 轮询一次

5. 注意事项

  1. 编码一致性agi_ub_evt_create_json_pipe 中指定的编码(如 "gbk")必须与后续 GBKToJson 中的解码逻辑一致。若 SDK 支持 UTF-8,建议直接使用 "utf-8" 以简化转换流程。
  2. 线程安全m_nEvt_pipe_idm_s_evt_data 若在多线程环境下被访问,需确保加锁保护,或严格遵循"单线程创建、单线程读写、单线程销毁"的原则。
  3. 缓冲区管理 :代码中使用 std::stringreservecapacity 配合 C 接口是安全的,但需确保传入 agi_ub_evt_pop_json_buf_data 的长度参数不超过实际分配容量。
  4. 阻塞策略
    • 高实时性 :使用独立线程 + wait_ms > 0
    • 低资源占用/UI 集成 :使用 QTimer (间隔 100ms+) + wait_ms = 0。切勿在 UI 主线程中使用 wait_ms > 0,否则会导致界面卡顿。
相关推荐
agicall.com2 个月前
信创电话助手录音模式说明:单轨混音 vs 双轨立体声
人工智能·语音识别·自动录音·电话录音盒·固话座机·统信uos电话录音