1. 核心流程概述
读取设备事件的完整生命周期分为三个阶段:
- 初始化阶段 :程序启动时,调用
agi_ub_evt_create_json_pipe创建事件管道。 - 运行阶段 :在循环或定时器中,先获取缓冲大小,再读取事件数据,解析 JSON 并处理业务逻辑。注意:此阶段严禁释放管道。
- 销毁阶段 :程序退出前,调用
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. 注意事项
- 编码一致性 :
agi_ub_evt_create_json_pipe中指定的编码(如"gbk")必须与后续GBKToJson中的解码逻辑一致。若 SDK 支持 UTF-8,建议直接使用"utf-8"以简化转换流程。 - 线程安全 :
m_nEvt_pipe_id和m_s_evt_data若在多线程环境下被访问,需确保加锁保护,或严格遵循"单线程创建、单线程读写、单线程销毁"的原则。 - 缓冲区管理 :代码中使用
std::string的reserve和capacity配合 C 接口是安全的,但需确保传入agi_ub_evt_pop_json_buf_data的长度参数不超过实际分配容量。 - 阻塞策略 :
- 高实时性 :使用独立线程 +
wait_ms > 0。 - 低资源占用/UI 集成 :使用 QTimer (间隔 100ms+) +
wait_ms = 0。切勿在 UI 主线程中使用wait_ms > 0,否则会导致界面卡顿。
- 高实时性 :使用独立线程 +