2026-04-26 12:25:13 +08:00
|
|
|
#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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
2026-06-03 08:54:01 +08:00
|
|
|
QMutexLocker locker(&dataQueueLock);
|
|
|
|
|
dataQueue.enqueue(std::make_pair(snCode, data));
|
2026-04-26 12:25:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
2026-06-03 08:54:01 +08:00
|
|
|
QQueue<std::pair<QString, std::map<QString, QVariant>>> vals;
|
2026-04-26 12:25:13 +08:00
|
|
|
|
|
|
|
|
// 从队列获取数据
|
|
|
|
|
{
|
2026-06-03 08:54:01 +08:00
|
|
|
QMutexLocker locker(&dataQueueLock);
|
|
|
|
|
if (dataQueue.isEmpty()) {
|
2026-04-26 12:25:13 +08:00
|
|
|
break;
|
|
|
|
|
}
|
2026-06-03 08:54:01 +08:00
|
|
|
dataQueue.swap(vals);
|
2026-04-26 12:25:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
2026-06-03 08:54:01 +08:00
|
|
|
while (!vals.empty()) {
|
|
|
|
|
auto val = vals.dequeue();
|
|
|
|
|
processData(val.first, val.second);
|
|
|
|
|
}
|
2026-04-26 12:25:13 +08:00
|
|
|
}
|
|
|
|
|
catch (const std::exception& ex) {
|
|
|
|
|
qCritical() << "addData: Exception caught:" << ex.what();
|
|
|
|
|
}
|
|
|
|
|
catch (...) {
|
|
|
|
|
qCritical() << "addData: Unknown exception caught.";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// 统一锁顺序:先队列锁,再数据库锁
|
2026-06-03 08:54:01 +08:00
|
|
|
QMutex dataQueueLock;
|
|
|
|
|
QQueue<std::pair<QString, std::map<QString, QVariant>>> dataQueue;
|
2026-04-26 12:25:13 +08:00
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
//判断是否为白班
|
2026-06-03 08:54:01 +08:00
|
|
|
bool isDayShift = (hour > dayShiftHour || (hour == dayShiftHour && minute >= dayShiftMin)) && (hour < nightShiftHour || (hour == nightShiftHour && minute <= nightShiftMin));
|
|
|
|
|
//判断是否为前一天(只有夜班可能是前一天的
|
|
|
|
|
bool isDayBefore = hour < dayShiftHour || (hour == dayShiftHour && minute < dayShiftMin);
|
2026-04-26 12:25:13 +08:00
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
//判断是否为白班
|
2026-06-03 08:54:01 +08:00
|
|
|
bool isDayShift = (hour > dayShiftHour || (hour == dayShiftHour && minute >= dayShiftMin)) && (hour < nightShiftHour || (hour == nightShiftHour && minute <= nightShiftMin));
|
|
|
|
|
//判断是否为前一天(只有白班可能是前一天的
|
|
|
|
|
bool isDayBefore = hour < nightShiftHour || (hour == nightShiftHour && minute < nightShiftMin);
|
2026-04-26 12:25:13 +08:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|