On main: V20260416-2.5.1
增加halconGuard类,用于防止halcon算法的输入输出和程序不一致而引起的报错,并且会检查算法文件是否存在,如不存在则会弹窗提示。 增加handleAiGpuThreadGuard类,用于防止因重复调用同一个模型而引起的报错,并且支持不停机更新模型,支持初始化时检测模型文件是否存在。 增加内存占用显示控件。 用户管理增加root权限,默认密码为dseuiroot,模型热更新时需要此权限。 支持缓存界面上的检测数据。 相机模块新增在回调后的指定时间没有取图时则清空容器的功能。 增加等待结果汇总功能,在启用时同一个SN的图片会等待总结果后再进行存图。 存图线程修改为多线程编码,单线程储存,提高IO效率。并且增加根据内存占用的百分比更新存图逻辑的功能,默认占用超过70%不储存OK原图,超过80%不储存原图,超过90%时不储存所有图片。
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+3
-267
@@ -28,6 +28,9 @@
|
||||
#include <global.h>
|
||||
#include <qthread.h>
|
||||
#include <qmutex.h>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
class SQLiteDB {
|
||||
public:
|
||||
@@ -451,271 +454,4 @@ private:
|
||||
QString dbPath;
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
bool isNewData(const QString& snCode) {
|
||||
auto res = db->_search(getCurrentProcessCsvName(), {}, { {"条码", snCode} });
|
||||
return res.empty();
|
||||
}
|
||||
|
||||
bool isAlgoNgData(const QString& snCode) {
|
||||
auto res = db->_search(getCurrentProcessCsvName(), {}, { {"条码", snCode} });
|
||||
if (res.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (res[0]["算法结果"].toString() == "NG") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isAiDetNgData(const QString& snCode) {
|
||||
auto res = db->_search(getCurrentProcessCsvName(), {}, { {"条码", snCode} });
|
||||
if (res.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (res[0]["AI检测结果"].toString() == "NG") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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"},
|
||||
{"CT", "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 res = db->_search(currentProcessCsvName, {}, { {"条码", snCode} });
|
||||
|
||||
if (res.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 (isAlgoNgData(snCode)) {
|
||||
updateData["算法结果"] = "NG";
|
||||
}
|
||||
if (isAiDetNgData(snCode)) {
|
||||
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 (isAlgoNgData(snCode)) {
|
||||
updateData["算法结果"] = "NG";
|
||||
}
|
||||
if (isAiDetNgData(snCode)) {
|
||||
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 && minute >= 30) && (hour <= 20 && minute <= 30);
|
||||
bool isDayBefore = hour <= 20 && minute <= 30;
|
||||
|
||||
QDateTime lastShiftDateTime;
|
||||
if (isDayBefore) {
|
||||
lastShiftDateTime = currentDateTime.addDays(-1);
|
||||
} else {
|
||||
lastShiftDateTime = currentDateTime;
|
||||
}
|
||||
|
||||
QString dateStr = lastShiftDateTime.toString("yyyy-MM-dd");
|
||||
return dateStr + (isDayShift ? "夜班" : "白班");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#pragma once
|
||||
Reference in New Issue
Block a user