Files
eUi/include/sql/dataRecorder.h
T
RCiRoot 97462c8aab On main: V20260416-2.5.1
增加halconGuard类,用于防止halcon算法的输入输出和程序不一致而引起的报错,并且会检查算法文件是否存在,如不存在则会弹窗提示。
增加handleAiGpuThreadGuard类,用于防止因重复调用同一个模型而引起的报错,并且支持不停机更新模型,支持初始化时检测模型文件是否存在。
增加内存占用显示控件。
用户管理增加root权限,默认密码为dseuiroot,模型热更新时需要此权限。
支持缓存界面上的检测数据。
相机模块新增在回调后的指定时间没有取图时则清空容器的功能。
增加等待结果汇总功能,在启用时同一个SN的图片会等待总结果后再进行存图。
存图线程修改为多线程编码,单线程储存,提高IO效率。并且增加根据内存占用的百分比更新存图逻辑的功能,默认占用超过70%不储存OK原图,超过80%不储存原图,超过90%时不储存所有图片。
2026-04-26 12:25:13 +08:00

253 lines
9.2 KiB
C++

#include <sql/qSqlBase.h>
#pragma once
class dataRecorder : public QThread {
public:
dataRecorder(SQLiteDB* dbIn, QList<std::pair<QString, QString>>externalInitDataVec = QList<std::pair<QString, QString>>()) : db(dbIn) {
db_InitColumns.append(externalInitDataVec);
}
~dataRecorder() {
// 确保线程安全退出
if (isRunning()) {
quit();
wait(5000); // 等待最多5秒
}
}
std::map<QString, QString> csvAndChartLink;
SQLiteDB* db;
void addData(QString snCode, std::map<QString, QVariant> data) {
if (snCode.isEmpty()) {
qWarning() << "addData: snCode is empty.";
return;
}
if (data.empty()) {
qWarning() << "addData: data is empty.";
return;
}
{
QMutexLocker locker(&queueLock);
queue.enqueue(std::make_pair(snCode, data));
}
if (!isRunning()) {
start();
}
}
std::map<QString, QVariant> getDataBySnCode(const QString& snCode) {
auto result = db->_search(getCurrentProcessCsvName(), {}, { {"条码", snCode} });;
return result.size() > 0 ? result[0] : std::map<QString, QVariant>();
}
protected:
void run() override {
while (true) {
std::pair<QString, std::map<QString, QVariant>> val;
// 从队列获取数据
{
QMutexLocker locker(&queueLock);
if (queue.isEmpty()) {
break;
}
val = queue.dequeue();
}
try {
processData(val.first, val.second);
}
catch (const std::exception& ex) {
qCritical() << "addData: Exception caught:" << ex.what();
}
catch (...) {
qCritical() << "addData: Unknown exception caught.";
}
}
}
private:
// 统一锁顺序:先队列锁,再数据库锁
QMutex queueLock;
QQueue<std::pair<QString, std::map<QString, QVariant>>> queue;
std::mutex _mutex;
int m_intervalMs = 10000;
QElapsedTimer timer;
QString currentProcessCsvNameCache;
nlohmann::json m_json;
QList<std::pair<QString, QString>> db_newCol = {};
QList<std::pair<QString, QString>> db_InitColumns = {
{"条码", "TEXT NOT NULL PRIMARY KEY"},
{"创建时间", "TEXT"},
{"修改时间", "TEXT"},
{"总流程耗时", "TEXT"},
{"算法结果", "TEXT"},
{"AI检测结果", "TEXT"},
};
void processData(const QString& snCode, const std::map<QString, QVariant>& data) {
QString currentProcessCsvName = getCurrentProcessCsvName();
// 确保表存在
if (currentProcessCsvName != currentProcessCsvNameCache) {
if (!db->createTable(currentProcessCsvName, db_InitColumns)) {
qWarning() << "addData: Failed to create table:" << currentProcessCsvName;
return;
}
currentProcessCsvNameCache = currentProcessCsvName;
}
// 检查并添加缺失的列
ensureColumns(currentProcessCsvName, data);
QString nowTime = QDateTime::currentDateTime().toString("yyyy_MM_dd hh:mm:ss.zzz");
// 检查数据是否存在
auto nowRes = db->_search(currentProcessCsvName, {}, { {"条码", snCode} });
if (nowRes.empty()) {
//数据不存在 检查上一个班次的数据
auto lastRes = db->_search(getLastProcessCsvName(), {}, { {"条码", snCode} });
//数据依旧不存在
if (lastRes.empty()) {
// 插入新数据
std::map<QString, QVariant> insertData = data;
insertData["条码"] = snCode;
insertData["创建时间"] = nowTime;
insertData["修改时间"] = nowTime;
if (!db->_insert(currentProcessCsvName, insertData)) {
qWarning() << "addData: Insert failed for table:" << currentProcessCsvName << "snCode:" << snCode;
}
}
else {//数据在上一个班次 更新到上一个班次的数据
std::map<QString, QVariant> updateData = data;
updateData["修改时间"] = nowTime;
if (lastRes[0]["算法结果"] == "NG")
updateData["算法结果"] = "NG";
if (lastRes[0]["AI检测结果"] == "NG") {
updateData["AI检测结果"] = "NG";
}
if (!db->_update(getLastProcessCsvName(), updateData, { {"条码", snCode} })) {
qWarning() << "addData: Update failed for table:" << getLastProcessCsvName() << "snCode:" << snCode;
}
}
}
else {//数据在当前班次 更新当前数据
std::map<QString, QVariant> updateData = data;
updateData["修改时间"] = nowTime;
if (nowRes[0]["算法结果"] == "NG") {
updateData["算法结果"] = "NG";
}
if (nowRes[0]["AI检测结果"] == "NG") {
updateData["AI检测结果"] = "NG";
}
if (!db->_update(getCurrentProcessCsvName(), updateData, { {"条码", snCode} })) {
qWarning() << "addData: Update failed for table:" << getCurrentProcessCsvName() << "snCode:" << snCode;
}
}
}
void ensureColumns(const QString& tableName, const std::map<QString, QVariant>& data) {
// 获取当前表的列信息
QList<QString> tableColumns;
auto columnsResult = db->query("PRAGMA table_info(\"" + tableName + "\")");
for (const auto& row : columnsResult) {
tableColumns.append(row.value("name").toString());
}
// 添加缺失的列
for (const auto& kv : data) {
if (!tableColumns.contains(kv.first)) {
if (!db->addColumn(tableName, kv.first, "TEXT")) {
qWarning() << "addData: Failed to add column:" << kv.first << "to table:" << tableName;
}
}
}
}
void init() {
std::lock_guard<std::mutex> locker(_mutex);
if (!db->createTable(getLastProcessCsvName(), db_InitColumns)) {
qDebug() << "Failed to create table " << getLastProcessCsvName();
}
currentProcessCsvNameCache = getCurrentProcessCsvName();
if (!db->createTable(currentProcessCsvNameCache, db_InitColumns)) {
qDebug() << "Failed to create table " << currentProcessCsvNameCache;
}
for (const auto& data : db_newCol) {
db->addColumn(getLastProcessCsvName(), data.first, data.second);
db->addColumn(getCurrentProcessCsvName(), data.first, data.second);
}
timer.start();
}
//获取当前班次的名字
QString getCurrentProcessCsvName() {
QDateTime currentDateTime = QDateTime::currentDateTime();
int hour = currentDateTime.time().hour();
int minute = currentDateTime.time().minute();
//判断是否为白班
bool isDayShift = (hour > 8 || (hour == 8 && minute >= 30)) && (hour < 20 || (hour == 20 && minute <= 30));
//判断是否为前一天
bool isDayBefore = hour < 8 || (hour == 8 && minute <= 29);
QString dateStr;
if (isDayBefore) {
dateStr = currentDateTime.addDays(-1).toString("yyyy-MM-dd");
}
else {
dateStr = currentDateTime.toString("yyyy-MM-dd");
}
return dateStr + (isDayShift ? "白班" : "夜班");
}
//获取上一个班次的名字
QString getLastProcessCsvName() {
QDateTime currentDateTime = QDateTime::currentDateTime();
int hour = currentDateTime.time().hour();
int minute = currentDateTime.time().minute();
//判断是否为白班
bool isDayShift = (hour > 8 || (hour == 8 && minute >= 30)) && (hour < 20 || (hour == 20 && minute <= 30));
//判断是否为前一天
bool isDayBefore = hour < 8 || (hour == 8 && minute <= 29);
QDateTime lastShiftDateTime;
//如果当前为前一天的夜班 那就返回前一天的白班
if (isDayBefore) {
return currentDateTime.addDays(-1).toString("yyyy-MM-dd") + "白班";
}
//如果当前为今天的白班 那就返回前一天的夜班
else if (!isDayBefore && isDayShift) {
return currentDateTime.addDays(-1).toString("yyyy-MM-dd") + "夜班";
}
//如果当前为今天的夜班 那就返回今天的白班
return currentDateTime.addDays(-1).toString("yyyy-MM-dd") + "白班";
}
void exportCsv() {
QString name = getLastProcessCsvName();
db->exportTableToCSV(name, "./csv/" + name + ".csv");
name = getCurrentProcessCsvName();
db->exportTableToCSV(name, "./csv/" + name + ".csv");
QString currentProcessCsvName = getCurrentProcessCsvName();
if (currentProcessCsvName != currentProcessCsvNameCache) {
if (db->createTable(currentProcessCsvName, db_InitColumns)) {
currentProcessCsvNameCache = currentProcessCsvName;
}
}
}
};