#include #pragma once class dataRecorder : public QThread { public: dataRecorder(SQLiteDB* dbIn, QList>externalInitDataVec = QList>()) : db(dbIn) { db_InitColumns.append(externalInitDataVec); } ~dataRecorder() { // 确保线程安全退出 if (isRunning()) { quit(); wait(5000); // 等待最多5秒 } } std::map csvAndChartLink; SQLiteDB* db; void addData(QString snCode, std::map 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 getDataBySnCode(const QString& snCode) { auto result = db->_search(getCurrentProcessCsvName(), {}, { {"条码", snCode} });; return result.size() > 0 ? result[0] : std::map(); } protected: void run() override { while (true) { std::pair> 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>> queue; std::mutex _mutex; int m_intervalMs = 10000; QElapsedTimer timer; QString currentProcessCsvNameCache; nlohmann::json m_json; QList> db_newCol = {}; QList> db_InitColumns = { {"条码", "TEXT NOT NULL PRIMARY KEY"}, {"创建时间", "TEXT"}, {"修改时间", "TEXT"}, {"总流程耗时", "TEXT"}, {"算法结果", "TEXT"}, {"AI检测结果", "TEXT"}, }; void processData(const QString& snCode, const std::map& 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 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 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 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& data) { // 获取当前表的列信息 QList 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 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; } } } };