diff --git a/include/global.h b/include/global.h index f9c6304..f851341 100644 --- a/include/global.h +++ b/include/global.h @@ -8,7 +8,7 @@ #include #include #pragma execution_character_set("utf-8") -const std::string softwareVersion = "2.0.3"; +const std::string softwareVersion = "V20251030-2.1.0"; const std::string euiJsonVersion = "1.0.6"; #define globalDatabasePth "./sqlDatabase/eui_db.db" //全局数据库路径 #define saveImgEnable 1 diff --git a/include/interface.h b/include/interface.h index 6099498..d2ae433 100644 --- a/include/interface.h +++ b/include/interface.h @@ -1,14 +1,21 @@ -#include +#include #include #include -class IBackend { +class IBackend :public QThread { + Q_OBJECT public: virtual ~IBackend() = default; - virtual void test() = 0; virtual void init(SQLiteDB* ,qtFuncBridge*,nlohmann::ordered_json& json) = 0; virtual void cleanup() = 0; + virtual nlohmann::ordered_json getJson() = 0; + virtual void getAllParams(std::vector>* params) = 0; + virtual void editParam(QString name, QVariant value) = 0; +public slots: virtual bool excuteCliCommand(const QString& command) = 0; virtual bool excuteCliCommandPreset(const QString& uuid) = 0; + virtual void triggerAndCaptureImage(std::string cameraName, std::vector* mats) = 0; + virtual void getCameraNames(std::vector* cameraNames) = 0; + }; extern "C" { diff --git a/include/qtBridge.h b/include/qtBridge.h index 2a2a177..f90ecd1 100644 --- a/include/qtBridge.h +++ b/include/qtBridge.h @@ -1,18 +1,25 @@ -#pragma once +#pragma once #include +#include +#include +#include + +#include +#include #include -#include -#include #include #include + #include +#include enum initStatus{ NotInit, InitFailure, InitOK }; +// 展示图片所需的打包结构体,用于在跨线程信号中携带图像与元信息 struct showImgPack{ cv::Mat img; QString name = ""; @@ -20,8 +27,36 @@ struct showImgPack{ showImgPack() {}; showImgPack(QString name,QString type) : type(type), name(name) {} }; + +enum valueType { + finsShort, + finsFloat +}; + +// PLC 值查看器的配置结构体 +struct PlcValueViewerStruct { + QString valueName; + int valueAddr; + valueType type = finsShort; + QVariant value = -999; +}; + class qtFuncBridge : public QObject { Q_OBJECT +public: + // 获取单例实例(线程不安全的延迟初始化,适用于主线程初始化) + static qtFuncBridge* getInstance() { + static qtFuncBridge* s_instance = nullptr; + if (!s_instance) { + // intentionally leaked: lives for application lifetime + s_instance = new qtFuncBridge(nullptr); + } + return s_instance; + } + + // 禁止拷贝 + Q_DISABLE_COPY(qtFuncBridge) + signals: // 日志相关 void loadingWindow_appendInitLog(const QString& text, const QColor& color); @@ -45,7 +80,7 @@ signals: void setPlcConnectionOk(); void setCamConnectionError(); void setCamConnectionOk(); - + void waveFormAddData(QString windowName, double value); /////////////////////////////////////////////// // 切换界面 void switchMainStakedWidget(int index); @@ -62,14 +97,22 @@ signals: void loadingWindow_appendWarningLog(QString text); void loadingWindow_clearWarningLog(); ////////////////////////////////////////////////////////////// - void reInitConfig(); + void restartInit(); //排障 void runCommand_addBtn(const QString& btnName, const QString& tip, const QString& uuid); - void runCommand_addPlcValueViewer(const QString& groupName,std::vector> viewerNameAndAddress); - void runCommand_refeshPlcValueViewer(int address,int value); + void runCommand_addPlcValueViewer(QString groupName, std::vector> viewerNameAndAddress); + void runCommand_refeshPlcValueViewer(int address,QString value); //性能监测 void sendTriggerManagerLoopData(int loopCounterPerMin,int maxInterval); + + void triggerAndCaptureImage(std::string cameraName, std::vector* mats); + void getCameraNames(std::vector* cameraNames); + void getAllParams(std::vector>* params); + void editParam(QString name, QVariant value); +private: + explicit qtFuncBridge(QObject* parent = nullptr) : QObject(parent) {} + ~qtFuncBridge() = default; }; -#pragma once \ No newline at end of file + diff --git a/include/simpleTools/windowsCtrl.hpp b/include/simpleTools/windowsCtrl.hpp index 4afa251..b3c40fc 100644 --- a/include/simpleTools/windowsCtrl.hpp +++ b/include/simpleTools/windowsCtrl.hpp @@ -12,6 +12,83 @@ #include #pragma comment(lib, "setupapi.lib") +// 禁用控制台选择(快速编辑与鼠标输入) +static bool disableConsoleSelection() { + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + if (hStdin == INVALID_HANDLE_VALUE || hStdin == NULL) return false; + + DWORD mode = 0; + if (!GetConsoleMode(hStdin, &mode)) return false; + + // 必须先设置 ENABLE_EXTENDED_FLAGS,再清除 ENABLE_QUICK_EDIT_MODE + DWORD newMode = mode | ENABLE_EXTENDED_FLAGS; + newMode &= ~(ENABLE_QUICK_EDIT_MODE | ENABLE_MOUSE_INPUT); + + if (newMode == mode) return true; // 已经是期望模式 + return SetConsoleMode(hStdin, newMode) != 0; +} + +// 恢复控制台选择(快速编辑与鼠标输入) +static bool enableConsoleSelection() { + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + if (hStdin == INVALID_HANDLE_VALUE || hStdin == NULL) return false; + + DWORD mode = 0; + if (!GetConsoleMode(hStdin, &mode)) return false; + + DWORD newMode = mode | ENABLE_EXTENDED_FLAGS | ENABLE_QUICK_EDIT_MODE | ENABLE_MOUSE_INPUT; + if (newMode == mode) return true; + return SetConsoleMode(hStdin, newMode) != 0; +} + +// 是否运行在 Windows Terminal 主机(PowerShell/命令行在 Windows Terminal 中) +static bool isWindowsTerminalHost() { + // Windows Terminal 会设置 WT_SESSION 环境变量 + return GetEnvironmentVariableW(L"WT_SESSION", nullptr, 0) > 0; +} + +// 绑定 CRT 与标准句柄到当前控制台 +static void bindCrtToConsoleIO() { + FILE* fp = nullptr; + freopen_s(&fp, "CONIN$", "r", stdin); + freopen_s(&fp, "CONOUT$", "w", stdout); + freopen_s(&fp, "CONOUT$", "w", stderr); + // 同步 C++ iostream 与 C stdio + std::ios::sync_with_stdio(); +} + +// 确保获得一个“不可选择”的控制台。 +// 如果检测到 Windows Terminal/PowerShell 环境,则脱离父控制台并新建独立控制台,避免被宿主选择影响。 +static bool ensureNoSelectionConsole(bool detachWhenWT = true) { + bool createdNew = false; + + if (detachWhenWT && isWindowsTerminalHost()) { + FreeConsole(); // 脱离 PowerShell/WT 的控制台 + if (AllocConsole()) { + bindCrtToConsoleIO(); + createdNew = true; + } + } else if (GetConsoleWindow() == NULL) { + // GUI 子系统或当前无控制台时创建一个 + if (AllocConsole()) { + bindCrtToConsoleIO(); + createdNew = true; + } + } else { + // 已有控制台,仍然尝试绑定,避免句柄无效 + bindCrtToConsoleIO(); + } + + // 禁用选择(对当前控制台生效) + bool ok = disableConsoleSelection(); + + // 保险:确保控制台窗口可见 + if (HWND hWnd = GetConsoleWindow()) { + ShowWindow(hWnd, SW_SHOW); + } + return createdNew ? ok : ok; +} + static void DisableComPort(const char* portName) { HDEVINFO hDevInfo; SP_DEVINFO_DATA DeviceInfoData; diff --git a/include/sql/qSqlBase.h b/include/sql/qSqlBase.h index 9d58cdb..65d02f5 100644 --- a/include/sql/qSqlBase.h +++ b/include/sql/qSqlBase.h @@ -33,7 +33,7 @@ class SQLiteDB { public: QSqlDatabase db; // 新增:全局互斥锁,保护所有数据库操作 - mutable std::mutex db_mutex; + mutable QMutex db_mutex; SQLiteDB(const QString& dbPath) : dbPath(dbPath) { @@ -43,7 +43,7 @@ public: // 所有公有方法加锁保护 bool execute(const QString& queryStr, const QVariantList& args = {}) { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); QSqlQuery query(db); query.prepare(queryStr); @@ -60,7 +60,7 @@ public: } QList> query(const QString& queryStr, const QVariantList& args = {}) { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); QList> results; QSqlQuery query(db); query.prepare(queryStr); @@ -87,7 +87,7 @@ public: } bool createTable(const QString& tableName, const QList>& columns) { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); if (columns.empty()) { qDebug() << "Create table failed: columns are empty."; return false; @@ -107,7 +107,7 @@ public: } std::vector getTables() { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); std::vector tables; QString sql = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';"; QSqlQuery query(db); @@ -122,13 +122,13 @@ public: } bool dropTable(const QString& tableName) { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); QString sql = QString("DROP TABLE IF EXISTS %1").arg(tableName); return execute(sql); } bool addColumn(const QString& tableName, const QString& columnName, const QString& columnType) { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); if (columnName.isEmpty() || columnType.isEmpty()) { qDebug() << "Add column failed: column name or type is empty."; return false; @@ -149,7 +149,7 @@ public: } bool tableExists(const QString& tableName) { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); QString sql = QString("SELECT name FROM sqlite_master WHERE type='table' AND name='%1';").arg(tableName); QList> result = query(sql); @@ -158,7 +158,7 @@ public: bool _insert(const QString& tableName, const std::map& data) { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); if (data.empty()) { qDebug() << "Insert failed: data is empty."; return false; @@ -200,7 +200,7 @@ public: bool _update(const QString& tableName, const std::map& data, const std::map& conditions) { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); if (data.empty() || conditions.empty()) { qDebug() << "Update failed: data or conditions are empty."; return false; @@ -242,7 +242,7 @@ public: bool _delete(const QString& tableName, const std::map& conditions) { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); if (conditions.empty()) { qDebug() << "Delete failed: conditions are empty."; return false; @@ -275,7 +275,7 @@ public: std::vector> _search(const QString& tableName, const QStringList& columns, const std::map& conditions = {}) { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); QStringList selectColumns = columns.isEmpty() ? QStringList("*") : columns; QString sql = QString("SELECT %1 FROM \"%2\"").arg(selectColumns.join(", "), tableName); @@ -316,13 +316,13 @@ public: } QList> queryTabelData(const QString& tableName) { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); QString sql = QString("SELECT * FROM \"%1\"").arg(tableName); return query(sql); } bool exportTableToCSV(const QString& tableName, const QString& filePath, std::mutex& _mutex) { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); using namespace std::chrono; auto start = high_resolution_clock::now(); @@ -372,7 +372,7 @@ public: } bool setWALMode(bool enableWAL = true, int busyTimeoutMs = 3000) { - std::lock_guard lock(db_mutex); + QMutexLocker lock(&db_mutex); if (!db.isOpen()) { qWarning() << "Database is not open, cannot set WAL mode."; return false; @@ -498,12 +498,12 @@ public: } bool isNewData(const QString& snCode) { - std::lock_guard locker(db->db_mutex); + QMutexLocker locker(&db->db_mutex); auto res = db->_search(getCurrentProcessCsvName(), {}, { {"条码",snCode} }); return res.empty(); } bool isNgData(const QString& snCode) { - std::lock_guard locker(db->db_mutex); + QMutexLocker locker(&db->db_mutex); auto res = db->_search(getCurrentProcessCsvName(), {}, { {"条码",snCode} }); if (res.empty() == true) return false; @@ -516,15 +516,17 @@ public: void run() override{ while (queue.size() > 0) { - queueLock.lock(); - auto val = queue.dequeue(); - queueLock.unlock(); + std::pair> val; + { + QMutexLocker locker(&queueLock); + val = queue.dequeue(); + } try { auto snCode = val.first; auto data = val.second; QString currentProcessCsvName = getCurrentProcessCsvName(); if (currentProcessCsvName != currentProcessCsvNameCache) { - std::lock_guard locker(db->db_mutex); + QMutexLocker locker(&db->db_mutex); if (!db->createTable(currentProcessCsvName, db_InitColumns)) { qWarning() << "addtData: Failed to create table:" << currentProcessCsvName; return; @@ -533,7 +535,7 @@ public: } QList tableColumns; { - std::lock_guard locker(db->db_mutex); + QMutexLocker locker(&db->db_mutex); QSqlQuery colQuery(db->db); QString colSql = QString("PRAGMA table_info(\"%1\")").arg(currentProcessCsvName); if (colQuery.exec(colSql)) { @@ -543,7 +545,6 @@ public: } } for (const auto& kv : data) { - std::lock_guard locker(db->db_mutex); if (!tableColumns.contains(kv.first)) { if (!db->addColumn(currentProcessCsvName, kv.first, "TEXT")) { qWarning() << "addtData: Failed to add column:" << kv.first << "to table:" << currentProcessCsvName; @@ -552,7 +553,6 @@ public: } std::vector> res; { - std::lock_guard locker(db->db_mutex); res = db->_search(currentProcessCsvName, {}, { {"条码",snCode} }); } QString nowTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"); @@ -560,7 +560,6 @@ public: if (res.empty()) { std::vector> lastRes; { - std::lock_guard locker(db->db_mutex); lastRes = db->_search(getLastProcessCsvName(), {}, { {"条码",snCode} }); } if (lastRes.empty()) { @@ -568,7 +567,6 @@ public: insertData["条码"] = snCode; insertData["创建时间"] = nowTime; insertData["修改时间"] = nowTime; - std::lock_guard locker(db->db_mutex); opResult = db->_insert(currentProcessCsvName, insertData); if (!opResult) { qWarning() << "addtData: Insert failed for table:" << currentProcessCsvName << "snCode:" << snCode; @@ -577,7 +575,6 @@ public: else { std::map updateData = data; updateData["修改时间"] = nowTime; - std::lock_guard locker(db->db_mutex); opResult = db->_update(getLastProcessCsvName(), updateData, { {"条码",snCode} }); if (!opResult) { qWarning() << "addtData: Update failed for table:" << getLastProcessCsvName() << "snCode:" << snCode; @@ -587,7 +584,6 @@ public: else { std::map updateData = data; updateData["修改时间"] = nowTime; - std::lock_guard locker(db->db_mutex); opResult = db->_update(getCurrentProcessCsvName(), updateData, { {"条码",snCode} }); if (!opResult) { qWarning() << "addtData: Update failed for table:" << getCurrentProcessCsvName() << "snCode:" << snCode; diff --git a/include/sxGlobal.h b/include/sxGlobal.h index 5c043f0..470c3f8 100644 --- a/include/sxGlobal.h +++ b/include/sxGlobal.h @@ -5,7 +5,6 @@ #include #include #pragma region 基础函数 - #define CATCH_EXCEPTIONS \ catch (HDevEngineCpp::HDevEngineException& ex) { \ std::cout << "HDevEngineException: " << ex.Message() << std::endl; \ @@ -71,7 +70,7 @@ static bool generalAlgoTest(std::shared_ptr data, detParams* _detParam textPainter.putText(Paint, "划伤", cv::Point(10,10), cv::Scalar(0, 255, 0)); cv::putText(Paint, "pos:" + data->additional["触发地址"].toString().toStdString(), cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0), 2); data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 - data->service->_missionResultHandle->handleAllResult(data->SNcode, !defects.size() > 0, defects);//发送图标操作 + data->service->_missionResultHandle->toBarChart(data->SNcode, !defects.size() > 0, defects);//发送图标操作 data->resultImgs.push_back(Paint); return true; } @@ -140,7 +139,6 @@ public: std::shared_ptr data = _serviceManager->_detDataManager->createDetData( QString::fromStdString(cameraName), sn, 1, camera->config.oriSaveFormat);//创建数据包 camera->captureImage(data);//拍照 - data->saveObjectName = saveObjectName; generalDet->ansyncProcess(data, address[batteryOffset], ("D" + address[0].completeAddr + "_1"));//处理算法 并在取到图片后发送完成信号 data->resultImgShowSet.push_back(showImgPack(viewerName, "结果图")); } diff --git a/include/yhGlobal.h b/include/yhGlobal.h index fabc3ea..555724b 100644 --- a/include/yhGlobal.h +++ b/include/yhGlobal.h @@ -85,8 +85,10 @@ static bool generalAlgoTest(std::shared_ptr data, detParams* _detParam TextRendererZH textPainter(Paint.cols, Paint.rows, 15); textPainter.putText(Paint, "划伤", cv::Point(10,10), cv::Scalar(0, 255, 0)); cv::putText(Paint, "pos:" + data->additional["触发地址"].toString().toStdString(), cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0), 2); + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.123");//发送数据1 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2)+ "_0.456");//发送数据2 data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 - data->service->_missionResultHandle->handleAllResult(data->SNcode, !defects.size() > 0, defects);//发送图标操作 + data->service->_missionResultHandle->toBarChart(data->SNcode, !defects.size() > 0, defects);//发送图标操作 data->resultImgs.push_back(Paint); return true; } @@ -153,12 +155,12 @@ public: }, ""); for (int i = 0; i < address.size(); i++) { _serviceManager->_triggerManager->registerPlcValueViewer(viewerNameIN + "_" + QString::number(i), - { {"触发",address[i].triggerAddr }, - {"反馈",address[i].completeAddr.toInt()}, - {"结果",address[i].resultAddr.toInt()}, - {"数值1",address[i].valueOffsetAddr}, - {"数值2",address[i].valueOffsetAddr + 2}, - {"数值3",address[i].valueOffsetAddr + 4} }); + { {"触发",address[i].triggerAddr,finsShort }, + {"反馈",address[i].completeAddr.toInt(),finsShort}, + {"结果",address[i].resultAddr.toInt(),finsShort}, + {"数值1",address[i].valueOffsetAddr,finsFloat}, + {"数值2",address[i].valueOffsetAddr + 2,finsFloat}, + {"数值3",address[i].valueOffsetAddr + 4,finsFloat} }); } } //获取图片并进行检测 @@ -170,7 +172,6 @@ public: std::shared_ptr data = _serviceManager->_detDataManager->createDetData( QString::fromStdString(cameraName), sn, 1, camera->config.oriSaveFormat);//创建数据包 camera->captureImage(data);//拍照 - data->saveObjectName = saveObjectName; generalDet->ansyncProcess(data, address[batteryOffset]);//处理算法 data->resultImgShowSet.push_back(showImgPack(viewerName, "结果图")); } @@ -231,29 +232,28 @@ public: evaluate(i, byteArray);}, ""); //注册清除完成信号触发器 任意触发地址归零时触发一次 _serviceManager->_triggerManager->registerCallback(address[i].triggerAddr, 0, [this, i](int triggerPos, int triggerValue, QByteArray& byteArray) { - _serviceManager->_triggerManager->writeShort("D" + QByteArray::number(address[0].completeAddr.toInt() + i * 2) + "_0"); }, "D" + address[0].completeAddr.toStdString() + "清零"); + _serviceManager->_triggerManager->writeShort("D" + address[i].completeAddr + "_0"); }, "D" + address[0].completeAddr.toStdString() + "清零"); } for (int i = 0; i < address.size(); i++) { _serviceManager->_triggerManager->registerPlcValueViewer(viewerNameIN + "_" + QString::number(i), - { {"触发",address[i].triggerAddr }, - {"反馈",address[i].completeAddr.toInt()}, - {"结果",address[i].resultAddr.toInt()}, - {"数值1",address[i].valueOffsetAddr}, - {"数值2",address[i].valueOffsetAddr + 2}, - {"数值3",address[i].valueOffsetAddr + 4} }); + { {"触发",address[i].triggerAddr,finsShort }, + {"反馈",address[i].completeAddr.toInt(),finsShort}, + {"结果",address[i].resultAddr.toInt(),finsShort}, + {"数值1",address[i].valueOffsetAddr,finsFloat}, + {"数值2",address[i].valueOffsetAddr + 2,finsFloat}, + {"数值3",address[i].valueOffsetAddr + 4,finsFloat} }); } } //获取图片并进行检测 void evaluate(int batteryOffset, QByteArray& byteArray) { - _serviceManager->_triggerManager->writeShort("D" + QByteArray::number(address[0].completeAddr.toInt() + batteryOffset) + "_1");//完成信号 根据偏移发送 + _serviceManager->_triggerManager->writeShort("D" + address[batteryOffset].completeAddr + "_1");//完成信号 根据偏移发送 std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, false, -1); auto camera = _serviceManager->_cameraManager->getCamera(cameraName); camera->clearCache(); std::shared_ptr data = _serviceManager->_detDataManager->createDetData( QString::fromStdString(cameraName), sn, 1, camera->config.oriSaveFormat);//创建数据包 camera->captureImage(data);//拍照 - data->saveObjectName = saveObjectName; generalDet->ansyncProcess(data, address[batteryOffset]);//处理算法 data->resultImgShowSet.push_back(showImgPack(viewerName, "结果图")); } diff --git a/resources/icon/biaoDingBanBiaoDing.png b/resources/icon/biaoDingBanBiaoDing.png new file mode 100644 index 0000000..a5001e4 Binary files /dev/null and b/resources/icon/biaoDingBanBiaoDing.png differ diff --git a/resources/icon/jiuDianBiaoDingBiaoDing.png b/resources/icon/jiuDianBiaoDingBiaoDing.png new file mode 100644 index 0000000..1a31533 Binary files /dev/null and b/resources/icon/jiuDianBiaoDingBiaoDing.png differ diff --git a/resources/resources.qrc b/resources/resources.qrc index 7e0d25a..27412fa 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -46,6 +46,8 @@ icon/plc_connection_ok.png icon/runningState.png icon/runningTime.png + icon/biaoDingBanBiaoDing.png + icon/jiuDianBiaoDingBiaoDing.png TongYongUiIcon/0.cameraModule.png diff --git a/rpcModule/rpc_finsTcp/rpc_finsTcp.cpp b/rpcModule/rpc_finsTcp/rpc_finsTcp.cpp index ea7e1fa..c7b8467 100644 --- a/rpcModule/rpc_finsTcp/rpc_finsTcp.cpp +++ b/rpcModule/rpc_finsTcp/rpc_finsTcp.cpp @@ -51,7 +51,6 @@ rpc_finsTcp::~rpc_finsTcp() { // 断开所有信号槽连接 subThread->disconnect(); - // 如果线程在运行,优雅退出 if (subThread->isRunning()) { subThread->quit(); // 最多等待3秒,防止死锁 diff --git a/src/Core/CMakeLists.txt b/src/Core/CMakeLists.txt index 04689e6..9a59a5b 100644 --- a/src/Core/CMakeLists.txt +++ b/src/Core/CMakeLists.txt @@ -17,7 +17,7 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/mission/algo ${CMAKE_CURRENT_SOURCE_DIR}/appConfig - ${CMAKE_CURRENT_SOURCE_DIR}/back + ${CMAKE_CURRENT_SOURCE_DIR}/service ${CMAKE_CURRENT_SOURCE_DIR}/front ${CMAKE_CURRENT_SOURCE_DIR}/handleAi ${CMAKE_CURRENT_SOURCE_DIR}/additionalFunc @@ -70,6 +70,7 @@ add_library(${CURRENT_FOLDER} SHARED ${WIN_RC_DIR} ${RESOURCES_DIR} ${PROJECT_ROOT_PATH}/include/qtBridge.h + ${PROJECT_ROOT_PATH}/include/interface.h ${CORE_HEADER_FILES} ) diff --git a/src/Core/back/imgSaveThreadManager.h b/src/Core/back/imgSaveThreadManager.h deleted file mode 100644 index dc77f71..0000000 --- a/src/Core/back/imgSaveThreadManager.h +++ /dev/null @@ -1,282 +0,0 @@ -#include -#include -#include -#include -#include -#include - -enum imgSaveThreadType { - oriImg, - resImg, - segImg, - clsImg, - tiffOriImg -}; -struct saveJob { - saveJob(std::string jobNameIn) :jobName(jobNameIn) {} - std::string jobName; - QQueue> imgQueue; -}; -class imgSaveThread : public QThread { - -public: - enum type { - normal, - priority - }; - QMutex mutex; - std::vector compressionParams{ cv::IMWRITE_PNG_COMPRESSION ,9 }; - QQueue< std::shared_ptr>priorityQueue; - QQueue< std::shared_ptr>defaultQueue; - int imgRemainCount = 0; - void run() { - - while (true) { - QQueue> localQueue; - std::shared_ptr job = nullptr; - // 优先处理 priorityQueue - { - QMutexLocker locker(&mutex); - if (!priorityQueue.isEmpty()) { - job = priorityQueue.dequeue(); - } - else if (!defaultQueue.isEmpty()) { - job = defaultQueue.dequeue(); - } - } - - // 没有任务就跳出 - if (job == nullptr) { - break; - } - - qDebug() << "saveJob start" << QString::fromStdString(job->jobName) << "remain" << imgRemainCount; - localQueue.swap(job->imgQueue); - imgRemainCount -= job->imgQueue.size(); - // 批量保存 - while (!localQueue.isEmpty()) { - auto data = std::move(localQueue.dequeue()); -#if saveImgEnable - cv::imwrite(data.first, data.second); -#endif - } - - qDebug() << "saveJob end " << QString::fromStdString(job->jobName); - } - } - - void addJob(std::shared_ptr job, type type_ = normal) { - - QMutexLocker locker(&mutex); - switch (type_) { - case normal: - defaultQueue.append(job); - break; - case priority: - priorityQueue.append(job); - break; - } - imgRemainCount += job->imgQueue.size(); - qDebug() << "savejob enqueue" << QString::fromStdString(job->jobName) << "queue size" << priorityQueue.size() << "|" << defaultQueue.size() << " imgRemain" << imgRemainCount; - this->start(); - } - - int queueSize() { - QMutexLocker locker(&mutex); - int size = defaultQueue.size(); - return size; - } -}; -class imgSaveThreadManager { -public: - imgSaveThreadManager(std::string pathIn) { - path = QString::fromStdString(pathIn); - } - ~imgSaveThreadManager() {}; - void setPath(QString pathIn) { - path = pathIn; - } - imgSaveThread saveThread; - QString genDirName(std::shared_ptr data, imgSaveThreadType type, saveMode mode) { - // 一级目录(根路径) - const QString basePath = path; - - // 二级目录 (日期与小时) - QDateTime now = QDateTime::currentDateTime(); - QString dateDir = now.toString("yyyy-MM-dd"); - QString hourDir = QString("%1H").arg(now.time().hour()); - - // 三级目录(图片类型) - static const QMap typeMap{ - { oriImg, "原图" }, - { resImg, "结果图" } - }; - QString imgTypeDir = typeMap.value(type, "error"); - - // 检测结果 - QString resultDir; - if (!data->aiDetOK && !data->halconOK) resultDir = "NG"; - else if (!data->aiDetOK) resultDir = "aiNG"; - else if (!data->halconOK) resultDir = "halconNG"; - else resultDir = "OK"; - - // SN 或相机名 - QString detailName = (data->SNcode.size() > 0 && data->SNcode.size() < 50) - ? QString::fromStdString(data->SNcode) - : QString::fromStdString(data->cameraKeyName); - - // 拼接路径 - QDir dir(basePath); - if (data->saveObjectName != "") - dir = QDir(dir.filePath(data->saveObjectName)); - dir = QDir(dir.filePath(dateDir)); - dir = QDir(dir.filePath(imgTypeDir)); - dir = QDir(dir.filePath(hourDir)); - dir = QDir(dir.filePath(resultDir)); - if (mode != oneFile) - dir = QDir(dir.filePath(detailName)); - return dir.path() + "/"; - } - - std::vector genFileNames(std::shared_ptr data, imgSaveThreadType type, std::string format = "") { - - std::vector resultFileNames; - std::string fileName; - if (data->SNcode.length() != 0 && data->SNcode.length() < 50) - fileName += data->SNcode; - else - fileName += data->cameraKeyName; - for (auto str : data->imgSaveNameParameter) - fileName += str + "_"; - switch (type) - { - case oriImg: - for (int i = 0; i < data->oriImgs.size(); i++) - { - if (i < data->oriFormat.size()) - resultFileNames.push_back(fileName + getCurrentDateTime() + "_Count" + std::to_string(i) + "." + data->oriFormat[i]); - else - resultFileNames.push_back(fileName + getCurrentDateTime() + "_Count" + std::to_string(i) + ".bmp"); - } - break; - case resImg: - for (int i = 0; i < data->resultImgs.size(); i++) - resultFileNames.push_back(fileName + getCurrentDateTime() + "_Count" + std::to_string(i) + ".jpg"); - break; - case segImg: - for (int i = 0; i < data->segOriginImgs.size(); i++) - resultFileNames.push_back(fileName + "SEG_" + getCurrentDateTime() + "_" + std::to_string(i) + ".png"); - break; - case clsImg: - for (int i = 0; i < data->clsOriginImgs.size(); i++) - resultFileNames.push_back(fileName + "CLS_RES" + getCurrentDateTime() + "_" + data->clsOriginImgs[i].first + "_Count" + std::to_string(i) + ".bmp"); - break; - } - return resultFileNames; - } - void packageData(QQueue>& jobQueue, std::string imgSavaPath, std::vector fileNames, std::vector& mats) { - if (fileNames.size() != mats.size()) - qWarning() << "检测到存图文件名数量与图片数量不符"; - - for (int index = 0; index < fileNames.size() && index < mats.size(); index++) { - try { - - if (mats[index].data == nullptr) { - qDebug() << "在存图线程中发现无效图像"; - continue; - } - } - catch (...) { - qDebug() << "在存图线程中发现无效图像"; - continue; - } - - cv::Mat img; - mats[index].copyTo(img); - jobQueue.enqueue(std::make_pair(imgSavaPath + fileNames[index], img)); - } - } - void packageData(QQueue>& jobQueue, std::string imgSavaPath, std::vector fileNames, std::vector>& mats) { - if (fileNames.size() != mats.size()) - qWarning() << "检测到存图文件名数量与图片数量不符"; - - for (int index = 0; index < fileNames.size() && index < mats.size(); index++) { - try { - - if (mats[index].second.data == nullptr) { - qDebug() << "在存图线程中发现无效图像"; - continue; - } - } - catch (...) { - qDebug() << "在存图线程中发现无效图像"; - continue; - } - - cv::Mat img; - mats[index].second.copyTo(img); - jobQueue.enqueue(std::make_pair(imgSavaPath + fileNames[index], img)); - } - - } - void saveOri(std::shared_ptr data) { - - if (data->oriImgs.size() != 0) { - - QString fileSavePath = genDirName(data, oriImg, data->_saveMode);//获取文件夹名 - createDirectoryIfNotExists(fileSavePath);//不存在此文件夹则创建 - std::vectorfileNames = genFileNames(data, oriImg);//按规则为文件创建命名 - std::shared_ptr job (new saveJob("[ORI_JOB]" + data->SNcode)); - packageData(job->imgQueue, fileSavePath.toLocal8Bit().toStdString(), fileNames, data->oriImgs);//打包数据 - saveThread.addJob(job); - } - } - void saveRes(std::shared_ptr data) { - - QString fileSavePath = genDirName(data, resImg, data->_saveMode);//获取文件夹名 - createDirectoryIfNotExists(fileSavePath);//不存在此文件夹则创建 - std::shared_ptr job(new saveJob("[RES_JOB]"+ data->SNcode)); - //结果图保存 - if (data->resultImgs.size() != 0) { - std::vectorfileNames = genFileNames(data, resImg);//按规则为文件创建命名 - packageData(job->imgQueue, fileSavePath.toLocal8Bit().toStdString(),fileNames, data->resultImgs);//打包数据 - } - //SEG原图 - if (data->segOriginImgs.size() != 0) { - std::vectorfileNames = genFileNames(data, segImg);//按规则为文件创建命名 - packageData(job->imgQueue, fileSavePath.toLocal8Bit().toStdString(),fileNames, data->segOriginImgs);//打包数据 - } - //CLS原图 - if (data->clsOriginImgs.size() != 0) { - std::vectorfileNames = genFileNames(data, clsImg);//按规则为文件创建命名 - packageData(job->imgQueue, fileSavePath.toLocal8Bit().toStdString(), fileNames, data->clsOriginImgs);//打包数据 - } - saveThread.addJob(job, imgSaveThread::priority); - //额外储存NG文件夹 - //std::string additionalSaveNgImgPath = genDirName(data, additionalSaveNgImg); - //std::vectoradditionalSaveNgImgNames = genFileNames(data, additionalSaveNgImg); - //if (additionalSaveNgImgNames.size() > 0) - // createDirectoryIfNotExists(additionalSaveNgImgPath);//不存在此文件夹则创建 - //int additionalSaveNgImgNamesIndex = 0; - //for (int index = 0; index < data->segOriginImgs.size()&& additionalSaveNgImgNames.size() > additionalSaveNgImgNamesIndex; index++) - //{ - // saveThread.addData(additionalSaveNgImgPath + additionalSaveNgImgNames[additionalSaveNgImgNamesIndex], data->segOriginImgs[index]); - // additionalSaveNgImgNamesIndex++; - //} - //for (int index = 0; index < data->oriImgs.size() && additionalSaveNgImgNames.size() > additionalSaveNgImgNamesIndex; index++) - //{ - // saveThread.addData(additionalSaveNgImgPath + additionalSaveNgImgNames[additionalSaveNgImgNamesIndex], data->oriImgs[index]); - // additionalSaveNgImgNamesIndex++; - //} - //for (int index = 0; index < data->resultImgs.size() && additionalSaveNgImgNames.size() > additionalSaveNgImgNamesIndex; index++) - //{ - // saveThread.addData(additionalSaveNgImgPath + additionalSaveNgImgNames[additionalSaveNgImgNamesIndex], data->resultImgs[index]); - // additionalSaveNgImgNamesIndex++; - //} - } -private: - QString path = ""; - -}; - -#pragma once \ No newline at end of file diff --git a/src/Core/backendInterface.cpp b/src/Core/backendInterface.cpp index 11e6312..cd1bd61 100644 --- a/src/Core/backendInterface.cpp +++ b/src/Core/backendInterface.cpp @@ -1,47 +1,121 @@ -#include "interface.h" -#include -#include -#include -#include +#include -class Backend : public IBackend { -public: - //appCore _core; - void test() override { std::cout << "Hello from Backend!" << std::endl; } - void init(SQLiteDB* db,qtFuncBridge* bridgeIn, nlohmann::ordered_json& json) override { - if (initOnce == false) { - cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_ERROR); - _detProcess = new detProcess(db,json, bridgeIn); - } - _detProcess->init(); +Backend::~Backend() { + if (_paramSyncManager != nullptr) { + delete _paramSyncManager; + } + safeShutdownDelete(_serviceManager._triggerManager); + safeShutdownDelete(_serviceManager._cameraManager); +} +void Backend::init(SQLiteDB* db, qtFuncBridge* bridgeIn, nlohmann::ordered_json& json) { + if (initOnce == false) { + emit bridgeIn->loadingWindow_appendInitLog(u8"核心加载成功,正在初始化", QColor("white")); + qDebug() << "核心加载成功,正在初始化"; + + sqlDatabase = db; bridge = bridgeIn; - emit bridge->loadingWindow_appendInitLog(u8"核心加载成功,正在初始化",QColor("white")); - _detProcess->start(); + missionJson = json; + _paramSyncManager = new paramSyncManager(missionJson["missionSettings"]); + cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_ERROR); + _csvWriter = new CsvWriter(sqlDatabase); + _serviceManager._detDataManager = new detDataManager(); + _missionResultHandle = new missionResultHandle(bridge); + _serviceManager._missionResultHandle = _missionResultHandle; + _serviceManager.dataBase = sqlDatabase; + _serviceManager._csvWriter = _csvWriter; + _serviceManager._paramSyncManager = _paramSyncManager; + //CLI 只在首次初始化创建 + connect(bridge, &qtFuncBridge::restartInit, this, &Backend::restartInit); + _cliManager = new CLIManager(&_serviceManager, bridge); + initOnce = true; } - void cleanup() override { - if (_detProcess != nullptr){ - delete _detProcess; - _detProcess = nullptr; + // 创建/重建需要的服务模块,避免重复创建 + if (!_serviceManager._imgSaveThreadManager) { + _serviceManager._imgSaveThreadManager = new imgSaveThreadManager(missionJson["imgSaveBasePath"]); + } + if (!_serviceManager._cameraManager) { + _serviceManager._cameraManager = new cameraManager(missionJson, bridge); + } + if (!_serviceManager._triggerManager) { + _serviceManager._triggerManager = new triggerManager(&_serviceManager, bridge, missionJson["Triggers"]); + } + this->start(); + +} +void Backend::run() { + if(cameraInitResult == false) + _serviceManager._cameraManager->start(); + if (triggerInitResult == false) + _serviceManager._triggerManager->start(); + if (cameraInitResult == false) + cameraInitResult = waitrForInitResult(_serviceManager._cameraManager); + if (triggerInitResult == false) + triggerInitResult = waitrForInitResult(_serviceManager._triggerManager); + + if (cameraInitResult == true && triggerInitResult == true) { + emit bridge->loadingWindow_appendInitLog("初始化Mission中...", "yellow"); + bool res = _missionLoader.loadMission(missionJson["missionDllPath"]); + if (res == false) { + emit bridge->loadingWindow_appendInitLog("初始化Mission失败!请检查路径是否正确.", "red"); } + _missionLoader.getmission(missionJson["missionDllPath"])->initBasic( + &_serviceManager, + missionJson["missionSettings"], bridge + ); + _cliManager->initBasicPreset(_missionLoader.getmission(missionJson["missionDllPath"])); + _serviceManager._detDataManager->setTriggerManager(_serviceManager._triggerManager); + initJsonRpc(); + emit bridge->initResult(InitOK); } - ~Backend() { - if (_detProcess != nullptr) { - delete _detProcess; - _detProcess = nullptr; - } + else { + emit bridge->initResult(InitFailure); } - bool excuteCliCommand(const QString& command) { - return _detProcess->_cliManager->cliCommand(command); +} +void Backend::restartInit() { + // 停止并删除线程型模块 + //safeShutdownDelete(_serviceManager._cameraManager); + //safeShutdownDelete(_serviceManager._triggerManager); + //if (_serviceManager._handleAiModelManager) { + // // try invoking stop if exists + // QMetaObject::invokeMethod(_serviceManager._handleAiModelManager, "stop", Qt::DirectConnection); + // safeDelete(_serviceManager._handleAiModelManager); + //} + if (!this->isRunning()) { + this->start(); } - bool excuteCliCommandPreset(const QString& uuid) { - return _detProcess->_cliManager->excutePreset(uuid); + else { + qDebug() << "detProcess already running, init refreshed. run() should handle reloading if needed."; } -private: - bool initOnce = false; - detProcess* _detProcess = nullptr; - qtFuncBridge* bridge = nullptr; -}; +} +void Backend::initJsonRpc() { + _rpcServer.registerMethod(QStringLiteral("reloadConfig"), [this](const nlohmann::json&) -> nlohmann::json { + _cliManager->cliCommand("reloadConfig"); + std::cout << "接收到远程命令->重新加载配置文件" << std::endl; + return nlohmann::json{ + {"result", "OK"} + }; + }); + // 检查服务器启动结果 + _rpcServer.start(2367); +} + + +bool Backend::waitrForInitResult(serviceModule* ptr, int overtimeS) { + int overTimeMs = overtimeS * 1000; + int timecnt = 0; + while (timecnt <= overTimeMs) { + if (ptr->status != NotInit) + break; + QThread::msleep(100); + timecnt += 100; + } + if (timecnt > overTimeMs) + return false; + if (ptr->status == InitFailure) + return false; + return true; +} extern "C" { IBackend* CreateBackend() { return new Backend(); } diff --git a/src/Core/detProcess.hpp b/src/Core/detProcess.hpp deleted file mode 100644 index 1b1cffc..0000000 --- a/src/Core/detProcess.hpp +++ /dev/null @@ -1,157 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -class detProcess : public QThread -{ -public: - CsvWriter* _csvWriter = nullptr; - missionResultHandle* _missionResultHandle = nullptr; - ServiceManager _serviceManager; - CLIManager* _cliManager; // 命令行接口管理器 - rpc::JsonRpcServer _rpcServer; - bool initOnce = false; - detProcess(SQLiteDB* dbIn, nlohmann::ordered_json& jsonIn, qtFuncBridge* bridgeIn): - db(dbIn),bridge(bridgeIn), missionJson(jsonIn){ - } - ~detProcess() { - safeShutdownDelete(_serviceManager._triggerManager); - safeShutdownDelete(_serviceManager._cameraManager); - safeDelete(_serviceManager._handleAiModelManager); - } - // 对普通指针的安全删除 - template - void safeDelete(T*& ptr) { - if (ptr) { delete ptr; ptr = nullptr; } - } - - // 尝试对基于 QThread 的 serviceModule 进行关停,然后删除 - void safeShutdownDelete(serviceModule* ptr) { - if (!ptr) return; - // 如果对象正在运行,尝试停止 - if (ptr->isRunning()) { - // 如果存在 stop 槽,则尝试调用 - QMetaObject::invokeMethod(ptr, "stop", Qt::DirectConnection); - // 作为后备,尝试退出事件循环 - QMetaObject::invokeMethod(ptr, "quit", Qt::DirectConnection); - // 等待其退出,给予一定时间 - if (!ptr->wait(2000)) { - // 仍在运行时,作为最后手段强制终止 - ptr->terminate(); - ptr->wait(1000); - } - } - delete ptr; ptr = nullptr; - } - - void init() { - // 创建/重建需要的服务模块,避免重复创建 - if (!_serviceManager._imgSaveThreadManager) { - _serviceManager._imgSaveThreadManager = new imgSaveThreadManager(missionJson["imgSaveBasePath"]); - } - if (!_serviceManager._cameraManager) { - _serviceManager._cameraManager = new cameraManager(missionJson, bridge); - } - if (!_serviceManager._triggerManager) { - _serviceManager._triggerManager = new triggerManager(this, bridge, missionJson["Triggers"]); - } - if (initOnce == false) { - initOnce = true; - _csvWriter = new CsvWriter(db); - _serviceManager._detDataManager = new detDataManager(); - _missionResultHandle = new missionResultHandle(bridge); - _serviceManager._missionResultHandle = _missionResultHandle; - _serviceManager.dataBase = db; - _serviceManager._csvWriter = _csvWriter; - // handleAiModelManager 与 CLI 等只在首次初始化创建 - if (!_serviceManager._handleAiModelManager) { - _serviceManager._handleAiModelManager = new handleAiModelManager(missionJson, bridge); - } - connect(bridge, &qtFuncBridge::reInitConfig, this, [=]() {reinitConfig(); }); - _cliManager = new CLIManager(&_serviceManager, bridge); - } - } - - bool waitrForInitResult(serviceModule* ptr, int overtimeS = 999999) { - int overTimeMs = overtimeS * 1000; - int timecnt = 0; - while (timecnt <= overTimeMs) { - if (ptr->status != NotInit) - break; - QThread::msleep(100); - timecnt += 100; - } - if (timecnt > overTimeMs) - return false; - if (ptr->status == InitFailure) - return false; - return true; - } - - // 重新初始化配置 - void reinitConfig() { - // 停止并删除线程型模块 - safeShutdownDelete(_serviceManager._cameraManager); - safeShutdownDelete(_serviceManager._triggerManager); - if (_serviceManager._handleAiModelManager) { - // try invoking stop if exists - QMetaObject::invokeMethod(_serviceManager._handleAiModelManager, "stop", Qt::DirectConnection); - safeDelete(_serviceManager._handleAiModelManager); - } - init(); - if (!this->isRunning()) { - this->start(); - } else { - qDebug() << "detProcess already running, init refreshed. run() should handle reloading if needed."; - } - } - // 初始化并运行主流程 - void run() { -#if 1 - _serviceManager._cameraManager->start(); - _serviceManager._handleAiModelManager->start(); - _serviceManager._triggerManager->start(); - bool res1 = waitrForInitResult(_serviceManager._cameraManager); - bool res2 = waitrForInitResult(_serviceManager._handleAiModelManager); - bool res3 = waitrForInitResult(_serviceManager._triggerManager); - - if (res1 == true && res2 == true && res3 == true) { - emit bridge->loadingWindow_appendInitLog("初始化Mission中...", "yellow"); - _missionLoader.loadMission(missionJson["missionDllPath"]); - _missionLoader.getmission(missionJson["missionDllPath"])->initBasic( - &_serviceManager, - missionJson["missionSettings"], bridge - ); - _cliManager->initBasicPreset(_missionLoader.getmission(missionJson["missionDllPath"])); - _serviceManager._detDataManager->setTriggerManager(_serviceManager._triggerManager); - initJsonRpc(); - emit bridge->initResult(InitOK); - } - else { - emit bridge->initResult(InitFailure); - } -#endif - } - void initJsonRpc() { - _rpcServer.registerMethod(QStringLiteral("reloadConfig"), [this](const nlohmann::json&) -> nlohmann::json { - _cliManager->cliCommand("reloadConfig"); - std::cout << "接收到远程命令->重新加载配置文件" << std::endl; - return nlohmann::json{ - {"result", "OK"} - }; - }); - // 检查服务器启动结果 - _rpcServer.start(2367); - } -private: - nlohmann::ordered_json missionJson; - SQLiteDB* db; - qtFuncBridge* bridge; - MissionLoader _missionLoader; // 任务加载器,用于加载任务配置 - -}; - -#pragma once diff --git a/src/core/additionalFunc/manualDetThread.cpp b/src/core/additionalFunc/manualDetThread.cpp index 2741f26..2cadc8a 100644 --- a/src/core/additionalFunc/manualDetThread.cpp +++ b/src/core/additionalFunc/manualDetThread.cpp @@ -1,5 +1,5 @@ #include -#include +#include void manualDetThread::initialization(detProcess* _appCore) { diff --git a/src/core/backendInterface.h b/src/core/backendInterface.h new file mode 100644 index 0000000..c49783d --- /dev/null +++ b/src/core/backendInterface.h @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include +#include +#include "interface.h" +#include +#include +#include +#include + +class Backend : public IBackend { +public: + CsvWriter* _csvWriter = nullptr; + missionResultHandle* _missionResultHandle = nullptr; + ServiceManager _serviceManager; + paramSyncManager* _paramSyncManager = nullptr; + CLIManager* _cliManager; // 命令行接口管理器 + rpc::JsonRpcServer _rpcServer; + + ~Backend(); + //重新初始化时禁止调用此函数 + void init(SQLiteDB* db, qtFuncBridge* bridgeIn, nlohmann::ordered_json& json) override; + // 重新初始化 + void restartInit(); + void run(); + + void cleanup() override { + + } + // 对普通指针的安全删除 + template + void safeDelete(T*& ptr) { + if (ptr) { delete ptr; ptr = nullptr; } + } + + // 尝试对基于 QThread 的 serviceModule 进行关停,然后删除 + void safeShutdownDelete(serviceModule* ptr) { + if (!ptr) return; + // 如果对象正在运行,尝试停止 + if (ptr->isRunning()) { + // 如果存在 stop 槽,则尝试调用 + QMetaObject::invokeMethod(ptr, "stop", Qt::DirectConnection); + // 作为后备,尝试退出事件循环 + QMetaObject::invokeMethod(ptr, "quit", Qt::DirectConnection); + // 等待其退出,给予一定时间 + if (!ptr->wait(2000)) { + // 仍在运行时,作为最后手段强制终止 + ptr->terminate(); + ptr->wait(1000); + } + } + delete ptr; ptr = nullptr; + } + + bool waitrForInitResult(serviceModule* ptr, int overtimeS = 999999); + + // 初始化RPC服务器 + void initJsonRpc(); + nlohmann::ordered_json getJson() { + missionJson["missionSettings"] = _paramSyncManager->toJson(); + return missionJson; + } + void editParam(QString name, QVariant value) { + _paramSyncManager->edit(name, value); + } + void getAllParams(std::vector>* params) { + auto allParams = _paramSyncManager->getAllParams(); + params->swap(allParams); + } +public slots: + void triggerAndCaptureImage(std::string cameraName, std::vector* mats) { + if (cameraInitResult == false) { + qCritical() << "[Error]调用triggerAndCaptureImage时相机未初始化!"; + return; + } + std::shared_ptr dataPtr = std::make_shared(); + auto camera = _serviceManager._cameraManager->getCamera(cameraName); + if (camera == nullptr) { + qCritical() << "[Error]调用triggerAndCaptureImage时未找到相机:" + << QString::fromStdString(cameraName); + return; + } + camera->captureImage(dataPtr); + int cnt = 0; + while (dataPtr->oriImgs.size() == 0 && cnt < 20) { + QThread::msleep(10); + cnt++; + } + if (dataPtr->oriImgs.size() == 0) { + cv::Mat mat(1000, 1000, CV_8UC1, 255); + cv::putText(mat, "Capture Image Timeout", cv::Point(50, 500), + cv::FONT_HERSHEY_SIMPLEX, 2, cv::Scalar(0, 0, 255), 3); + mats->push_back(mat); + return; + } + mats->swap(dataPtr->oriImgs); + return; + } + bool excuteCliCommand(const QString& command) { + return _cliManager->cliCommand(command); + } + bool excuteCliCommandPreset(const QString& uuid) { + return _cliManager->excutePreset(uuid); + } + void getCameraNames(std::vector* cameraNames) { + for (auto camera : _serviceManager._cameraManager->cameras) { + cameraNames->push_back(camera.first); + } + return; + } +private: + bool initOnce = false; + nlohmann::ordered_json missionJson; + SQLiteDB* sqlDatabase; + qtFuncBridge* bridge; + MissionLoader _missionLoader; // 任务加载器,用于加载任务配置 + bool cameraInitResult = false, + triggerInitResult = false; + +}; +#pragma once diff --git a/src/core/back/CLIManager.h b/src/core/service/CLIManager.h similarity index 100% rename from src/core/back/CLIManager.h rename to src/core/service/CLIManager.h diff --git a/src/Core/back/backStruct.h b/src/core/service/backStruct.h similarity index 100% rename from src/Core/back/backStruct.h rename to src/core/service/backStruct.h diff --git a/src/Core/back/cameraManager.cpp b/src/core/service/cameraManager.cpp similarity index 98% rename from src/Core/back/cameraManager.cpp rename to src/core/service/cameraManager.cpp index 19ac289..6ad62dd 100644 --- a/src/Core/back/cameraManager.cpp +++ b/src/core/service/cameraManager.cpp @@ -24,7 +24,7 @@ static MV_CC_DEVICE_INFO_LIST searchMvDevice() { MV_CC_DEVICE_INFO_LIST m_stDevList; memset(&m_stDevList, 0, sizeof(MV_CC_DEVICE_INFO_LIST)); - int nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE || MV_USB_DEVICE, &m_stDevList); + int nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &m_stDevList); return m_stDevList; } diff --git a/src/Core/back/cameraManager.h b/src/core/service/cameraManager.h similarity index 94% rename from src/Core/back/cameraManager.h rename to src/core/service/cameraManager.h index bf88870..60223bf 100644 --- a/src/Core/back/cameraManager.h +++ b/src/core/service/cameraManager.h @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -180,6 +180,16 @@ public slots: camera->second->trigger(); } } + cv::Mat triggerAndGetImage(std::string cameraName) { + auto camera = cameras.find(cameraName); + std::shared_ptr data(new detData());//创建数据包 + data->imgAmountMin = 1; + camera->second->captureImage(data); + while (data->oriImgs.size() == 0) { + QThread::msleep(10); + } + return data->oriImgs[0]; + } }; #pragma once diff --git a/src/core/back/detCallbackChain.hpp b/src/core/service/detCallbackChain.hpp similarity index 95% rename from src/core/back/detCallbackChain.hpp rename to src/core/service/detCallbackChain.hpp index d06deda..a8c0097 100644 --- a/src/core/back/detCallbackChain.hpp +++ b/src/core/service/detCallbackChain.hpp @@ -35,7 +35,7 @@ public: void process(std::shared_ptr data, addressTemplate address, QByteArray completeSignal = "") { data->service = _serviceManager; while (data->oriImgs.size() < data->imgAmountMin) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + QThread::msleep(10); } if(completeSignal != "") _triggerManager->writeShort(completeSignal); @@ -99,8 +99,8 @@ public: data->sqlVec.push_back({ "SN", data->SNcode }); data->sqlVec.push_back({ "totalCt", std::to_string(data->algoTime) + "ms" }); // 异步保存检测图像 - std::thread([this,data] {_imgSaveThreadManager->saveOri(data); }).detach(); - std::thread([this,data] {_imgSaveThreadManager->saveRes(data); }).detach(); + std::thread([this,data] {_imgSaveThreadManager->saveImage(data); }).detach(); + //std::thread([this,data] {_imgSaveThreadManager->saveRes(data); }).detach(); } #pragma region 注册回调函数 diff --git a/src/Core/back/detData.h b/src/core/service/detData.h similarity index 84% rename from src/Core/back/detData.h rename to src/core/service/detData.h index 6e314f3..8736fba 100644 --- a/src/Core/back/detData.h +++ b/src/core/service/detData.h @@ -19,21 +19,12 @@ class detDataManager; class ServiceManager; using algoEvaluateCallback = std::function,handleAiModelManager*)>; class detData{ -public: - - +public: std::chrono::steady_clock::time_point startTime = std::chrono::high_resolution_clock::now(); std::chrono::steady_clock::time_point getImageTime; long long algoTime = 0;//算法执行时间容器 //参数 - - //结果 - bool aiDetOK = true; - bool halconOK = true; - //结果图展示预设 - std::vector< showImgPack>resultImgShowSet; - //需要存入的SQL数据 - std::vector> sqlVec; + #pragma region 基础函数 static QString getUuid() { return QUuid::createUuid().toString(); @@ -48,8 +39,7 @@ public: ServiceManager* service;//服务类 包含plc交互模块,图标交互模块等 #pragma endregion #pragma region 存图相关 - saveMode _saveMode = saveMode::basic; - QString saveObjectName = "";//存图项目名 用于与其他检测项目区分开 + QString saveObjectName; QMutex resultMutex;//结果图锁 std::vector resultImgs;//结果图容器 std::vector segOriginImgs;//seg检测原图容器 @@ -58,14 +48,21 @@ public: QMutex oriImgMutex;//原图锁 std::vector oriImgs;//原图容器 std::vector oriFormat;//原图格式 如果数量不一致直接全存bmp - std::vector imgSaveNameParameter;//命名规则 str1_str2... 第一个参数已默认为相机名,在构造函数 - void customSaveParam(QString saveObjectNameIN, saveMode _saveModeIN, std::vector imgSaveNameParameterIN) { - saveObjectName = saveObjectNameIN; - _saveMode = _saveModeIN; - imgSaveNameParameter.insert(imgSaveNameParameter.end(),imgSaveNameParameterIN.begin(), imgSaveNameParameterIN.end()); - } + + //PATH_SAMPLE : "{imgSaveRootDir}/{yyyy-MM-dd}/{HH}H/{imageType}/{processResult}/{SN}/" + //NAME_SAMPLE {SN}_{cameraName}_{index}} + QString customPath = "",customFileName = ""; + std::vector>customImageSaveTemplateParams; #pragma endregion - QMap additional; +#pragma region 结果相关 + bool aiDetOK = true; + bool halconOK = true; + //结果图展示预设 + std::vector< showImgPack>resultImgShowSet; + //需要存入的SQL数据 + std::vector> sqlVec; +#pragma endregion + QMap additional;//自定义参数 detData() {} ~detData() { for (auto& img : oriImgs) @@ -87,7 +84,6 @@ public: QMutexLocker locker(&dataMutex); std::shared_ptr dataPtr(new detData()); dataPtr->cameraKeyName = IN_cameraKeyName.toStdString(); - dataPtr->imgSaveNameParameter.push_back(IN_cameraKeyName.toStdString()); dataPtr->oriFormat = oriFormat; dataPtr->SNcode = IN_SN; dataPtr->imgAmountMin = imgAmountMin; diff --git a/src/core/service/imgSaveThreadManager.h b/src/core/service/imgSaveThreadManager.h new file mode 100644 index 0000000..e0fe693 --- /dev/null +++ b/src/core/service/imgSaveThreadManager.h @@ -0,0 +1,320 @@ +#include +#include +#include +#include +#include +#include +enum imgSaveThreadType { + oriImg, + resImg, + segImg, + clsImg +}; +class ImageSavePathFormatter { +public: + QString rootDir = ""; + std::string genPath(std::shared_ptr data, imgSaveThreadType type, QString customPath = "", std::vector>customParams = std::vector>()){ + QString pathTemplate = !customPath.isEmpty() ? customPath : defaultPath; + QString result = pathTemplate; + + // 替换日期时间占位符 + QDateTime now = QDateTime::currentDateTime(); + result.replace("{yyyy-MM-dd}", now.toString("yyyy-MM-dd")); + result.replace("{HH}", QString("%1").arg(now.time().hour(), 2, 10, QChar('0'))); + result.replace("{mm}", QString("%1").arg(now.time().minute(), 2, 10, QChar('0'))); + result.replace("{ss}", QString("%1").arg(now.time().second(), 2, 10, QChar('0'))); + + // 替换图片类型占位符 + static const QMap typeMap{ + { oriImg, "原图" }, + { resImg, "结果图" }, + { segImg, "结果图" }, + { clsImg, "结果图" } + }; + result.replace("{imageType}", typeMap.value(type, "Unknown")); + + // 替换检测结果占位符 + QString processResult; + if (!data->aiDetOK && !data->halconOK) processResult = "NG"; + else if (!data->aiDetOK) processResult = "aiNG"; + else if (!data->halconOK) processResult = "halconNG"; + else processResult = "OK"; + result.replace("{processResult}", processResult); + + // 替换SN/设备信息占位符 + QString snCode = (data->SNcode.size() > 0 && data->SNcode.size() < 50) + ? QString::fromStdString(data->SNcode) + : QString::fromStdString(data->cameraKeyName); + result.replace("{SN}", snCode); + result.replace("{cameraName}", QString::fromStdString(data->cameraKeyName)); + + // 替换根目录占位符 + result.replace("{imgSaveRootDir}", rootDir.isEmpty() ? "." : rootDir); + + // 替换根目录占位符 + result.replace("{saveObjectName}", data->saveObjectName.isEmpty() ? "" : data->saveObjectName); + + // 确保路径以/结尾 + if (!result.endsWith("/") && !result.endsWith("\\")) { + result += "/"; + } + for (auto& pair : customParams) { + result.replace(pair.first, pair.second); + } + return result.toLocal8Bit().toStdString(); + } + std::string genFileName(std::shared_ptr data, imgSaveThreadType type, int index = 0, QString customFileName = "", std::vector> customParams = std::vector>(), const std::string& format = "bmp") { + QString fileTemplate = !customFileName.isEmpty() ? customFileName : defaultFileName; + QString result = fileTemplate; + + // 替换SN/设备信息占位符 + QString snCode = (data->SNcode.size() > 0 && data->SNcode.size() < 50) + ? QString::fromStdString(data->SNcode) + : QString::fromStdString(data->cameraKeyName); + result.replace("{SN}", snCode); + result.replace("{cameraName}", QString::fromStdString(data->cameraKeyName)); + + // 替换索引占位符 + result.replace("{index}", QString::number(index)); + + // 替换时间戳占位符 + QDateTime now = QDateTime::currentDateTime(); + result.replace("{yyyy-MM-dd}", now.toString("yyyy-MM-dd")); + result.replace("{HH-mm-ss}", now.toString("HH-mm-ss")); + result.replace("{timestamp}", QString::number(now.toMSecsSinceEpoch())); + + // 替换图片类型占位符 + static const QMap typeMap{ + { oriImg, "ORI" }, + { resImg, "RES" }, + { segImg, "SEG" }, + { clsImg, "CLS" } + }; + result.replace("{imageType}", typeMap.value(type, "UNKNOWN")); + for (auto& pair : customParams) { + result.replace(pair.first, pair.second); + } + + return result.toLocal8Bit().toStdString() + "." + format; + } + std::vector genFileNames(std::shared_ptr data, imgSaveThreadType type, std::vector& imgs) { + + std::vector result; + switch (type) { + case oriImg: + for (int i = 0; i < imgs.size(); i++) { + if (i < data->oriFormat.size()) + result.push_back(genFileName(data, type, i, data->customFileName, data->customImageSaveTemplateParams, data->oriFormat[i])); + else + result.push_back(genFileName(data, type, i, data->customFileName, data->customImageSaveTemplateParams)); + } + break; + + case resImg: + for (int i = 0; i < imgs.size(); i++) { + result.push_back(genFileName(data, type, i, data->customFileName, data->customImageSaveTemplateParams, "jpg")); + } + break; + + default: + for (int i = 0; i < imgs.size(); i++) { + result.push_back(genFileName(data, type, i, data->customFileName, data->customImageSaveTemplateParams)); + } + } + return result; + } + std::vector genClsFileNames(std::shared_ptr data, imgSaveThreadType type, std::vector>& imgs, QString customFileName = "") { + std::vector result; + for (int i = 0; i < imgs.size(); i++) { + result.push_back(genFileName(data, type, i, defaultFileName, data->customImageSaveTemplateParams)); + } + return result; + } +private: + QString defaultPath = "{imgSaveRootDir}/{yyyy-MM-dd}/{saveObjectName}/{HH}H/{imageType}/{processResult}/{SN}/"; + QString defaultFileName = "{SN}_{cameraName}_{index}"; +}; +struct saveJob { + saveJob(std::string jobNameIn) :jobName(jobNameIn) {} + std::string jobName; + QQueue> imgQueue; +}; +class imgSaveThread : public QThread { + +public: + enum type { + normal, + priority + }; + QMutex mutex; + std::vector compressionParams{ cv::IMWRITE_PNG_COMPRESSION ,9 }; + QQueue< std::shared_ptr>priorityQueue; + QQueue< std::shared_ptr>defaultQueue; + int imgRemainCount = 0; + void run() { + + while (true) { + QQueue> localQueue; + std::shared_ptr job = nullptr; + // 优先处理 priorityQueue + { + QMutexLocker locker(&mutex); + if (!priorityQueue.isEmpty()) { + job = priorityQueue.dequeue(); + } + else if (!defaultQueue.isEmpty()) { + job = defaultQueue.dequeue(); + } + } + + // 没有任务就跳出 + if (job == nullptr) { + break; + } + + qDebug() << "saveJob start" << QString::fromStdString(job->jobName) << "remain" << imgRemainCount; + imgRemainCount -= job->imgQueue.size(); + localQueue.swap(job->imgQueue); + // 批量保存 + while (!localQueue.isEmpty()) { + auto data = std::move(localQueue.dequeue()); +#if saveImgEnable + cv::imwrite(data.first, data.second); +#endif + } + + qDebug() << "saveJob end " << QString::fromStdString(job->jobName); + } + } + + void addJob(std::shared_ptr job, type type_ = normal) { + + QMutexLocker locker(&mutex); + switch (type_) { + case normal: + defaultQueue.append(job); + break; + case priority: + priorityQueue.append(job); + break; + } + imgRemainCount += job->imgQueue.size(); + qDebug() << "savejob enqueue" << QString::fromStdString(job->jobName) << "queue size" << priorityQueue.size() << "|" << defaultQueue.size() << " imgRemain" << imgRemainCount; + this->start(); + } + + int queueSize() { + QMutexLocker locker(&mutex); + int size = defaultQueue.size(); + return size; + } +}; +class imgSaveThreadManager { +public: + imgSaveThreadManager(std::string pathIn) { + _ImageSavePathFormatter.rootDir = QString::fromStdString(pathIn); + } + ~imgSaveThreadManager() {}; + imgSaveThread saveThread; + void saveImage(std::shared_ptr data) { + std::string oriSavePath = _ImageSavePathFormatter.genPath(data, oriImg, data->customPath, data->customImageSaveTemplateParams);//获取文件夹名 + createDirectoryIfNotExists(oriSavePath);//不存在此文件夹则创建 + if (data->oriImgs.size() != 0) { + + std::vectorfileNames = _ImageSavePathFormatter.genFileNames(data, oriImg, data->oriImgs);//按规则为文件创建命名 + std::shared_ptr job (new saveJob("[ORI_JOB]" + data->SNcode)); + packageData(job->imgQueue, oriSavePath, fileNames, data->oriImgs);//打包数据 + saveThread.addJob(job); + } + + std::string resSavePath = _ImageSavePathFormatter.genPath(data, resImg, data->customPath, data->customImageSaveTemplateParams);//获取文件夹名 + createDirectoryIfNotExists(resSavePath);//不存在此文件夹则创建 + std::shared_ptr job(new saveJob("[RES_JOB]"+ data->SNcode)); + //结果图保存 + if (data->resultImgs.size() != 0) { + std::vectorfileNames = _ImageSavePathFormatter.genFileNames(data, resImg, data->resultImgs);//按规则为文件创建命名 + packageData(job->imgQueue, resSavePath,fileNames, data->resultImgs);//打包数据 + } + //SEG原图 + if (data->segOriginImgs.size() != 0) { + std::vectorfileNames = _ImageSavePathFormatter.genFileNames(data, segImg, data->segOriginImgs);//按规则为文件创建命名 + packageData(job->imgQueue, oriSavePath, fileNames, data->segOriginImgs);//打包数据 + } + //CLS原图 + if (data->clsOriginImgs.size() != 0) { + std::vectorfileNames = _ImageSavePathFormatter.genClsFileNames(data, clsImg, data->clsOriginImgs);//按规则为文件创建命名 + packageData(job->imgQueue, oriSavePath, fileNames, data->clsOriginImgs);//打包数据 + } + saveThread.addJob(job, imgSaveThread::priority); + //额外储存NG文件夹 + //std::string additionalSaveNgImgPath = genDirName(data, additionalSaveNgImg); + //std::vectoradditionalSaveNgImgNames = genFileNames(data, additionalSaveNgImg); + //if (additionalSaveNgImgNames.size() > 0) + // createDirectoryIfNotExists(additionalSaveNgImgPath);//不存在此文件夹则创建 + //int additionalSaveNgImgNamesIndex = 0; + //for (int index = 0; index < data->segOriginImgs.size()&& additionalSaveNgImgNames.size() > additionalSaveNgImgNamesIndex; index++) + //{ + // saveThread.addData(additionalSaveNgImgPath + additionalSaveNgImgNames[additionalSaveNgImgNamesIndex], data->segOriginImgs[index]); + // additionalSaveNgImgNamesIndex++; + //} + //for (int index = 0; index < data->oriImgs.size() && additionalSaveNgImgNames.size() > additionalSaveNgImgNamesIndex; index++) + //{ + // saveThread.addData(additionalSaveNgImgPath + additionalSaveNgImgNames[additionalSaveNgImgNamesIndex], data->oriImgs[index]); + // additionalSaveNgImgNamesIndex++; + //} + //for (int index = 0; index < data->resultImgs.size() && additionalSaveNgImgNames.size() > additionalSaveNgImgNamesIndex; index++) + //{ + // saveThread.addData(additionalSaveNgImgPath + additionalSaveNgImgNames[additionalSaveNgImgNamesIndex], data->resultImgs[index]); + // additionalSaveNgImgNamesIndex++; + //} + } +private: + ImageSavePathFormatter _ImageSavePathFormatter; + void packageData(QQueue>& jobQueue, std::string imgSavaPath, std::vector fileNames, std::vector& mats) { + if (fileNames.size() != mats.size()) + qWarning() << "检测到存图文件名数量与图片数量不符"; + + for (int index = 0; index < fileNames.size() && index < mats.size(); index++) { + try { + + if (mats[index].data == nullptr) { + qDebug() << "在存图线程中发现无效图像"; + continue; + } + } + catch (...) { + qDebug() << "在存图线程中发现无效图像"; + continue; + } + + cv::Mat img; + mats[index].copyTo(img); + jobQueue.enqueue(std::make_pair(imgSavaPath + fileNames[index], img)); + } + } + void packageData(QQueue>& jobQueue, std::string imgSavaPath, std::vector fileNames, std::vector>& mats) { + if (fileNames.size() != mats.size()) + qWarning() << "检测到存图文件名数量与图片数量不符"; + + for (int index = 0; index < fileNames.size() && index < mats.size(); index++) { + try { + + if (mats[index].second.data == nullptr) { + qDebug() << "在存图线程中发现无效图像"; + continue; + } + } + catch (...) { + qDebug() << "在存图线程中发现无效图像"; + continue; + } + + cv::Mat img; + mats[index].second.copyTo(img); + jobQueue.enqueue(std::make_pair(imgSavaPath + fileNames[index], img)); + } + + } +}; + +#pragma once \ No newline at end of file diff --git a/src/core/back/missionBase.hpp b/src/core/service/missionBase.hpp similarity index 100% rename from src/core/back/missionBase.hpp rename to src/core/service/missionBase.hpp diff --git a/src/core/back/missionLoader.h b/src/core/service/missionLoader.h similarity index 86% rename from src/core/back/missionLoader.h rename to src/core/service/missionLoader.h index 256996b..0ee58e5 100644 --- a/src/core/back/missionLoader.h +++ b/src/core/service/missionLoader.h @@ -7,7 +7,7 @@ class MissionLoader { bool loadMission(const std::string& missionPath) { hDlls[missionPath] = LoadLibrary(missionPath.c_str()); if (!hDlls[missionPath]) { - std::cerr << "Failed to load DLL!" << std::endl; + std::cerr << "Failed to load Misson DLL!" << std::endl; return false; } createMissionFunc createMission = (createMissionFunc)GetProcAddress(hDlls[missionPath], "createMission"); @@ -17,7 +17,7 @@ class MissionLoader { missionMap[missionPath] = createMission(); } else { - std::cerr << "Failed to get function pointers!" << std::endl; + std::cerr << "Failed to get Misson function pointers!" << std::endl; return false; } return true; diff --git a/src/core/back/missionResultHandle.h b/src/core/service/missionResultHandle.h similarity index 84% rename from src/core/back/missionResultHandle.h rename to src/core/service/missionResultHandle.h index 78cde9e..9587962 100644 --- a/src/core/back/missionResultHandle.h +++ b/src/core/service/missionResultHandle.h @@ -6,8 +6,7 @@ public: missionResultHandle(qtFuncBridge* qtBridgeIn) { qtBridge = qtBridgeIn; } - //如果需要根据条码判断一个料的okng,只需要调用此函数即可 - void handleAllResult(std::string SN,bool isNG,std::vector defectAndNum) { + void toBarChart(std::string SN,bool isNG,std::vector defectAndNum) { if (isNG == false) { qtBridge->pieWidgetAddOK(); } @@ -16,6 +15,9 @@ public: } emit qtBridge->histogramWidgetAddData(defectAndNum); } + void toWaveForm(QString windowName, double value) { + emit qtBridge->waveFormAddData(windowName, value); + } void test() { QThread::msleep(1000); emit qtBridge->pieWidgetAddNG(); diff --git a/src/core/service/paramSyncManager.h b/src/core/service/paramSyncManager.h new file mode 100644 index 0000000..0571f7d --- /dev/null +++ b/src/core/service/paramSyncManager.h @@ -0,0 +1,151 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class paramSyncManager { +public: + // 构造函数:从JSON数据初始化参数 + explicit paramSyncManager(const nlohmann::ordered_json& json = nlohmann::ordered_json::object()) { + loadFromJson(json); + } + // 获取参数值,如果不存在则返回默认值 + QVariant get(QString name, QVariant defaultValue) { + QMutexLocker locker(&globalMutex); + if (map.contains(name)) { + return map[name]; + } + map.insert(name, defaultValue); + return defaultValue; + } + + // 编辑/设置参数值 + void edit(const QString& name, const QVariant& target) { + QMutexLocker locker(&globalMutex); + if (map.contains(name)) { + map[name] = target; + } else { + // 如果参数不存在,创建新的参数项 + map.insert(name, target); + } + } + + // 检查参数是否存在 + bool contains(const QString& name) const { + QMutexLocker locker(&globalMutex); + return map.contains(name); + } + + // 移除参数 + bool remove(const QString& name) { + QMutexLocker locker(&globalMutex); + return map.remove(name) > 0; + } + + // 获取所有参数名 + QStringList getParameterNames() const { + QMutexLocker locker(&globalMutex); + return map.keys(); + } + + //获取所有参数 + std::vector> getAllParams() { + QMutexLocker locker(&globalMutex); + std::vector> result; + result.reserve(map.size()); + for (auto it = map.cbegin(); it != map.cend(); ++it) { + result.emplace_back(it.key(), it.value()); + } + return result; + } + + // 从JSON加载数据 + void loadFromJson(const nlohmann::ordered_json& json) { + QMutexLocker locker(&globalMutex); + map.clear(); + + if (!json.is_object()) { + qDebug() << "Invalid JSON format for paramSyncManager"; + return; + } + + for (auto it = json.begin(); it != json.end(); ++it) { + QString key = QString::fromStdString(it.key()); + QVariant value = jsonToQVariant(it.value()); + + map.insert(key, value); + } + } + + // 导出为JSON + nlohmann::ordered_json toJson() const { + QMutexLocker locker(&globalMutex); + nlohmann::ordered_json result = nlohmann::ordered_json::object(); + + for (auto it = map.begin(); it != map.end(); ++it) { + result[it.key().toStdString()] = qVariantToJson(it.value()); + } + + return result; + } + + // 清空所有参数 + void clear() { + QMutexLocker locker(&globalMutex); + map.clear(); + } + +private: + + QMap map; + mutable QMutex globalMutex; // 保护整个map的全局互斥锁 + + // 辅助方法:将JSON值转换为QVariant + QVariant jsonToQVariant(const nlohmann::json& json) const { + if (json.is_null()) { + return QVariant(); + } else if (json.is_boolean()) { + return QVariant(json.get()); + } else if (json.is_number_integer()) { + return QVariant(json.get()); + } else if (json.is_number_float()) { + return QVariant(json.get()); + } else if (json.is_string()) { + return QVariant(QString::fromStdString(json.get())); + } else if (json.is_array()) { + // 可以根据需要扩展数组支持 + return QVariant(QString::fromStdString(json.dump())); + } else if (json.is_object()) { + // 可以根据需要扩展对象支持 + return QVariant(QString::fromStdString(json.dump())); + } + return QVariant(); + } + + // 辅助方法:将QVariant转换为JSON值 + nlohmann::json qVariantToJson(const QVariant& variant) const { + switch (variant.type()) { + case QVariant::Bool: + return variant.toBool(); + case QVariant::Int: + return variant.toInt(); + case QVariant::Double: + return variant.toDouble(); + case QVariant::String: + return variant.toString().toStdString(); + case QVariant::Invalid: + return nullptr; + default: + // 对于其他类型,转换为字符串 + return variant.toString().toStdString(); + } + } +}; + diff --git a/src/core/back/serviceManager.h b/src/core/service/serviceManager.h similarity index 80% rename from src/core/back/serviceManager.h rename to src/core/service/serviceManager.h index 5d3615d..4e67cce 100644 --- a/src/core/back/serviceManager.h +++ b/src/core/service/serviceManager.h @@ -4,16 +4,19 @@ #include #include #include +#include + class ServiceManager { public: detDataManager* _detDataManager = nullptr;// 检测数据管理器 需要初始化 imgSaveThreadManager* _imgSaveThreadManager = nullptr;// 保存图像线程 需要初始化 triggerManager* _triggerManager = nullptr;// 触发管理器 - handleAiModelManager* _handleAiModelManager = nullptr;// 模型管理器 + //handleAiModelManager* _handleAiModelManager = nullptr;// 模型管理器 cameraManager* _cameraManager = nullptr; SQLiteDB* dataBase = nullptr; CsvWriter* _csvWriter = nullptr; missionResultHandle* _missionResultHandle = nullptr; + paramSyncManager* _paramSyncManager = nullptr; }; #pragma once \ No newline at end of file diff --git a/src/core/back/serviceModule.h b/src/core/service/serviceModule.h similarity index 98% rename from src/core/back/serviceModule.h rename to src/core/service/serviceModule.h index 08a6e38..f45f711 100644 --- a/src/core/back/serviceModule.h +++ b/src/core/service/serviceModule.h @@ -17,7 +17,6 @@ public: connect(_thread, &QThread::started, this, [this]() { this->run(); }); } virtual ~serviceModule() { - // 尝试优雅关闭 if (_thread) { if (_thread->isRunning()) { _thread->quit(); diff --git a/src/core/back/summaryData.h b/src/core/service/summaryData.h similarity index 100% rename from src/core/back/summaryData.h rename to src/core/service/summaryData.h diff --git a/src/Core/back/triggerManager.cpp b/src/core/service/triggerManager.cpp similarity index 54% rename from src/Core/back/triggerManager.cpp rename to src/core/service/triggerManager.cpp index d479160..9e5a157 100644 --- a/src/Core/back/triggerManager.cpp +++ b/src/core/service/triggerManager.cpp @@ -2,15 +2,13 @@ #include #include #include -//#include -#include +#include -triggerManager::triggerManager(detProcess* _detProcessIn, qtFuncBridge* bridgeIn,nlohmann::ordered_json& triggerJson) +triggerManager::triggerManager(ServiceManager* _serviceManager, qtFuncBridge* bridgeIn,nlohmann::ordered_json& triggerJson) { bridge = bridgeIn; - _detProcess = _detProcessIn; - dManager = _detProcessIn->_serviceManager._detDataManager; - _cameraManager = _detProcessIn->_serviceManager._cameraManager; + dManager = _serviceManager->_detDataManager; + _cameraManager = _serviceManager->_cameraManager; for (auto& d : triggerJson["readAllCmd"]) { readAllStruct _struct; _struct.readAllCmd = QByteArray::fromStdString(d); @@ -68,26 +66,16 @@ bool triggerManager::connectHost(){ return false; } if (libLoadedflag){ -#if switchToRpc == 0 - CreateFunction createFun = (CreateFunction)m_lib->resolve("create"); - ReleaseFunction releaseFun = (ReleaseFunction)m_lib->resolve("destory"); -#else + CreateRpcModuleBaseFunction createFun = (CreateRpcModuleBaseFunction)m_lib->resolve("create"); ReleaseRpcModuleBaseFunction releaseFun = (ReleaseRpcModuleBaseFunction)m_lib->resolve("destory"); -#endif bool initFailure = false; if (createFun && releaseFun) { trigger = createFun(); destory_finstcp = releaseFun; -#if switchToRpc == 0 - trigger->writeData(vec_mat, QByteArray(IpAndPort.toUtf8())); - initFailure = trigger->initParas("sleeptime_20_readnum_400_writetype_uchar"); -#else trigger->writeData(vec_mat, QByteArray(IpAndPort.toUtf8())); initFailure = trigger->initParas(QByteArray()); -#endif - emit bridge->loadingWindow_appendInitLog(triggerFileName + "连接到" + IpAndPort,"white"); } @@ -110,19 +98,7 @@ bool triggerManager::connectHost(){ return false; } void triggerManager::reciveCommond() { -#if switchToRpc==0 - //auto start = std::chrono::high_resolution_clock::now(); - QByteArray readByte; - mutex.lock(); - if (!sendFinsMsg(readAllCmd)) - qCritical() << "[TCP] " << " finstcp_ptr reciveData error!"; - if (!reciveFinsMsg(readByte)) - qCritical() << "[TCP] " << " finstcp_ptr readData error!"; - mutex.unlock(); - tLocker.reFreshLocker(readByte);//经过检查后再触发 - decodeCameraCMD(readByte); -#else - //auto start = std::chrono::high_resolution_clock::now(); + for (auto& _struct : readAllCmds) { QByteArray readByte = _struct.readAllCmd; mutex.lock(); @@ -132,7 +108,7 @@ void triggerManager::reciveCommond() { tLocker[_struct.readAllCmd].reFreshLocker(readByte);//经过检查后再触发 decodeCameraCMD(_struct,readByte); } -#endif + } void triggerManager::decodeCameraCMD(readAllStruct& _struct, QByteArray byte) @@ -175,4 +151,112 @@ void triggerManager::decodeCameraCMD(readAllStruct& _struct, QByteArray byte) checkCallback(_struct,byte); checkUnionCallback(_struct, byte); checkPlcValueViewer(_struct, byte); +} + + +void triggerManager::checkCallback(readAllStruct _struct, QByteArray& byteArray) {//检查是否符合回调函数的触发条件 + QMutexLocker locer(&mutex); + for (auto callback : triggerCallbackFuncs[_struct.readAllCmd]) + { + if (checkTrigger(callback.triggerAddress, callback.triggerValue, _struct, byteArray) == true) + { + qDebug() << "func:" << QString::fromStdString(callback.funcName) << "->已触发"; + std::thread([callback, byteArray]() mutable { + try { + callback.func(callback.triggerAddress, callback.triggerValue, byteArray); // 执行回调函数 + } + catch (const std::exception& e) { + std::cerr << "Error in callback " << callback.funcName << ": " << e.what() << std::endl; + } + }).detach(); + } + } +} + +void triggerManager::checkUnionCallback(readAllStruct& _struct, QByteArray& byteArray) { + QMutexLocker locer(&mutex); + auto it = unionTriggerCallbackFuncs.find(_struct.readAllCmd); + if (it == unionTriggerCallbackFuncs.end()) return; + auto& vec = it->second; + for (auto& cb : vec) { + if (cb.triggerAddressAndValue.empty()) continue; + bool matched = false; + std::vector> triggerSource; + if (cb.mode == conditionMode:: or ) { + // 任意一个满足即可 + for (auto& cond : cb.triggerAddressAndValue) { + if (checkTrigger(cond.first, cond.second, _struct, byteArray, true)) { // 不加锁,先判断 + matched = true; + triggerSource.push_back(cond); + } + else + triggerSource.push_back({ cond.first ,INT_MIN });// 不满足的标记为 INT_MIN + } + } + else { // AND 模式 + matched = true; + for (auto& cond : cb.triggerAddressAndValue) { + if (!checkTrigger(cond.first, cond.second, _struct, byteArray, true)) { matched = false; break; } + } + triggerSource = cb.triggerAddressAndValue; // 全部都符合 + } + if (!matched) continue; + // 再次对匹配条件加锁,防止重复触发 + for (auto& cond : cb.triggerAddressAndValue) { + int triggerPos = (cond.first - _struct.startPos) * 2 + 1; + if (triggerPos >= 0 && triggerPos < byteArray.size()) { + // 只有当前值仍符合才加锁 + if (byteArray.at(triggerPos) == cond.second) { + tLocker[_struct.readAllCmd].addLocker(triggerPos, cond.second); + } + } + } + qDebug() << "Union func:" << QString::fromStdString(cb.funcName) << "->已触发"; + std::thread([cb, copyByte = byteArray, triggerSource]() mutable { + try { + cb.func(triggerSource, copyByte); + } + catch (const std::exception& e) { + std::cerr << "Error in union callback " << cb.funcName << ": " << e.what() << std::endl; + } + }).detach(); + } +} + +bool triggerManager::checkTrigger(int dmPos, int value, readAllStruct& _struct, QByteArray& byte, bool lockerDisable) { + int triggerPos = (dmPos - _struct.startPos) * 2 + 1; + if (byte.size() < triggerPos) + return false; + if (byte.at(triggerPos) == value) { + if (tLocker[_struct.readAllCmd].checkLocker(triggerPos) == false) + return false; + if (lockerDisable == false) + tLocker[_struct.readAllCmd].addLocker(triggerPos, value); + return true; + } + else + return false; +} +void triggerManager::checkPlcValueViewer(readAllStruct& _struct, QByteArray& byteArray) { + QMutexLocker locer(&mutex); + for (auto& data : plcValueViewerDatas) { + int checkPos = (data.valueAddr - _struct.startPos) * 2 + 1; + if (checkPos < byteArray.size() && checkPos >= 0) { + QVariant newValue; + if (data.type == finsFloat) + newValue = readFinsFloatFromQByteArray(byteArray, checkPos, true); + else + newValue = byteArray.at(checkPos); + + if (newValue != data.value) { + data.value = newValue; + if (data.type == finsFloat) + emit bridge->runCommand_refeshPlcValueViewer(data.valueAddr, + QString::number(data.value.toDouble(), 'f', 4)); + else + emit bridge->runCommand_refeshPlcValueViewer(data.valueAddr, data.value.toString()); + } + } + } + } \ No newline at end of file diff --git a/src/Core/back/triggerManager.h b/src/core/service/triggerManager.h similarity index 69% rename from src/Core/back/triggerManager.h rename to src/core/service/triggerManager.h index 0ee9189..0f729e9 100644 --- a/src/Core/back/triggerManager.h +++ b/src/core/service/triggerManager.h @@ -10,7 +10,6 @@ #include #include // for std::thread used in async callbacks -#define switchToRpc 1 #define debugTesting 0 using triggerCallback = std::function; using uniontriggerCallback = std::function>, QByteArray& byte)>; @@ -18,7 +17,44 @@ class cameraManager; class detThreadManager; class detDataManager; class lightSourceManager; -class detProcess; +class ServiceManager; + +// 将此静态函数加入到类 triggerManager 的合适位置(如 private 部分) +static float readFinsFloatFromQByteArray(const QByteArray& buf, int startByteIndex, bool highLowConversion) { + if (buf.isEmpty()) { + return 0.0f; + } + + int byteIndex = startByteIndex; + if (byteIndex % 2 != 0) { + --byteIndex; // 对齐到字高位 + } + if (byteIndex < 0 || byteIndex + 3 >= buf.size()) { + qWarning() << "[FINS]" << "readFinsFloatFromQByteArray index out of range" << byteIndex; + return 0.0f; + } + + std::array bytes = { + static_cast(buf.at(byteIndex)), + static_cast(buf.at(byteIndex + 1)), + static_cast(buf.at(byteIndex + 2)), + static_cast(buf.at(byteIndex + 3)) + }; + + if (highLowConversion) { + std::swap(bytes[0], bytes[2]); + std::swap(bytes[1], bytes[3]); // 交换 16 位字顺序 + } + + quint32 raw = (quint32(bytes[0]) << 24) | + (quint32(bytes[1]) << 16) | + (quint32(bytes[2]) << 8) | + quint32(bytes[3]); + + float value = 0.0f; + std::memcpy(&value, &raw, sizeof(float)); + return value; +} //防止重复触发 class triggerLockerT { public: @@ -68,7 +104,7 @@ public: this->stop(); delete trigger; } - triggerManager(detProcess*, qtFuncBridge* bridgeIn, nlohmann::ordered_json& triggerJson); + triggerManager(ServiceManager*, qtFuncBridge* bridgeIn, nlohmann::ordered_json& triggerJson); bool connectHost(); void run(); void stop(){ @@ -181,15 +217,6 @@ public: #pragma endregion #pragma region 接收数据 void reciveCommond(); - //int read(int dmPos, QByteArray byte) - //{ - // int triggerPos = (dmPos - startPos) * 2; - // if (byte.size() < triggerPos) - // return false; - // int val1 = byte.at(triggerPos) * 256; - // int val2 = static_cast(byte.at(triggerPos + 1)); - // return val1 + val2; - //} QVariant read(int dmPos) { QByteArray readByte; mutex.lock(); @@ -217,138 +244,20 @@ public: conditionMode mode; std::string funcName; }; - std::map> triggerCallbackFuncs; - //单条件的触发器注册 - bool registerCallback(int triggerAddress, int triggerValue, triggerCallback func, std::string funcName =""){ - QMutexLocker locer(&mutex); - triggerCallbackStruct _triggerCallbackStruct; - _triggerCallbackStruct.triggerAddress = triggerAddress; - _triggerCallbackStruct.triggerValue = triggerValue; - _triggerCallbackStruct.func = func; - _triggerCallbackStruct.funcName = funcName.empty() ? std::to_string(triggerAddress) + "触发" : funcName; - for (auto& _struct : readAllCmds) { - if (triggerAddress >= _struct.startPos && triggerAddress < _struct.startPos + _struct.readLength) { - triggerCallbackFuncs[_struct.readAllCmd].push_back(_triggerCallbackStruct); - return true; - } - } - qCritical() << "触发器回调地址超限了" << triggerAddress << triggerValue; - return false; - } - std::map> unionTriggerCallbackFuncs; - //联合条件的触发器注册 - bool registerUnionCallback(std::vector> triggerAddressAndValue, - conditionMode mode, - uniontriggerCallback func, std::string funcName = "") { - QMutexLocker locer(&mutex); - if (triggerAddressAndValue.size() < 2) { - qCritical() << "联合触发器至少需要两个条件"; - return false; - } - // 1. 校验所有地址都在某一个 readAllStruct 范围内(限制:当前实现只支持单段内的联合条件) - readAllStruct* ownerStruct = nullptr; - for (auto& candidate : readAllCmds) { - bool allIn = true; - for (auto& p : triggerAddressAndValue) { - if (!(p.first >= candidate.startPos && p.first < candidate.startPos + candidate.readLength)) { - allIn = false; break; - } - } - if (allIn) { ownerStruct = &candidate; break; } - } - if (ownerStruct == nullptr) { - qCritical() << "联合触发器的各地址不在同一块连续读取区间内,当前实现不支持跨区间联合"; - return false; - } - // 2. 构造并登记 - unionTriggerCallbackStruct cb; - cb.triggerAddressAndValue = std::move(triggerAddressAndValue); - cb.func = std::move(func); - cb.mode = mode; - cb.funcName = funcName.empty() ? QString("%1~%2联合触发").arg(cb.triggerAddressAndValue[0].first).arg(cb.triggerAddressAndValue[cb.triggerAddressAndValue.size() - 1].first).toStdString() : funcName; - unionTriggerCallbackFuncs[ownerStruct->readAllCmd].push_back(std::move(cb)); - qInfo() << "注册联合触发器成功:" << ownerStruct->readAllCmd << ", 条件数=" << unionTriggerCallbackFuncs[ownerStruct->readAllCmd].back().triggerAddressAndValue.size(); - return true; - } - void checkCallback(readAllStruct _struct,QByteArray & byteArray){//检查是否符合回调函数的触发条件 - QMutexLocker locer(&mutex); - for (auto callback : triggerCallbackFuncs[_struct.readAllCmd]) - { - if (checkTrigger(callback.triggerAddress, callback.triggerValue, _struct, byteArray) == true) - { - qDebug() << "func:" << QString::fromStdString(callback.funcName) << "->已触发"; - std::thread([callback, byteArray]() mutable { - try { - callback.func(callback.triggerAddress, callback.triggerValue, byteArray); // 执行回调函数 - } - catch (const std::exception& e) { - std::cerr << "Error in callback " << callback.funcName << ": " << e.what() << std::endl; - } - }).detach(); - } - } - } - void checkUnionCallback(readAllStruct& _struct, QByteArray& byteArray) { - QMutexLocker locer(&mutex); - auto it = unionTriggerCallbackFuncs.find(_struct.readAllCmd); - if (it == unionTriggerCallbackFuncs.end()) return; - auto& vec = it->second; - for (auto& cb : vec) { - if (cb.triggerAddressAndValue.empty()) continue; - bool matched = false; - std::vector> triggerSource; - if (cb.mode == conditionMode::or) { - // 任意一个满足即可 - for (auto& cond : cb.triggerAddressAndValue) { - if (checkTrigger(cond.first, cond.second, _struct, byteArray, true)) { // 不加锁,先判断 - matched = true; - triggerSource.push_back(cond); - } - else - triggerSource.push_back({ cond.first ,INT_MIN});// 不满足的标记为 INT_MIN - } - } else { // AND 模式 - matched = true; - for (auto& cond : cb.triggerAddressAndValue) { - if (!checkTrigger(cond.first, cond.second, _struct, byteArray, true)) { matched = false; break; } - } - triggerSource = cb.triggerAddressAndValue; // 全部都符合 - } - if (!matched) continue; - // 再次对匹配条件加锁,防止重复触发 - for (auto& cond : cb.triggerAddressAndValue) { - int triggerPos = (cond.first - _struct.startPos) * 2 + 1; - if (triggerPos >= 0 && triggerPos < byteArray.size()) { - // 只有当前值仍符合才加锁 - if (byteArray.at(triggerPos) == cond.second) { - tLocker[_struct.readAllCmd].addLocker(triggerPos, cond.second); - } - } - } - qDebug() << "Union func:" << QString::fromStdString(cb.funcName) << "->已触发"; - std::thread([cb, copyByte = byteArray, triggerSource]() mutable { - try { - cb.func(triggerSource, copyByte); - } catch (const std::exception& e) { - std::cerr << "Error in union callback " << cb.funcName << ": " << e.what() << std::endl; - } - }).detach(); - } - } std::string readSnFromQByteArray(QByteArray plcByte, int dmPos, - bool highLowConversion, bool clearAfterRead, int Size){ + bool highLowConversion, bool clearAfterRead, int Size) { int startPos = 0; for (auto& _struct : readAllCmds) { if (dmPos >= _struct.startPos && dmPos < _struct.startPos + _struct.readLength) startPos = _struct.startPos; } if (startPos == 0) { - qCritical() << dmPos<< "读条码越界"; + qCritical() << dmPos << "读条码越界"; return "READ_SN_ERROR_OUT_OF_RANGE[" + std::to_string(dmPos) + "]"; } int triggerPos = (dmPos - startPos) * 2; - if (triggerPos < 0){ + if (triggerPos < 0) { std::cout << "Read SN ,trigger is DM:" << dmPos << ",but read start at DM:" << startPos << endl; return "readSnConfigError" + getCurrentDateTime(); } @@ -392,38 +301,87 @@ public: return str.toStdString(); } - //SUB - void registerPlcValueViewer(const QString& groupName, const std::vector>& viewerNameAndAddress) { - for (auto& data : viewerNameAndAddress) { - if (data.second < 0)continue; - plcValueViewerDatas.push_back({ data.second, -1 }); - } - emit bridge->runCommand_addPlcValueViewer(groupName, viewerNameAndAddress); - } - void checkPlcValueViewer(readAllStruct& _struct, QByteArray& byteArray) { + //单条件的触发器注册 + bool registerCallback(int triggerAddress, int triggerValue, triggerCallback func, std::string funcName) { QMutexLocker locer(&mutex); - for (auto& data : plcValueViewerDatas) { - int checkPos = (data.first - _struct.startPos) * 2 + 1; - if(checkPos< byteArray.size() && checkPos >= 0) - if (byteArray.at(checkPos) != data.second) { - data.second = byteArray.at(checkPos); - emit bridge->runCommand_refeshPlcValueViewer(data.first, data.second); - } + triggerCallbackStruct _triggerCallbackStruct; + _triggerCallbackStruct.triggerAddress = triggerAddress; + _triggerCallbackStruct.triggerValue = triggerValue; + _triggerCallbackStruct.func = func; + _triggerCallbackStruct.funcName = funcName.empty() ? std::to_string(triggerAddress) + "触发" : funcName; + for (auto& _struct : readAllCmds) { + if (triggerAddress >= _struct.startPos && triggerAddress < _struct.startPos + _struct.readLength) { + triggerCallbackFuncs[_struct.readAllCmd].push_back(_triggerCallbackStruct); + return true; + } } - + qCritical() << "触发器回调地址超限了" << triggerAddress << triggerValue; + return false; } - std::vector>plcValueViewerDatas; + std::map> triggerCallbackFuncs; + + //联合条件的触发器注册 + bool registerUnionCallback(std::vector> triggerAddressAndValue, + conditionMode mode, + uniontriggerCallback func, std::string funcName) { + QMutexLocker locer(&mutex); + if (triggerAddressAndValue.size() < 2) { + qCritical() << "联合触发器至少需要两个条件"; + return false; + } + // 1. 校验所有地址都在某一个 readAllStruct 范围内(限制:当前实现只支持单段内的联合条件) + readAllStruct* ownerStruct = nullptr; + for (auto& candidate : readAllCmds) { + bool allIn = true; + for (auto& p : triggerAddressAndValue) { + if (!(p.first >= candidate.startPos && p.first < candidate.startPos + candidate.readLength)) { + allIn = false; break; + } + } + if (allIn) { ownerStruct = &candidate; break; } + } + if (ownerStruct == nullptr) { + qCritical() << "联合触发器的各地址不在同一块连续读取区间内,当前实现不支持跨区间联合"; + return false; + } + // 2. 构造并登记 + unionTriggerCallbackStruct cb; + cb.triggerAddressAndValue = std::move(triggerAddressAndValue); + cb.func = std::move(func); + cb.mode = mode; + cb.funcName = funcName.empty() ? QString("%1~%2联合触发").arg(cb.triggerAddressAndValue[0].first).arg(cb.triggerAddressAndValue[cb.triggerAddressAndValue.size() - 1].first).toStdString() : funcName; + unionTriggerCallbackFuncs[ownerStruct->readAllCmd].push_back(std::move(cb)); + qInfo() << "注册联合触发器成功:" << ownerStruct->readAllCmd << ", 条件数=" << unionTriggerCallbackFuncs[ownerStruct->readAllCmd].back().triggerAddressAndValue.size(); + return true; + } + std::map> unionTriggerCallbackFuncs; + + //SUB + void registerPlcValueViewer(const QString& groupName, const std::vector& viewerDatas) { + QMutexLocker locer(&mutex); + for (auto& data : viewerDatas) { + if (data.valueAddr < 0)continue; + plcValueViewerDatas.push_back(data); + } + std::vector> forShow; + for (auto& data : viewerDatas) { + forShow.push_back(std::make_pair(data.valueName, data.valueAddr)); + } + emit bridge->runCommand_addPlcValueViewer(groupName, forShow); + } + std::vectorplcValueViewerDatas; private: QElapsedTimer lostConnectionCountTimer,lostConnectionRePrintTimer; qtFuncBridge* bridge; bool libLoadedflag = false; QLibrary* m_lib = nullptr; - detProcess* _detProcess; + //detProcess* _detProcess; lightSourceManager* _lightSourceManager; detThreadManager* _threadManager; detDataManager* dManager; cameraManager* _cameraManager; std::map tLocker;//防止重复触发,每次收到数据时检查数据是否与上次相同,如果相同checkTrigger会返回false + void decodeCameraCMD(readAllStruct&, QByteArray byte); /// /// 检测QByteArray中某位置的值是否和输入一样,使用了绝对位置 /// @@ -431,32 +389,7 @@ private: /// 值 /// QByteArray /// - bool checkTrigger(int dmPos, int value, readAllStruct& _struct, QByteArray& byte, bool lockerDisable = false){ - int triggerPos = (dmPos - _struct.startPos) * 2 + 1; - if (byte.size() < triggerPos) - return false; - if (byte.at(triggerPos) == value){ - if (tLocker[_struct.readAllCmd].checkLocker(triggerPos) == false) - return false; - if (lockerDisable == false) - tLocker[_struct.readAllCmd].addLocker(triggerPos, value); - return true; - } - else - return false; - } - bool checkTrigger_Unsafe(int dmPos, int value, readAllStruct& _struct, QByteArray& byte, bool lockerDisable = false) { - int triggerPos = (dmPos - _struct.startPos) * 2 + 1; - if (byte.at(triggerPos) == value) { - if (tLocker[_struct.readAllCmd].checkLocker(triggerPos) == false) - return false; - if (lockerDisable == false) - tLocker[_struct.readAllCmd].addLocker(triggerPos, value); - return true; - } - else - return false; - } + bool checkTrigger(int dmPos, int value, readAllStruct& _struct, QByteArray& byte, bool lockerDisable = false); //接受数据 bool reciveFinsMsg(QByteArray& readByte){ if (!trigger->readData(vec_mat, readByte)) @@ -467,8 +400,10 @@ private: return true; } QMutex mutex; //互斥锁,防止多线程访问冲突 - //解析并触发 - void decodeCameraCMD(readAllStruct&,QByteArray byte); + void checkPlcValueViewer(readAllStruct& _struct, QByteArray& byteArray); + void checkCallback(readAllStruct _struct, QByteArray& byteArray); + void checkUnionCallback(readAllStruct& _struct, QByteArray& byteArray); + //触发器的配置 int sleepIntervalMS = 50;//间隔 int heartBeatIntervalMS = 800; diff --git a/src/fuck-u-code-windows-amd64.exe b/src/fuck-u-code-windows-amd64.exe deleted file mode 100644 index c9da3c8..0000000 Binary files a/src/fuck-u-code-windows-amd64.exe and /dev/null differ diff --git a/src/launcher/GUI/front/loadingWidget.h b/src/launcher/GUI/front/loadingWidget.h index 638a93b..c88d05f 100644 --- a/src/launcher/GUI/front/loadingWidget.h +++ b/src/launcher/GUI/front/loadingWidget.h @@ -29,7 +29,7 @@ public: { retryBtn->setDisabled(true); start(); - emit bridge->reInitConfig(); + emit bridge->restartInit(); }); } void start(){ diff --git a/src/launcher/GUI/front/pages/displayPageWidget.h b/src/launcher/GUI/front/pages/displayPageWidget.h index b76df9f..3d70325 100644 --- a/src/launcher/GUI/front/pages/displayPageWidget.h +++ b/src/launcher/GUI/front/pages/displayPageWidget.h @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -15,12 +15,15 @@ #include #include #include -#include +#include #include #include #include #include #include +#include +#include + class toolBarWidget : public QWidget { Q_OBJECT public: @@ -72,79 +75,8 @@ public: this->setFixedHeight(80); } // 通用的创建工具按钮函数实现 - bool createToolButton(const QString& iconPath, const QString& text, - std::function clickHandler, - const QSize& buttonSize = QSize(100, 60), const QSize& iconSize = QSize(32, 32)){ - - // 参数验证 - if (iconPath.isEmpty() || text.isEmpty() || !clickHandler) { - return false; - } - - // 创建布局 - QVBoxLayout* btnLayout = new QVBoxLayout(); - if (!btnLayout) { - return false; - } - - btnLayout->setSpacing(0); - btnLayout->setContentsMargins(0, 0, 0, 0); - - // 使用 QToolButton 来实现图标在上文字在下的效果 - QToolButton* btn = new QToolButton(this); - if (!btn) { - delete btnLayout; - return false; - } - - // 设置图标,检查图标是否加载成功 - QIcon icon(iconPath); - if (icon.isNull()) { - delete btn; - delete btnLayout; - return false; - } - - - btn->setIcon(icon); - btn->setText(text); - btn->setIconSize(iconSize); - btn->setFixedSize(buttonSize.width(), buttonSize.height()); // 增加高度以容纳文字 - btn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); // 文字在图标下方 - - // 设置按钮样式 - btn->setStyleSheet(R"( - QToolButton { - background-color: transparent; - border: none; - color: white; - font-family: "微软雅黑"; - font-size: 14px; - font-weight: bold; - } - QToolButton:hover { - background-color: black; - border-radius: 5px; - } - )"); - - btnLayout->addWidget(btn); - btnLayout->setAlignment(Qt::AlignCenter); - - // 连接点击事件 - if (!connect(btn, &QToolButton::clicked, clickHandler)) { - delete btn; - delete btnLayout; - return false; - } - - // 添加到按钮布局容器中 - buttonsLayout->addLayout(btnLayout); - - return true; - } bool createToolButton(QIcon icon, const QString& text, - std::function clickHandler, + std::function clickHandler, Auth::UserLevel level = Auth::UserLevel::GUEST, const QSize& buttonSize = QSize(100, 60), const QSize& iconSize = QSize(32, 32)) { // 参数验证 @@ -196,7 +128,11 @@ public: btnLayout->setAlignment(Qt::AlignCenter); // 连接点击事件 - if (!connect(btn, &QToolButton::clicked, clickHandler)) { + if (!connect(btn, &QToolButton::clicked, [this, level,clickHandler]() { + m_config->Authorization.executeWithPermission(level, [this, clickHandler]() { + clickHandler(); + }); + })) { delete btn; delete btnLayout; return false; @@ -251,7 +187,8 @@ public: dayNightShiftHistogramWidget* _dayNightShiftHistogramWidget; userSwitch* _userSwitch; softwareDataDisplay* _softwareDataDisplay; - + calibration* _calibration; + cameraRealTimeCapture* _cameraRealTimeCapture; runCommandWidget* _runCommandWidget; LogWindow* log = new LogWindow(); displayWidget(appConfig* config) :m_config(config){ @@ -357,7 +294,7 @@ public: QFont font3("微软雅黑", 10, QFont::Bold); #pragma region 日志分析按钮 { - _toolBarWidget->createToolButton(":/TongYongUiIcon/TongYongUiIcon/4.LogAnalyze.png", + _toolBarWidget->createToolButton(QIcon(":/TongYongUiIcon/TongYongUiIcon/4.LogAnalyze.png"), QString::fromStdString(trans.translate("Log")), [=]() { emit changeStackWidget(2); @@ -394,7 +331,7 @@ public: #pragma endregion #pragma region 结果图查看按钮 { - _toolBarWidget->createToolButton(":/TongYongUiIcon/TongYongUiIcon/23.1.imageView.png", + _toolBarWidget->createToolButton(QIcon(":/TongYongUiIcon/TongYongUiIcon/23.1.imageView.png"), QString::fromStdString(trans.translate("ViewImage")), [=]() { QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(config->imgSavePath))); @@ -403,25 +340,21 @@ public: #pragma endregion #pragma region 修改检测参数按钮 { - _toolBarWidget->createToolButton(":/TongYongUiIcon/TongYongUiIcon/search_1.png", + _toolBarWidget->createToolButton(QIcon(":/TongYongUiIcon/TongYongUiIcon/search_1.png"), QString::fromStdString(trans.translate("Param")), - [=]() { - config->Authorization.executeWithPermission(Auth::UserLevel::ADMIN,[this]() { - JsonParamTableEditor* dlg = new JsonParamTableEditor(this); - dlg->setJsonObject(m_config->missionJson["backend"]["missionSettings"]); - dlg->setAttribute(Qt::WA_DeleteOnClose); - connect(dlg, &JsonParamTableEditor::jsonAccepted, [this](const nlohmann::json& result) { - m_config->missionJson["backend"]["missionSettings"] = result; - }); + [this]() { + paramTableEditor* dlg = new paramTableEditor(this); + std::vector> params; + emit qtFuncBridge::getInstance()->getAllParams(¶ms); + dlg->setParams(params); dlg->show(); - }); - }); + }, Auth::UserLevel::ADMIN); } #pragma endregion #pragma region 故障排查按钮 { _runCommandWidget = new runCommandWidget(); - _toolBarWidget->createToolButton(":/TongYongUiIcon/TongYongUiIcon/18.help.png", + _toolBarWidget->createToolButton(QIcon(":/TongYongUiIcon/TongYongUiIcon/18.help.png"), QString::fromStdString(trans.translate("Debug")), [=]() { _runCommandWidget->show(); @@ -429,6 +362,36 @@ public: }); } #pragma endregion +#pragma region 相机实时取流 + { + _cameraRealTimeCapture = new cameraRealTimeCapture(); + _toolBarWidget->createToolButton(QIcon(":/TongYongUiIcon/TongYongUiIcon/0.cameraModule.png"), + QString::fromStdString("相机实时取流"), + [=]() { + _cameraRealTimeCapture->show(); + }, Auth::UserLevel::ADMIN); + } +#pragma endregion +#pragma region 九点标定引导按钮 + { + _calibration = new calibration(); + _toolBarWidget->createToolButton(QIcon(":/icon/icon/jiuDianBiaoDingBiaoDing.png"), + QString::fromStdString("九点标定"), + [=]() { + _calibration->show(); + }, Auth::UserLevel::ADMIN); + } +#pragma endregion +#pragma region 标定板标定 + { + //_calibration = new calibration(); + _toolBarWidget->createToolButton(QIcon(":/icon/icon/biaoDingBanBiaoDing.png"), + QString::fromStdString("标定板标定"), + [=]() { + //_calibration->show(); + }, Auth::UserLevel::ADMIN); + } +#pragma endregion #pragma region 读取bin目录下的tools文件夹并展示 { std::vector files; diff --git a/src/launcher/GUI/front/pages/logPageWidget.h b/src/launcher/GUI/front/pages/logPageWidget.h index d1a7ea2..5ef65c2 100644 --- a/src/launcher/GUI/front/pages/logPageWidget.h +++ b/src/launcher/GUI/front/pages/logPageWidget.h @@ -31,7 +31,7 @@ //行数上限 const int MAX_LOGWIDGET_LINES = 1000; //内存中存储的消息数量上限 -const int MAX_STORED_MESSAGES = 8000; +const int MAX_STORED_MESSAGES = 5000; //批量处理延迟时间(毫秒) const int BATCH_UPDATE_DELAY = 300; @@ -44,25 +44,6 @@ public: qtConsle }; -private: - // 用于存储过滤器状态 - struct FilterState { - QComboBox* levelFilter; - QLineEdit* regexFilter; - QPlainTextEdit* textWidget; - QStringList allMessages; // 存储所有原始消息 - bool autoScroll = true; - mutable QMutex mutex; // 保护此状态的互斥锁 - QStringList pendingMessages; // 待处理的消息队列 - }; - - std::unordered_map filterStates; - - // 线程安全和性能优化相关 - mutable QMutex globalMutex; // 全局保护锁 - QTimer* batchUpdateTimer; // 批量更新定时器 - -public: QVBoxLayout* Layout; QHBoxLayout* btnLayout; std::vector btns; @@ -128,6 +109,10 @@ public: btns[0]->pressed(); Layout->addWidget(widgets); + //初始化颜色 QTextCharFormat whiteFormat; + whiteFormat.setForeground(QColor(255, 255, 255)); // 白色 + redFormat.setForeground(QColor(255, 100, 100)); // 红色 + warningFormat.setForeground(QColor(255, 200, 100)); // 橙色 } void addLogWidget(QString btnText, QPlainTextEdit* textWidget, logType type) { @@ -144,9 +129,6 @@ public: btnPtr->toDefault(); btn->pressed(); widgets->setCurrentIndex(index); - - // 切换页面时应用过滤器以显示最新内容 - applyFilters(index); }); QWidget* widget = new QWidget(); @@ -157,41 +139,16 @@ public: widgetLayout->setContentsMargins(0, 0, 0, 0); widgetLayout->setAlignment(Qt::AlignLeft | Qt::AlignTop); - // 第一行工具栏:过滤器 - QHBoxLayout* toolHLayout = new QHBoxLayout(); - widgetLayout->addLayout(toolHLayout); - - QLabel* toolName = new QLabel(u8"日志级别:"); - toolName->setFont(font2); - toolName->setPalette(palette); - toolHLayout->addWidget(toolName); - - QStringList options = { "全部", "Debug", "Info", "Warning", "Error" }; - QComboBox* filterSelector = new QComboBox(); - filterSelector->addItems(options); - toolHLayout->addWidget(filterSelector); - - QLabel* toolName2 = new QLabel(u8"正则匹配:"); - toolName2->setFont(font2); - toolName2->setPalette(palette); - toolHLayout->addWidget(toolName2); - - QLineEdit* lineedit = new QLineEdit(); - lineedit->setPlaceholderText("输入正则表达式..."); - toolHLayout->addWidget(lineedit); - // 第二行工具栏:功能按钮 QHBoxLayout* toolHLayout2 = new QHBoxLayout(); widgetLayout->addLayout(toolHLayout2); - QPushButton* saveBtn = new QPushButton("保存日志"); - QPushButton* clearBtn = new QPushButton("清空"); + QPushButton* openDirBtn = new QPushButton("打开日志文件夹"); QPushButton* autoScrollBtn = new QPushButton("自动滚动"); autoScrollBtn->setCheckable(true); autoScrollBtn->setChecked(true); - saveBtn->setFixedSize(80, 25); - clearBtn->setFixedSize(60, 25); + openDirBtn->setFixedSize(120, 25); autoScrollBtn->setFixedSize(80, 25); // 设置按钮样式 @@ -215,36 +172,29 @@ public: } )"; - saveBtn->setStyleSheet(buttonStyle); - clearBtn->setStyleSheet(buttonStyle); + openDirBtn->setStyleSheet(buttonStyle); autoScrollBtn->setStyleSheet(buttonStyle); - toolHLayout2->addWidget(saveBtn); - toolHLayout2->addWidget(clearBtn); + toolHLayout2->addWidget(openDirBtn); toolHLayout2->addWidget(autoScrollBtn); toolHLayout2->addStretch(); // 存储过滤器状态 FilterState& state = filterStates[index]; - state.levelFilter = filterSelector; - state.regexFilter = lineedit; state.textWidget = textWidget; state.autoScroll = true; - // 连接过滤器信号 - connect(filterSelector, QOverload::of(&QComboBox::currentIndexChanged), - [this, index]() { applyFilters(index); }); - - connect(lineedit, &QLineEdit::textChanged, - [this, index]() { applyFilters(index); }); // 连接功能按钮 - connect(clearBtn, &QPushButton::clicked, [this, index]() { clearLog(index); }); connect(autoScrollBtn, &QPushButton::toggled, [this, index](bool checked) { filterStates[index].autoScroll = checked; }); + connect(openDirBtn, &QPushButton::clicked,[=]() { + QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(logBasePath))); + }); widgetLayout->addWidget(textWidget); + } QString getCurrentDate() { @@ -256,6 +206,23 @@ public: } private: + // 用于存储过滤器状态 + struct FilterState { + //QComboBox* levelFilter; + //QLineEdit* regexFilter; + QPlainTextEdit* textWidget; + QStringList allMessages; // 存储所有原始消息 + bool autoScroll = true; + mutable QMutex mutex; // 保护此状态的互斥锁 + QStringList pendingMessages; // 待处理的消息队列 + }; + + std::unordered_map filterStates; + + // 线程安全和性能优化相关 + mutable QMutex globalMutex; // 全局保护锁 + QTimer* batchUpdateTimer; // 批量更新定时器 + // 线程安全地添加消息 void addMessageSafe(int index, const QString& message) { QMutexLocker globalLocker(&globalMutex); @@ -282,98 +249,27 @@ private: batchUpdateTimer->start(); } } - - // 批量处理待处理的消息 - - // 判断消息是否应该显示 - bool shouldShowMessage(const QString& message, const FilterState& state) { - QString levelFilter = state.levelFilter->currentText(); - QString regexPattern = state.regexFilter->text(); - - // 级别过滤 - if (levelFilter != "全部") { - if (!message.contains(levelFilter, Qt::CaseInsensitive)) { - return false; - } - } - - // 正则过滤 - if (!regexPattern.isEmpty()) { - QRegularExpression regex(regexPattern); - if (regex.isValid() && !regex.match(message).hasMatch()) { - return false; - } - } - - return true; - } - // 应用过滤器 - void applyFilters(int index) { - QMutexLocker globalLocker(&globalMutex); - - if (filterStates.find(index) == filterStates.end()) return; - - FilterState& state = filterStates[index]; - QMutexLocker stateLocker(&state.mutex); - - QString levelFilter = state.levelFilter->currentText(); - QString regexPattern = state.regexFilter->text(); - - // 清空当前显示 - state.textWidget->clear(); - - QRegularExpression regex; - bool regexValid = true; - if (!regexPattern.isEmpty()) { - regex.setPattern(regexPattern); - if (!regex.isValid()) { - state.textWidget->appendPlainText("正则表达式无效: " + regexPattern); - return; - } - } - - // 过滤并显示消息 - for (const QString& message : state.allMessages) { - if (shouldShowMessage(message, state)) { - // 根据日志级别设置颜色 - appendColoredMessage(state.textWidget, message); - } - } - - // 清空待处理消息,因为已经全部重新过滤了 - state.pendingMessages.clear(); - - // 自动滚动到底部 - if (state.autoScroll) { - scrollToBottom(state.textWidget); - } - } - // 添加带颜色的消息 + QTextCharFormat whiteFormat; + QTextCharFormat redFormat; + QTextCharFormat warningFormat; void appendColoredMessage(QPlainTextEdit* textEdit, const QString& message) { QTextCursor cursor = textEdit->textCursor(); cursor.movePosition(QTextCursor::End); - QTextCharFormat format; - format.setForeground(QColor(255, 255, 255)); // 默认白色 - // 根据关键词设置颜色 if (message.contains("Error", Qt::CaseInsensitive) || message.contains("错误", Qt::CaseInsensitive)) { - format.setForeground(QColor(255, 100, 100)); // 红色 - } else if (message.contains("Warning", Qt::CaseInsensitive) || + cursor.setCharFormat(redFormat); + } + else if (message.contains("Warning", Qt::CaseInsensitive) || message.contains("警告", Qt::CaseInsensitive)) { - format.setForeground(QColor(255, 200, 100)); // 橙色 - } else if (message.contains("Info", Qt::CaseInsensitive) || - message.contains("信息", Qt::CaseInsensitive)) { - format.setForeground(QColor(100, 200, 255)); // 蓝色 - } else if (message.contains("Debug", Qt::CaseInsensitive) || - message.contains("调试", Qt::CaseInsensitive)) { - format.setForeground(QColor(150, 150, 150)); // 灰色 + cursor.setCharFormat(warningFormat); } + else + cursor.setCharFormat(whiteFormat); - cursor.setCharFormat(format); cursor.insertText(message + "\n"); textEdit->setTextCursor(cursor); } @@ -384,23 +280,6 @@ private: scrollBar->setValue(scrollBar->maximum()); } - // 清空日志 - void clearLog(int index) { - QMutexLocker globalLocker(&globalMutex); - - if (filterStates.find(index) == filterStates.end()) return; - - auto reply = QMessageBox::question(this, "确认", "确定要清空当前日志吗?", - QMessageBox::Yes | QMessageBox::No); - if (reply == QMessageBox::Yes) { - FilterState& state = filterStates[index]; - QMutexLocker stateLocker(&state.mutex); - - state.allMessages.clear(); - state.pendingMessages.clear(); - state.textWidget->clear(); - } - } public slots: void recvStdLogMsg(QString logMsg) { @@ -433,9 +312,7 @@ private slots: if (index == currentIndex) { // 增量添加消息而不是重新过滤所有消息 for (const QString& message : state.pendingMessages) { - if (shouldShowMessage(message, state)) { - appendColoredMessage(state.textWidget, message); - } + appendColoredMessage(state.textWidget, message); } // 自动滚动到底部 diff --git a/src/launcher/GUI/mainwindow.cpp b/src/launcher/GUI/mainwindow.cpp index 62f70ae..9cf588f 100644 --- a/src/launcher/GUI/mainwindow.cpp +++ b/src/launcher/GUI/mainwindow.cpp @@ -97,7 +97,7 @@ void MainWindow::initLeftMenuBarComponents() { // //配置页面 _settingWidget = new settingWidget(config); - registerPageBar(leftMenuBar::barPositionEnum::bottom, ":/icon/icon/setting.png", u8"配置设置", _settingWidget); + registerPageBar(leftMenuBar::barPositionEnum::bottom, ":/icon/icon/setting.png", u8"配置设置", _settingWidget,Auth::UserLevel::ADMIN); leftBar->pageBtns[0]->click(); QObject::connect(_displayWidget, &displayWidget::changeStackWidget, this, &MainWindow::switchStackWidget); diff --git a/src/launcher/GUI/mainwindow.h b/src/launcher/GUI/mainwindow.h index 436350f..d15fa47 100644 --- a/src/launcher/GUI/mainwindow.h +++ b/src/launcher/GUI/mainwindow.h @@ -35,7 +35,6 @@ QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE -class fbBridge; class appConfig; class topMenuBar : public QWidget { Q_OBJECT @@ -313,11 +312,20 @@ private: bool setNormalAndMove = false;//判断是否从最大化开始移动 #pragma endregion #pragma region 窗口切换栏 - void registerPageBar(leftMenuBar::barPositionEnum pos,QString iconPath, QString pageDescription, QWidget* page) { + void registerPageBar(leftMenuBar::barPositionEnum pos,QString iconPath, QString pageDescription, QWidget* page, Auth::UserLevel level = Auth::UserLevel::GUEST) { auto btn = leftBar->registerPageBar(pos,iconPath, pageDescription, page); - connect(btn, &toolBarButton::toolBarButtonClicked, [=](int index) { - switchStackWidget(index); - }); + if (level == Auth::UserLevel::GUEST) { + connect(btn, &toolBarButton::toolBarButtonClicked, [=](int index) { + switchStackWidget(index); + }); + } + else{ + connect(btn, &toolBarButton::toolBarButtonClicked, [=](int index) { + config->Authorization.executeWithPermission(level, [this, index]() { + switchStackWidget(index); + }); + }); + } pageWidgets.addWidget(page); //如果报错请检查Qwidget是否有效 } @@ -398,8 +406,15 @@ public slots: _loadingWidget.clearLog(); backEndInit = true; logService::getInstance()->disableLoadingWidgetOutput(); - if (isLogin == true) + if (isLogin == true) { loading(false); + } + #if HIDE_CONSOLE + logService::getInstance()->disableConsoleOutput(); + if (config->consoleHandle) { + ShowWindow(config->consoleHandle, SW_HIDE); + }// 隐藏控制台窗口 + #endif break; default: _loadingWidget.setError(); diff --git a/src/launcher/GUI/paramEditorDialog.h b/src/launcher/GUI/paramEditorDialog.h new file mode 100644 index 0000000..27edfb4 --- /dev/null +++ b/src/launcher/GUI/paramEditorDialog.h @@ -0,0 +1,516 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class paramTableEditor : public QDialog { + Q_OBJECT + + +public: + explicit paramTableEditor(QWidget* parent = nullptr) + : QDialog(parent) { + this->setWindowTitle("JSON 表格编辑器"); + this->resize(700, 450); + setWindowFlags(this->windowFlags() | Qt::FramelessWindowHint);// 去掉边框 + //setAttribute(Qt::WA_TranslucentBackground); // 背景透明 + this->setObjectName("paramTableEditor"); + this->setStyleSheet(QString("QDialog#paramTableEditor {background-color: rgb(%1);border: 2px solid #000000;border-radius: 6px;}").arg(sColor_Blue)); + + m_tableView = new QTableView(this); + m_tableModel = new QStandardItemModel(0, 2, this);// 2列:选项、值 + m_tableView->setModel(m_tableModel); + m_tableView->setCornerButtonEnabled(false); + + // 设置无边框 + m_tableView->setFrameShape(QFrame::NoFrame); + m_tableView->setShowGrid(false); + + // 禁止拖动单元格大小 + m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); + m_tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); + + // 禁止点击表头 + m_tableView->horizontalHeader()->setSectionsClickable(false); + m_tableView->verticalHeader()->setSectionsClickable(false); + + // 禁止点击第一列的单元格 + m_tableView->setEditTriggers(QAbstractItemView::EditTrigger::DoubleClicked | QAbstractItemView::EditTrigger::EditKeyPressed); + m_tableView->setItemDelegateForColumn(0, new QStyledItemDelegate(m_tableView)); + m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_tableView->setSelectionMode(QAbstractItemView::SingleSelection); + m_tableView->setColumnWidth(0, 120); // 可根据实际调整 + + // 禁止第一列选中 + m_tableView->setColumnHidden(0, false); // 显示第一列 + m_tableView->setFocusPolicy(Qt::NoFocus); + + //设置表头 + m_tableModel->setHorizontalHeaderLabels({ "选项", "值" }); + + //设置表格属性 + m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_tableView->setAlternatingRowColors(true); + m_tableView->horizontalHeader()->setStretchLastSection(true); + m_tableView->setColumnWidth(1, 200);// 选项列宽度 + + + fancyButtonText* okBtn = new fancyButtonText("保存", 70, 22, ""); + fancyButtonText* cancelBtn = new fancyButtonText("取消", 70, 22, ""); + + connect(okBtn, &QPushButton::clicked, this, ¶mTableEditor::onAccept); + connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); + + QHBoxLayout* btnLayout = new QHBoxLayout(); + btnLayout->addStretch(); + btnLayout->addWidget(okBtn); + btnLayout->addWidget(cancelBtn); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->addWidget(m_tableView); + mainLayout->addLayout(btnLayout); + + m_tableView->setStyleSheet(QString(R"( + QTableView { + background-color: rgb(%1); + font-family: "Microsoft YaHei"; + color: #ffffff; + gridline-color: #555555; + alternate-background-color: #3a3a3a; + } + QTableView::item { + background-color: transparent; + } + QTableView::item:selected { + color: #ffffff; + background-color: transparent; + } + QTableView::item:alternate { + background-color: transparent; + } + QTableCornerButton::section { + background: transparent; +border: none; + } + )").arg(sColor_Blue)); + + QString headerStyle = QStringLiteral( + "QHeaderView {" + "background-color: transparent;" + "border: none;" + "}" + "QHeaderView::section {" + "background-color: transparent;" + "font-family: 'Microsoft YaHei';" + "font-weight: bold;" + "color: #ffffff;" + "border: none;" + "padding: 4px;" + "}" + ); + m_tableView->horizontalHeader()->setStyleSheet(headerStyle); + m_tableView->verticalHeader()->setStyleSheet(( + headerStyle + + "QHeaderView {" + "background-color: rgb(%1);" + "}" + ).arg(sColor_Blue));; + m_tableView->setCornerButtonEnabled(false); + + // 强制隐藏交叉方块区域 + m_tableView->horizontalHeader()->setFixedHeight( + m_tableView->horizontalHeader()->height() - 1); + m_tableView->verticalHeader()->setFixedWidth( + m_tableView->verticalHeader()->width() - 1); + + } + + void setParams(std::vector> params) { + m_tableModel->setRowCount(0); + m_editorWidgets.clear(); + + if (params.size() == 0) return; + + int row = 0; + for (auto data : params) { + m_tableModel->insertRow(row); + + const QString& key = data.first; + const QVariant& value = data.second; + + + // 设置键名(不可编辑) + QStandardItem* keyItem = new QStandardItem(key); + keyItem->setEditable(false); + keyItem->setBackground(QColor(60, 60, 60)); + keyItem->setForeground(QColor(255, 255, 255)); + m_tableModel->setItem(row, 0, keyItem); + + // 确定类型(内部使用,不显示) + m_originalTypes[key] = value.type(); + + //为特定类型创建专用编辑器控件 + QWidget* editorWidget = createEditorWidget(value, value.type()); + if (editorWidget) { + m_tableView->setIndexWidget(m_tableModel->index(row, 1), editorWidget); + m_editorWidgets[key] = editorWidget; + } + else { + // 默认为文本显示 + QString valueText= value.toString(); + QStandardItem* valueItem = new QStandardItem(valueText); + valueItem->setToolTip(generateTypeHint(value.type())); + m_tableModel->setItem(row, 1, valueItem); + } + row++; + } + } + +signals: + +private slots: + void onAccept() { + std::vector> result; + + for (int row = 0; row < m_tableModel->rowCount(); ++row) { + QStandardItem* keyItem = m_tableModel->item(row, 0); + if (!keyItem) { + QMessageBox::warning(this, "错误", QString("第 %1 行数据不完整").arg(row + 1)); + return; + } + + QString key = keyItem->text().trimmed(); + if (key.isEmpty()) { + QMessageBox::warning(this, "错误", QString("第 %1 行 key 为空").arg(row + 1)); + return; + } + + //获取原始类型 + if (m_originalTypes.find(key) == m_originalTypes.end()) { + QMessageBox::warning(this, "错误", QString("第 %1 行找不到原始类型信息").arg(row + 1)); + return; + } + + auto expectedType = m_originalTypes[key]; + + //从编辑器获取值 + QVariant convertedValue; + QString errorMsg; + if (!getValueFromEditor(key, expectedType, convertedValue, errorMsg)) { + QMessageBox::critical(this, "类型验证失败",QString("第 %1 行值验证失败:\n%2").arg(row + 1).arg(errorMsg)); + return; + } + //esult[keyStd] = convertedValue; + //result.push_back({ key, convertedValue }); + emit qtFuncBridge::getInstance()->editParam(key, convertedValue); + } + this->close(); + delete this; + //qDebug() << "Result JSON: " << QString::fromStdString(result.dump(4)); + + //emit jsonAccepted(result); + //accept(); + } + +private: + QTableView* m_tableView; + QStandardItemModel* m_tableModel; + nlohmann::json m_originalJson; // 保存原始JSON以便类型检查 + std::map m_originalTypes;// 保存每个键的原始类型 + std::map m_editorWidgets;// 保存每个键对应的编辑器控件 + + // 根据类型创建不同的编辑器控件 + QWidget* createEditorWidget(const QVariant& value, QVariant::Type type) { + switch (type) { + case QVariant::Type::Bool:{ + CircularSwitch* switchBtn = new CircularSwitch(); + switchBtn->setChecked(value.toBool()); + switchBtn->setToolTip(generateTypeHint(type)); + return switchBtn; + } + case QVariant::Type::Int: { + QSpinBox* spinBox = new QSpinBox(); + spinBox->setMaximumWidth(160); + spinBox->setRange(INT_MIN, INT_MAX); + spinBox->setValue(value.toInt()); + spinBox->setToolTip(generateTypeHint(type)); + return spinBox; + } + case QVariant::Type::Double: { + QDoubleSpinBox* spinBox = new QDoubleSpinBox(); + spinBox->setMaximumWidth(160); + spinBox->setDecimals(3);//显示小位数 + spinBox->setValue(value.toDouble()); + spinBox->setToolTip(generateTypeHint(type)); + return spinBox; + } + case QVariant::Type::String: { + QLineEdit* lineEdit = new QLineEdit(); + lineEdit->setText(value.toString()); + lineEdit->setToolTip(generateTypeHint(type)); + return lineEdit; + } + // 其他类型仍使用文本显示 + default: + return nullptr; + } + } + + bool getValueFromEditor(const QString& key, QVariant::Type expectedType, QVariant& result, QString& errorMsg) { + auto it = m_editorWidgets.find(key); + + try { + if (it != m_editorWidgets.end()) { + // 有专用编辑器控件 + QWidget* widget = it->second; + switch (expectedType) { + case QVariant::Type::Bool: { + CircularSwitch* switchBtn = qobject_cast(widget); + if (switchBtn) { + bool value = switchBtn->isChecked(); + result = value; + // 更新模型数据 + updateModelData(key, value ? "true" : "false"); + return true; + } + errorMsg = "布尔值控件类型错误"; + return false; + } + case QVariant::Type::Int: { + QSpinBox* spinBox = qobject_cast(widget); + if (spinBox) { + int value = spinBox->value(); + result = value; + updateModelData(key, QString::number(value)); + return true; + } + errorMsg = "数字控件INT类型错误"; + return false; + } + case QVariant::Type::Double: { + QDoubleSpinBox* spinBox = qobject_cast(widget); + if (spinBox) { + double value = spinBox->value(); + result = value; + updateModelData(key, QString::number(value)); + return true; + } + errorMsg = "数字控件INT类型错误"; + return false; + } + case QVariant::Type::String: { + QLineEdit* lineEdit = qobject_cast(widget); + if (lineEdit) { + QString value = lineEdit->text().trimmed(); + result = value; + updateModelData(key, value); + return true; + } + errorMsg = "字符串控件类型错误"; + return false; + } + //default: + // // 其他类型使用默认文本处理 + // return validateAndConvertValue(getTextFromCell(key), expectedType, result, errorMsg); + } + } + //else { + // // 没有专用编辑器控件,使用默认文本处理 + // return validateAndConvertValue(getTextFromCell(key), expectedType, result, errorMsg); + //} + } + catch (const std::exception& e) { + errorMsg = QString("获取值错误: %1").arg(e.what()); + return false; + } + } + + // 更新模型中的数据项 + void updateModelData(const QString& key, const QString& value) { + for (int row = 0; row < m_tableModel->rowCount(); ++row) { + QStandardItem* keyItem = m_tableModel->item(row, 0); + if (keyItem && keyItem->text() == key) { + QStandardItem* valueItem = m_tableModel->item(row, 1); + if (valueItem) { + valueItem->setText(value); + } + break; + } + } + } + + // 从单元格获取文本值(用于没有专用编辑器的类型) + QString getTextFromCell(const std::string& key) { + //查找键对应的行 + for (int row = 0; row < m_tableModel->rowCount(); ++row) { + QStandardItem* keyItem = m_tableModel->item(row, 0); + if (keyItem && keyItem->text().toStdString() == key) { + QStandardItem* valueItem = m_tableModel->item(row, 1); + if (valueItem) { + return valueItem->text(); + } + break; + } + } + return ""; + } + + // 获取类型的显示名称 + //QString getTypeDisplayName(JsonValueType type) { + // switch (type) { + // case JsonValueType::String: return "字符串"; + // case JsonValueType::Number: return "数字"; + // case JsonValueType::Boolean: return "布尔"; + // case JsonValueType::Array: return "数组"; + // case JsonValueType::Object: return "对象"; + // case JsonValueType::Null: return "空值"; + // default: return "未知"; + // } + //} + + // 格式化值用于显示 + /*QString formatValueForDisplay(const QVariant& value, QVariant::Type type) { + switch (type) { + case QVariant::Type::String: + return QString::fromStdString(value.get()); + case QVariant::Type::Int: + if (value.is_number_integer()) { + return QString::number(value.get()); + } else { + return QString::number(value.get()); + } + case JsonValueType::Boolean: + return value.get() ? "true" : "false"; + case JsonValueType::Array: + case JsonValueType::Object: + return QString::fromStdString(value.dump()); + case JsonValueType::Null: + return "null"; + default: + return QString::fromStdString(value.dump()); + } + }*/ + + // 生成类型提示 + QString generateTypeHint(QVariant::Type type) { + switch (type) { + case QVariant::Type::String: + return "输入字符串值(不需要引号)"; + case QVariant::Type::Int: + return "输入数字值(整数)"; + case QVariant::Type::Double: + return "输入数字值(小数)"; + case QVariant::Type::Bool: + return "输入布尔值(true 或 false)"; + default: + return "请输入有效的JSON值"; + } + } + + // 验证并转换值 + //bool validateAndConvertValue(const QString& input, JsonValueType expectedType, + // nlohmann::json& result, QString& errorMsg) { + // QString trimmedInput = input.trimmed(); + // + // // 长度检查 + // if (trimmedInput.size() > 10240) { + // errorMsg = "输入值过大(超过10240字符)"; + // return false; + // } + // + // try { + // switch (expectedType) { + // case JsonValueType::String: + // result = trimmedInput.toStdString(); + // return true; + // + // case JsonValueType::Number: { + // bool ok; + // // 先尝试整数 + // int64_t intVal = trimmedInput.toLongLong(&ok); + // if (ok) { + // result = intVal; + // return true; + // } + // // 再尝试浮点数 + // double doubleVal = trimmedInput.toDouble(&ok); + // if (ok && !std::isnan(doubleVal) && !std::isinf(doubleVal)) { + // result = doubleVal; + // return true; + // } + // errorMsg = "无效的数字格式"; + // return false; + // } + // + // case JsonValueType::Boolean: + // if (trimmedInput.toLower() == "true") { + // result = true; + // return true; + // } else if (trimmedInput.toLower() == "false") { + // result = false; + // return true; + // } + // errorMsg = "布尔值只能是 true 或 false"; + // return false; + // + // case JsonValueType::Null: + // if (trimmedInput.toLower() == "null") { + // result = nullptr; + // return true; + // } + // errorMsg = "空值只能是 null"; + // return false; + // + // case JsonValueType::Array: + // case JsonValueType::Object: { + // // 对于数组和对象,尝试解析JSON + // result = nlohmann::json::parse(trimmedInput.toStdString()); + // + // // 验证类型是否匹配 + // if (expectedType == JsonValueType::Array && !result.is_array()) { + // errorMsg = "期望数组格式,如:[1,2,3]"; + // return false; + // } + // if (expectedType == JsonValueType::Object && !result.is_object()) { + // errorMsg = "期望对象格式,如:{\"key\":\"value\"}"; + // return false; + // } + // return true; + // } + // + // default: + // errorMsg = "未知的数据类型"; + // return false; + // } + // } + // catch (const nlohmann::json::parse_error& e) { + // errorMsg = QString("JSON解析错误: %1").arg(e.what()); + // return false; + // } + // catch (const std::exception& e) { + // errorMsg = QString("转换错误: %1").arg(e.what()); + // return false; + // } + //} +}; diff --git a/src/launcher/GUI/uiModule/uiModule_calibration.h b/src/launcher/GUI/uiModule/uiModule_calibration.h new file mode 100644 index 0000000..1d68270 --- /dev/null +++ b/src/launcher/GUI/uiModule/uiModule_calibration.h @@ -0,0 +1,860 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//标定数据结构 +struct CalibrationData { + int pointNumber; + QPixmap image; + double x; + double y; + + CalibrationData(int num, const QPixmap& img, double coordX, double coordY) + : pointNumber(num), image(img), x(coordX), y(coordY) { + } +}; + +//坐标输入对话框 +class CoordinateInputDialog : public QDialog { + Q_OBJECT +public: + CoordinateInputDialog(double currentX = 0.0, double currentY = 0.0, QWidget* parent = nullptr) : QDialog(parent) { + setWindowTitle("输入坐标"); + setFixedSize(300, 150); + + QFormLayout* layout = new QFormLayout(this); + + xEdit = new QLineEdit(this); + xEdit->setPlaceholderText("请输入X坐标"); + if (currentX != 0.0) { + xEdit->setText(QString::number(currentX, 'f', 2)); + } + yEdit = new QLineEdit(this); + yEdit->setPlaceholderText("请输入Y坐标"); + if (currentY != 0.0) { + yEdit->setText(QString::number(currentY, 'f', 2)); + } + + layout->addRow("X坐标:", xEdit); + layout->addRow("Y坐标:", yEdit); + + QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + layout->addRow(buttonBox); + + connect(buttonBox, &QDialogButtonBox::accepted, this, &CoordinateInputDialog::validateAndAccept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + QDoubleValidator* validator = new QDoubleValidator(this); + xEdit->setValidator(validator); + yEdit->setValidator(validator); + + //连接回车键信号,方便快速输入 + connect(xEdit, &QLineEdit::returnPressed, this, [this]() {yEdit->setFocus(); }); + connect(yEdit, &QLineEdit::returnPressed, this, &CoordinateInputDialog::validateAndAccept); + + //初始焦点设置到第一个输入框 + xEdit->setFocus(); + xEdit->selectAll(); + } + + double getX() const { + bool ok; + QString text = xEdit->text(); + double value = text.toDouble(&ok); + return ok ? value : 0.0; + } + + double getY() const { + bool ok; + QString text = yEdit->text(); + double value = text.toDouble(&ok); + return ok ? value : 0.0; + } + +private slots: + void validateAndAccept() { + //检查输入是否有效 + bool xOk, yOk; + xEdit->text().toDouble(&xOk); + yEdit->text().toDouble(&yOk); + + if (!xOk || !yOk) { + QMessageBox::warning(this, "输入错误", "请输入有效的坐标数值"); + return; + } + accept(); + } + +private: + QLineEdit* xEdit; + QLineEdit* yEdit; +}; + +//九点标定图片区域 +class CalibrationPoint : public QWidget { + Q_OBJECT + Q_PROPERTY(float opacity READ opacity WRITE setOpacity) +public: + CalibrationPoint(int number, QWidget* parent = nullptr) + : QWidget(parent), m_number(number), m_hasImage(false), m_x(0.00), m_y(0.00) { + setFixedSize(240, 240); + setStyleSheet(R"( + CalibrationPoint { + background-color: rgb(70, 75, 80); + border: 2px solid rgb(100, 105, 110); + border-radius: 8px; + } + )"); + + // 创建闪烁效果 + m_effect = new QGraphicsOpacityEffect(this); + m_effect->setOpacity(1.0); + setGraphicsEffect(m_effect); + + m_animation = new QPropertyAnimation(m_effect, "opacity", this); + m_animation->setDuration(500); + m_animation->setStartValue(1.0); + m_animation->setEndValue(0.3); + m_animation->setLoopCount(-1); + } + + float opacity() const { return m_effect->opacity(); } + void setOpacity(float opacity) { m_effect->setOpacity(opacity); } + + void startBlink() { + m_animation->start(); + setStyleSheet(R"( + CalibrationPoint { + background-color: rgb(90, 95, 100); + border: 3px solid rgb(255, 200, 50); + border-radius: 8px; + } + )"); + } + + void stopBlink() { + m_animation->stop(); + m_effect->setOpacity(1.0); + if (m_hasImage) { + setStyleSheet(R"( + CalibrationPoint { + background-color: rgb(70, 75, 80); + border: 3px solid rgb(50, 200, 50); + border-radius: 8px; + } + )"); + } + else { + setStyleSheet(R"( + CalibrationPoint { + background-color: rgb(70, 75, 80); + border: 2px solid rgb(100, 105, 110); + border-radius: 8px; + } + )"); + } + } + + void setImage(const QPixmap& image) { + m_image = image; + m_hasImage = true; + update(); + } + + void clearImage() { + m_image = QPixmap(); + m_hasImage = false; + m_x = 0; + m_y = 0; + update(); + } + + bool hasImage() const { return m_hasImage; } + int number() const { return m_number; } + + void setCoordinates(double x, double y) { + m_x = x; + m_y = y; + update(); + } + + double getX() const { return m_x; } + double getY() const { return m_y; } + QPixmap getImage() const { return m_image; } +protected: + void paintEvent(QPaintEvent* event) override { + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + // 绘制背景 + painter.fillRect(rect(), QColor(70, 75, 80)); + + // 绘制钉子位置指示 + drawPinPosition(painter); + + if (m_hasImage && !m_image.isNull()) { + // 绘制图像 + QPixmap scaled = m_image.scaled(width() - 20, height() - 50, + Qt::KeepAspectRatio, Qt::SmoothTransformation); + painter.drawPixmap((width() - scaled.width()) / 2, 5, scaled); + + // 绘制坐标信息 + painter.setPen(Qt::yellow); + QFont font("微软雅黑", 10); + painter.setFont(font); + QString coordText = QString("X: %1, Y: %2").arg(m_x, 0, 'f', 2).arg(m_y, 0, 'f', 2); + painter.drawText(rect(), Qt::AlignBottom | Qt::AlignHCenter, coordText); + } + else { + // 绘制数字 + painter.setPen(Qt::white); + QFont font("微软雅黑", 24, QFont::Bold); + painter.setFont(font); + painter.drawText(rect(), Qt::AlignCenter, QString::number(m_number)); + } + + // 绘制边框 + QPen pen(QColor(100, 105, 110), 2); + painter.setPen(pen); + painter.drawRoundedRect(rect(), 8, 8); + + // 绘制状态指示 + if (m_hasImage) { + painter.setPen(QPen(QColor(50, 200, 50), 3)); + } + else if (m_animation->state() == QAbstractAnimation::Running) { + painter.setPen(QPen(QColor(255, 200, 50), 3)); + } + painter.drawRoundedRect(rect(), 8, 8); + } + +private: + void drawPinPosition(QPainter& painter) { + //根据数字确定钉子的位置 + QPointF center; + switch (m_number) { + case 1: center = QPointF(width() * 0.2, height() * 0.2); break; //左上 + case 2: center = QPointF(width() * 0.5, height() * 0.2); break; //中上 + case 3: center = QPointF(width() * 0.8, height() * 0.2); break; //右上 + case 4: center = QPointF(width() * 0.8, height() * 0.5); break; //中右 + case 5: center = QPointF(width() * 0.5, height() * 0.5); break; //中心 + case 6: center = QPointF(width() * 0.2, height() * 0.5); break; //中左 + case 7: center = QPointF(width() * 0.2, height() * 0.8); break; //左下 + case 8: center = QPointF(width() * 0.5, height() * 0.8); break; //中下 + case 9: center = QPointF(width() * 0.8, height() * 0.8); break; //右下 + default: center = QPointF(width() * 0.5, height() * 0.5); break; + } + + // 绘制两个白色同心圆表示钉子 + painter.setPen(QPen(Qt::white, 2)); + painter.setBrush(Qt::NoBrush); + painter.drawEllipse(center, 8, 8); // 外圆 + painter.drawEllipse(center, 4, 4); // 内圆 + + // 绘制位置标签 + painter.setPen(Qt::white); + QFont font("微软雅黑", 8); + painter.setFont(font); + QString positionText; + switch (m_number) { + case 1: positionText = "左上"; break; + case 2: positionText = "中上"; break; + case 3: positionText = "右上"; break; + case 4: positionText = "中右"; break; + case 5: positionText = "中心"; break; + case 6: positionText = "中左"; break; + case 7: positionText = "左下"; break; + case 8: positionText = "中下"; break; + case 9: positionText = "右下"; break; + } + painter.drawText(QRectF(center.x() - 20, center.y() + 15, 40, 20), + Qt::AlignCenter, positionText); + } + +private: + int m_number; + bool m_hasImage; + QPixmap m_image; + QGraphicsOpacityEffect* m_effect; + QPropertyAnimation* m_animation; + double m_x; + double m_y; +}; +//九点标定 +class calibration : public QWidget { + Q_OBJECT +public: + calibration() { + this->setWindowTitle("九点标定"); + this->setFixedSize(1980, 1000); + m_qtFuncBridge = qtFuncBridge::getInstance(); + +#pragma region 主界面 + + QVBoxLayout* vLayout = new QVBoxLayout(this); + vLayout->setContentsMargins(0, 0, 0, 0); + + // 标题 + QLabel* title = new QLabel("九点标定"); + title->setObjectName("titleLabel"); + title->setFixedHeight(25); + title->setAlignment(Qt::AlignCenter); + vLayout->addWidget(title); + + // 说明文字 + QLabel* description = new QLabel("请按照数字顺序依次拍摄标定点图像"); + description->setObjectName("descriptionLabel"); + description->setFixedHeight(20); + description->setAlignment(Qt::AlignCenter); + vLayout->addWidget(description); + + // 九宫格主区域 + QWidget* mainWidget = new QWidget(); + mainWidget->setObjectName("mainWidget"); + QGridLayout* gridLayout = new QGridLayout(mainWidget); + gridLayout->setSpacing(10); + gridLayout->setContentsMargins(20, 20, 20, 20); + + //创建九点标定点 + int order[9] = { 1, 2, 3, 6, 5, 4, 7, 8, 9 }; + for (int i = 0; i < 9; i++) { + CalibrationPoint* point = new CalibrationPoint(order[i]); + m_points.append(point); + gridLayout->addWidget(point, i / 3, i % 3); + } + + vLayout->addWidget(mainWidget); + + // 控制按钮区域 + QWidget* controlWidget = new QWidget(); + controlWidget->setFixedHeight(50); + QHBoxLayout* controlLayout = new QHBoxLayout(controlWidget); + + m_captureBtn = new QPushButton("拍摄当前点"); + m_captureBtn->setObjectName("captureButton"); + m_captureBtn->setFixedSize(120, 40); + + m_prevBtn = new QPushButton("上一个"); + m_prevBtn->setObjectName("controlButton"); + m_prevBtn->setFixedSize(80, 35); + + m_nextBtn = new QPushButton("下一个"); + m_nextBtn->setObjectName("controlButton"); + m_nextBtn->setFixedSize(80, 35); + + m_modifyCoordBtn = new QPushButton("修改坐标"); + m_modifyCoordBtn->setObjectName("controlButton"); + m_modifyCoordBtn->setFixedSize(80, 35); + + m_retakeCurrentBtn = new QPushButton("重新拍摄当前点"); + m_retakeCurrentBtn->setObjectName("retakeButton"); + m_retakeCurrentBtn->setFixedSize(120, 35); + + m_resetBtn = new QPushButton("全部重新拍照"); + m_resetBtn->setObjectName("resetButton"); + m_resetBtn->setFixedSize(100, 35); + + controlLayout->addStretch(); + controlLayout->addWidget(m_modifyCoordBtn); + controlLayout->addWidget(m_prevBtn); + controlLayout->addWidget(m_captureBtn); + controlLayout->addWidget(m_nextBtn); + controlLayout->addWidget(m_retakeCurrentBtn); + controlLayout->addWidget(m_resetBtn); + controlLayout->addStretch(); + + vLayout->addWidget(controlWidget); + + // 状态显示 + m_statusLabel = new QLabel("准备开始标定"); + m_statusLabel->setObjectName("statusLabel"); + m_statusLabel->setFixedHeight(30); + m_statusLabel->setAlignment(Qt::AlignCenter); + vLayout->addWidget(m_statusLabel); + + // 设置样式 + this->setStyleSheet(R"( + QWidget#mainWidget { + background-color: rgb(50, 55, 60); + border-radius: 10px; + border: 1px solid rgb(70, 75, 80); + } + QLabel#titleLabel { + color: red; + font-family: "微软雅黑"; + font-size: 24px; + font-weight: bold; + } + QLabel#descriptionLabel { + color: red; + font-family: "微软雅黑"; + font-size: 14px; + } + QLabel#statusLabel { + color: rgb(255, 255, 100); + font-family: "微软雅黑"; + font-size: 16px; + font-weight: bold; + padding: 8px; + background-color: rgb(60, 65, 70); + border-radius: 5px; + } + QPushButton#captureButton { + background-color: rgb(0, 120, 215); + color: white; + border: none; + border-radius: 5px; + font-family: "微软雅黑"; + font-size: 14px; + font-weight: bold; + } + QPushButton#captureButton:hover { + background-color: rgb(0, 140, 235); + } + QPushButton#captureButton:pressed { + background-color: rgb(0, 100, 195); + } + QPushButton#captureButton:disabled { + background-color: rgb(80, 85, 90); + color: rgb(150, 150, 150); + } + QPushButton#controlButton { + background-color: rgb(80, 85, 90); + color: white; + border: 1px solid rgb(100, 105, 110); + border-radius: 5px; + font-family: "微软雅黑"; + font-size: 12px; + } + QPushButton#controlButton:hover { + background-color: rgb(90, 95, 100); + } + QPushButton#controlButton:disabled { + background-color: rgb(60, 65, 70); + color: rgb(150, 150, 150); + } + QPushButton#retakeButton { + background-color: rgb(180, 120, 60); + color: white; + border: none; + border-radius: 5px; + font-family: "微软雅黑"; + font-size: 12px; + } + QPushButton#retakeButton:hover { + background-color: rgb(200, 140, 80); + } + QPushButton#retakeButton:disabled { + background-color: rgb(80, 85, 90); + color: rgb(150, 150, 150); + } + QPushButton#resetButton { + background-color: rgb(180, 60, 60); + color: white; + border: none; + border-radius: 5px; + font-family: "微软雅黑"; + font-size: 12px; + } + QPushButton#resetButton:hover { + background-color: rgb(200, 80, 80); + } + )"); + + // 初始化状态 + m_currentPoint = 0; + updateUI(); + + //连接信号槽 + connect(m_captureBtn, &QPushButton::clicked, this, &calibration::captureImage);//触发拍照按钮 + connect(m_prevBtn, &QPushButton::clicked, this, &calibration::previousPoint);//上一个按钮 + connect(m_nextBtn, &QPushButton::clicked, this, &calibration::nextPoint);//下一个按钮 + connect(m_modifyCoordBtn, &QPushButton::clicked, this, &calibration::modifyCoordinates);//修改坐标按钮 + connect(m_retakeCurrentBtn, &QPushButton::clicked, this, &calibration::retakeCurrentPoint);//重新拍摄当前点 + connect(m_resetBtn, &QPushButton::clicked, this, &calibration::resetCalibration);//全部重新拍照按钮 +#pragma endregion + + + + +} + +private slots: + void captureImage() { + if (m_currentPoint >= 0 && m_currentPoint < m_points.size()) { + int displayIndex = getDisplayIndex(m_currentPoint); + + // 检查是否已经拍摄过 + if (m_points[displayIndex]->hasImage()) { + m_statusLabel->setText(QString("第 %1 个标定点已经拍摄过了").arg(m_currentPoint + 1)); + return; + } + + //调用相机拍照 + QPixmap capturedImage = CameraCapture(); + + //显示拍摄的图像 + m_points[displayIndex]->setImage(capturedImage); + m_points[displayIndex]->stopBlink(); + + //弹出坐标输入对话框 + CoordinateInputDialog dialog(0.0, 0.0, this); + if (dialog.exec() == QDialog::Accepted) { + double x = dialog.getX(); + double y = dialog.getY(); + m_points[displayIndex]->setCoordinates(x, y); + + + // 更新状态 + m_statusLabel->setText(QString("已拍摄第 %1 个标定点").arg(m_currentPoint + 1)); + + // 自动移动到下一个未拍摄的点 + bool foundNext = false; + for (int i = m_currentPoint + 1; i < 9; i++) { + int nextDisplayIndex = getDisplayIndex(i); + if (!m_points[nextDisplayIndex]->hasImage()) { + m_currentPoint = i; + foundNext = true; + break; + } + } + + if (!foundNext) { + // 所有点都拍摄完成 + m_statusLabel->setText("九点标定完成!"); + m_captureBtn->setEnabled(false); + } + } + else { + //用户取消了坐标输入,清除图像 + m_points[displayIndex]->clearImage(); + m_statusLabel->setText(QString("已取消第 %1 个标定点的坐标输入").arg(m_currentPoint + 1)); + } + updateUI(); + } + } + + //修改坐标函数 + void modifyCoordinates() { + if (m_currentPoint >= 0 && m_currentPoint < 9) { + int displayIndex = getDisplayIndex(m_currentPoint); + + if (!m_points[displayIndex]->hasImage()) { + m_statusLabel->setText(QString("第 %1 个标定点没有图像,无法修改坐标").arg(m_currentPoint + 1)); + return; + } + + // 弹出坐标输入对话框,传入当前坐标值 + CoordinateInputDialog dialog(m_points[displayIndex]->getX(), + m_points[displayIndex]->getY(), this); + if (dialog.exec() == QDialog::Accepted) { + double x = dialog.getX(); + double y = dialog.getY(); + m_points[displayIndex]->setCoordinates(x, y); + m_statusLabel->setText(QString("已修改第 %1 个标定点的坐标").arg(m_currentPoint + 1)); + } + } + } + + //上一个按钮 + void previousPoint() { + if (m_currentPoint > 0) { + int displayIndex = getDisplayIndex(m_currentPoint); + m_points[displayIndex]->stopBlink(); + + // 找到前一个可导航的点 + for (int i = m_currentPoint - 1; i >= 0; i--) { + m_currentPoint = i; + break; + } + + updateUI(); + } + } + + //下一个按钮 + void nextPoint() { + if (m_currentPoint < 8) { + int displayIndex = getDisplayIndex(m_currentPoint); + m_points[displayIndex]->stopBlink(); + + // 找到后一个可导航的点 + for (int i = m_currentPoint + 1; i < 9; i++) { + m_currentPoint = i; + break; + } + + updateUI(); + } + } + + //重新拍摄当前点按钮 + void retakeCurrentPoint() { + if (m_currentPoint >= 0 && m_currentPoint < 9) { + int displayIndex = getDisplayIndex(m_currentPoint); + + // 弹出选项对话框,让用户选择重新拍摄还是仅修改坐标 + QMessageBox msgBox(this); + msgBox.setWindowTitle("重新拍摄选项"); + msgBox.setText("请选择操作:"); + msgBox.setIcon(QMessageBox::Question); + + QPushButton* retakeButton = msgBox.addButton("重新拍摄图像", QMessageBox::ActionRole); + QPushButton* modifyButton = msgBox.addButton("仅修改坐标", QMessageBox::ActionRole); + QPushButton* cancelButton = msgBox.addButton("取消", QMessageBox::RejectRole); + + msgBox.exec(); + + if (msgBox.clickedButton() == retakeButton) { + //重新拍摄图像 + QPixmap capturedImage = CameraCapture(); + m_points[displayIndex]->setImage(capturedImage); + + //重新输入坐标 + CoordinateInputDialog dialog(m_points[displayIndex]->getX(), + m_points[displayIndex]->getY(), this); + if (dialog.exec() == QDialog::Accepted) { + double x = dialog.getX(); + double y = dialog.getY(); + m_points[displayIndex]->setCoordinates(x, y); + } + + m_statusLabel->setText(QString("已重新拍摄第 %1 个标定点").arg(m_currentPoint + 1)); + } + else if (msgBox.clickedButton() == modifyButton) { + //仅修改坐标 + CoordinateInputDialog dialog(m_points[displayIndex]->getX(), + m_points[displayIndex]->getY(), this); + if (dialog.exec() == QDialog::Accepted) { + double x = dialog.getX(); + double y = dialog.getY(); + m_points[displayIndex]->setCoordinates(x, y); + m_statusLabel->setText(QString("已修改第 %1 个标定点的坐标").arg(m_currentPoint + 1)); + } + } + updateUI(); + } + } + + //全部重新拍照按钮 + void resetCalibration() { + QMessageBox confirmBox(this); + confirmBox.setWindowTitle("确认重新拍摄"); + confirmBox.setText("确定要全部重新拍摄吗?\n这将清除所有已拍摄的图像和坐标数据!"); + confirmBox.setIcon(QMessageBox::Warning); + confirmBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + confirmBox.setDefaultButton(QMessageBox::No); + + if (confirmBox.exec() == QMessageBox::Yes) { + // 停止所有闪烁 + for (CalibrationPoint* point : m_points) { + point->stopBlink(); + } + + // 重置所有点 + for (CalibrationPoint* point : m_points) { + point->clearImage(); + } + + // 重置状态 + m_currentPoint = 0; + m_captureBtn->setEnabled(true); + updateUI(); + + m_statusLabel->setText("已重置所有标定点,请重新开始标定"); + } + } +private: + // 获取当前导航点对应的显示索引 + int getDisplayIndex(int navIndex) { + static int mapping[9] = { 0, 1, 2, 5, 4, 3, 6, 7, 8 }; + return mapping[navIndex]; + } + + void updateUI() { + // 停止所有点的闪烁 + for (CalibrationPoint* point : m_points) { + point->stopBlink(); + } + + // 开始当前点的闪烁 + if (m_currentPoint >= 0 && m_currentPoint < 9) { + int displayIndex = getDisplayIndex(m_currentPoint); + m_points[displayIndex]->startBlink(); + + // 更新状态文字 + if (m_points[displayIndex]->hasImage()) { + m_statusLabel->setText(QString("第 %1 个标定点已拍摄,可重新拍摄").arg(m_currentPoint + 1)); + // 如果当前点已拍摄,启用重新拍摄当前点按钮 + m_retakeCurrentBtn->setEnabled(true); + } + else { + m_statusLabel->setText(QString("请拍摄第 %1 个标定点").arg(m_currentPoint + 1)); + // 如果当前点未拍摄,禁用重新拍摄当前点按钮 + m_retakeCurrentBtn->setEnabled(false); + } + } + + // 更新按钮状态 + m_prevBtn->setEnabled(m_currentPoint > 0); + m_nextBtn->setEnabled(m_currentPoint < 8); + + // 更新拍摄按钮状态 + int displayIndex = getDisplayIndex(m_currentPoint); + bool canCapture = !m_points[displayIndex]->hasImage(); + m_captureBtn->setEnabled(canCapture); + + // 更新修改坐标按钮状态 + bool canModify = m_points[displayIndex]->hasImage(); + m_modifyCoordBtn->setEnabled(canModify); + + // 检查是否所有点都已完成 + bool allCompleted = true; + for (int i = 0; i < 9; i++) { + int idx = getDisplayIndex(i); + if (!m_points[idx]->hasImage()) { + allCompleted = false; + break; + } + } + + if (allCompleted) { + m_statusLabel->setText("九点标定完成!正在处理数据..."); + m_captureBtn->setEnabled(false); + + //自动处理标定数据 + QTimer::singleShot(100, this, [this]() {processCalibrationData(); }); + } + } + + //相机拍照 + QPixmap CameraCapture() { + // 模拟相机拍照 + std::vector imgs; + emit m_qtFuncBridge->triggerAndCaptureImage("Camera1", &imgs); + int cnt = 0; + while (imgs.size() == 0 && cnt < 30) { + QThread::msleep(100); + cnt++; + } + if (imgs.size() == 0)return QPixmap (200, 150);; + QPixmap pixmap = cvMatToQPixMap(imgs[0]); + + QPainter painter(&pixmap); + painter.setRenderHint(QPainter::Antialiasing); + + // 绘制中心十字 + painter.setPen(QPen(Qt::white, 2)); + painter.drawLine(100, 50, 100, 100); + painter.drawLine(75, 75, 125, 75); + + //绘制数字 + painter.setPen(Qt::white); + QFont font("Arial", 16, QFont::Bold); + painter.setFont(font); + painter.drawText(pixmap.rect(), Qt::AlignCenter, + QString::number(m_currentPoint + 1)); + + return pixmap; + } + + //处理标定数据的函数 + void processCalibrationData() { + std::vector calibrationData; + + // 收集所有已拍摄点的数据 + for (int i = 0; i < 9; i++) { + int displayIndex = getDisplayIndex(i); + CalibrationPoint* point = m_points[displayIndex]; + + if (point->hasImage()) { + calibrationData.emplace_back( + point->number(), + point->getImage(), + point->getX(), + point->getY() + ); + } + } + + // 调用标定算法或处理函数 + if (!calibrationData.empty()) { + performCalibration(calibrationData); + } + } + + // 执行标定计算的函数 + void performCalibration(const std::vector& data) { + //标定算法 + // data 包含所有标定点的编号、图像和坐标信息 + + qDebug() << "开始标定计算,共有" << data.size() << "个标定点"; + + for (const auto& item : data) { + qDebug() << "标定点" << item.pointNumber + << ": X=" << item.x << ", Y=" << item.y + << ", 图像尺寸:" << item.image.size(); + + //添加标定算法 + //相机标定、坐标转换等 + } + + // 标定完成后的处理 + m_statusLabel->setText("标定计算完成!"); + + //添加标定结果保存、应用等逻辑 + saveCalibrationResults(data); + } + + // 保存标定结果 + void saveCalibrationResults(const std::vector& data) { + //实现标定结果的保存逻辑 + //例如保存到文件、数据库或发送到其他模块 + + qDebug() << "保存标定结果..."; + //保存逻辑 + } + +protected: + //重写showEvent,使窗口显示在屏幕中心 + void showEvent(QShowEvent* event) override { + QWidget::showEvent(event); + //获取屏幕的几何信息 + QScreen* screen = QGuiApplication::primaryScreen(); + QRect screenGeometry = screen->geometry(); + + int x = (screenGeometry.width() - this->width()) / 2; + int y = (screenGeometry.height() - this->height()) / 2; + this->move(x, y); + } + +private: + qtFuncBridge* m_qtFuncBridge; + QVector m_points; + QPushButton* m_captureBtn; + QPushButton* m_prevBtn; + QPushButton* m_nextBtn; + QPushButton* m_modifyCoordBtn; + QPushButton* m_retakeCurrentBtn; + QPushButton* m_resetBtn; + QLabel* m_statusLabel; + int m_currentPoint; +}; \ No newline at end of file diff --git a/src/launcher/GUI/uiModule/uiModule_cameraRealTimeCapture.h b/src/launcher/GUI/uiModule/uiModule_cameraRealTimeCapture.h new file mode 100644 index 0000000..e498be6 --- /dev/null +++ b/src/launcher/GUI/uiModule/uiModule_cameraRealTimeCapture.h @@ -0,0 +1,124 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//实时取流线程 +class GrabbingThread :public QThread { +public: + GrabbingThread(QLabel* showImageLabelIN, QMutex* mutex) { + showImageLabel = showImageLabelIN; + } + void setCameraName(std::string cameraNameIN) { + cameraName = cameraNameIN; + } + void run() { + m_runFlag = true; + while (m_runFlag == true) { + QMutexLocker locker(mutex); + std::vector mats; + emit qtFuncBridge::getInstance()->triggerAndCaptureImage(cameraName, &mats); + for (auto mat : mats) { + auto pixmap = cvMatToQPixMap(mat); + showImageLabel->setPixmap(pixmap.scaled(showImageLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); + + } + } + } + void stop() { + m_runFlag = false; + } +private: + bool m_runFlag = false; + QLabel* showImageLabel = nullptr; + std::string cameraName; + QMutex* mutex; +}; + +class cameraRealTimeCapture : public QWidget { + Q_OBJECT +public: + GrabbingThread* _GrabbingThread; + QMutex mutex; + cameraRealTimeCapture() { +#pragma region 实时取流 + QVBoxLayout* vLayout2 = new QVBoxLayout(this); + QComboBox* cameraSelectBox = new QComboBox(); + vLayout2->addWidget(cameraSelectBox, 0, Qt::AlignTop); + + QPushButton* refreshCameraBtn = new QPushButton("刷新相机"); + vLayout2->addWidget(refreshCameraBtn, 0, Qt::AlignTop); + + QPushButton* startGrabbing = new QPushButton("开始取流"); + startGrabbing->setDisabled(true); + vLayout2->addWidget(startGrabbing, 0, Qt::AlignTop); + + QPushButton* saveImageBtn = new QPushButton("保存图像"); + saveImageBtn->setDisabled(true); + vLayout2->addWidget(saveImageBtn, 0, Qt::AlignTop); + + connect(refreshCameraBtn, &QPushButton::clicked, [=]() { + cameraSelectBox->clear(); + startGrabbing->setEnabled(true); + saveImageBtn->setEnabled(true); + std::vector < std::string> cameraNamesVec; + emit qtFuncBridge::getInstance()->getCameraNames(&cameraNamesVec); + for (auto cameraName : cameraNamesVec) { + cameraSelectBox->addItem(QString::fromStdString(cameraName)); + } + }); + + QLabel* showNowTimeImageLabel = new QLabel; + vLayout2->addWidget(showNowTimeImageLabel, 1, Qt::AlignTop); + + showNowTimeImageLabel->setText("实时取流"); + showNowTimeImageLabel->setStyleSheet(" border - radius: 10px; border: 1px solid rgb(70, 75, 80);"); + showNowTimeImageLabel->setFixedSize(800, 800); + _GrabbingThread = new GrabbingThread(showNowTimeImageLabel,&mutex); + connect(startGrabbing, &QPushButton::clicked, [=]() { + if (_GrabbingThread->isRunning() == false) { + showNowTimeImageLabel->clear(); + _GrabbingThread->setCameraName(cameraSelectBox->currentText().toStdString()); + _GrabbingThread->start(); + startGrabbing->setText("停止取流"); + } + else { + _GrabbingThread->stop(); + startGrabbing->setText("开始取流"); + } + }); + connect(this, &cameraRealTimeCapture::close, [=]() { + _GrabbingThread->stop(); + startGrabbing->setText("开始取流"); + }); + + // 保存图像功能:保存当前显示的图像到文件 + connect(saveImageBtn, &QPushButton::clicked, [=]() { + QMutexLocker locker(&mutex); + const QPixmap* pix = showNowTimeImageLabel->pixmap(); + if (!pix || pix->isNull()) { + QMessageBox::warning(this, "提示", "没有可保存的图像"); + return; + } + QString ts = QDateTime::currentDateTime().toString("yyyyMMdd_HHmmsszzz"); + QString suggested = QString("snapshot_%1.png").arg(ts); + QString filePath = QFileDialog::getSaveFileName(this, "保存图像", QDir::homePath() + "/" + suggested, + "PNG (*.png);;JPEG (*.jpg *.jpeg);;BMP (*.bmp)"); + if (filePath.isEmpty()) return; + if (!pix->save(filePath)) { + QMessageBox::warning(this, "保存失败", "无法保存图像到所选位置"); + } + }); + +#pragma endregion + } +}; \ No newline at end of file diff --git a/src/launcher/GUI/uiModule/uiModule_imageView.h b/src/launcher/GUI/uiModule/uiModule_imageView.h index 2db683f..798d3fd 100644 --- a/src/launcher/GUI/uiModule/uiModule_imageView.h +++ b/src/launcher/GUI/uiModule/uiModule_imageView.h @@ -260,10 +260,10 @@ private: float scaleY = static_cast(imageHeight) / FOV.height(); double currentScale = transform().m11(); - if ((factor.y() > 0 && currentScale > 10.0) || (factor.y() < 0 && currentScale < 0.1)) + if ((factor.y() > 0 && currentScale > 10.0) || (factor.y() < 0 && currentScale < 1)) return; - scale(factor.y() > 0 ? 1.2 : 0.8, factor.y() > 0 ? 1.2 : 0.8); + scale(factor.y() > 0 ? 1.1 : 0.9, factor.y() > 0 ? 1.1 : 0.9); } void togglePan(bool pan, const QPoint& startPos = QPoint()) { diff --git a/src/launcher/GUI/uiModule/uiModule_runCommand.h b/src/launcher/GUI/uiModule/uiModule_runCommand.h index 0184918..537cdc0 100644 --- a/src/launcher/GUI/uiModule/uiModule_runCommand.h +++ b/src/launcher/GUI/uiModule/uiModule_runCommand.h @@ -2,9 +2,13 @@ #include #include #include +#include +#include +#include + class plcValueViewer :public QWidget{ public: - QPalette palette; + QFont font1 = QFont("微软雅黑", 12, QFont::Bold); QFont font2 = QFont("微软雅黑", 10, QFont::Bold); QFont font3 = QFont("微软雅黑", 6, QFont::Bold); @@ -13,26 +17,31 @@ public: QMap>& plcViwerLineedits) :groupName(groupNameIN){ - palette.setColor(QPalette::WindowText, Qt::white); QLabel* groupNameLabel = new QLabel(groupNameIN); groupNameLabel->setFont(font1); - groupNameLabel->setPalette(palette); + groupNameLabel->setStyleSheet("color: white;"); + //groupNameLabel->setPalette(palette); layout->addWidget(groupNameLabel); for (auto& data : viewerNameAndAddress) { plcViwerLineedits[data.second].push_back(addViewer(data.first)); } - widget->setMaximumHeight(100); + widget->setMaximumHeight(800); } QLabel* addViewer(const QString& viewerName) { + //QPalette palette; + //palette.setColor(QPalette::WindowText, Qt::white); + map.insert(groupName + viewerName ,new QLabel("*")); auto label = map[groupName + viewerName]; label->setFont(font2); - label->setPalette(palette); + label->setStyleSheet("color: white;"); + //label->setPalette(palette); auto textLabel = new QLabel(viewerName); textLabel->setFont(font3); - textLabel->setPalette(palette); + textLabel->setStyleSheet("color: white;"); + //textLabel->setPalette(palette); QVBoxLayout* viewerLayout = new QVBoxLayout(); viewerLayout->addWidget(label,Qt::AlignCenter); @@ -55,31 +64,27 @@ public: this->setWindowTitle("执行命令"); this->setObjectName("runCommandWidget"); this->setStyleSheet(QString("QDialog#runCommandWidget {background-color: rgb(%1);border: 2px solid #000000;border-radius: 6px;}").arg(sColor_Blue)); - this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); - + btnWidgets = new QListWidget(this); btnWidgets->setStyleSheet("background: transparent;"); - QWidget* listContainer = new QWidget(this); - plcValueViewerLayout = new QVBoxLayout(listContainer); - plcValueViewerLayout->setContentsMargins(0, 0, 0, 0); - plcValueViewerLayout->setSpacing(6); - listContainer->setLayout(plcValueViewerLayout); - listContainer->setStyleSheet("background: transparent;"); + // 使用 QListView 包裹 plcValueViewer + plcValueViewerList = new QListView(this); + plcValueViewerList->setStyleSheet("background: transparent;"); + plcValueViewerList->setEditTriggers(QAbstractItemView::NoEditTriggers); + plcValueViewerList->setSelectionMode(QAbstractItemView::NoSelection); + plcValueViewerList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + plcValueViewerList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + plcValueViewerList->setResizeMode(QListView::Adjust); - QScrollArea* scrollArea = new QScrollArea(this); - scrollArea->setWidgetResizable(true); - scrollArea->setFrameShape(QFrame::NoFrame); - scrollArea->setWidget(listContainer); - scrollArea->setStyleSheet("background: transparent;"); + plcValueViewerModel = new QStandardItemModel(plcValueViewerList); + plcValueViewerList->setModel(plcValueViewerModel); QGridLayout* layout = new QGridLayout(this); layout->addWidget(btnWidgets, 0, 0); - layout->addWidget(scrollArea, 0, 1); + layout->addWidget(plcValueViewerList, 0, 1); - this->setFixedWidth(sizeX); - this->setMinimumHeight(sizeY); - //this->setMaximumHeight(sizeY); + this->setFixedSize(sizeX, sizeY); } signals: @@ -104,19 +109,40 @@ public slots: } }); } - void addPlcValueViewer(const QString& groupName,std::vector> viewerNameAndAddress) { - auto widget = new plcValueViewer(groupName, viewerNameAndAddress, plcViwerLineedits); - plcValueViewerLayout->addWidget(widget->widget,Qt::AlignTop); + void addPlcValueViewer(QString groupName, std::vector> viewerNameAndAddress) { + auto viewer = new plcValueViewer(groupName, viewerNameAndAddress, plcViwerLineedits); + // 添加到 QListView 中 + QStandardItem* item = new QStandardItem(); + // 根据内部 widget 的 sizeHint 设置行高 + item->setSizeHint(viewer->widget->sizeHint()); + plcValueViewerModel->appendRow(item); + QModelIndex index = plcValueViewerModel->index(plcValueViewerModel->rowCount() - 1, 0); + plcValueViewerList->setIndexWidget(index, viewer->widget); } - void refreshPlcValueViewer(int address, int value) { + void refreshPlcValueViewer(int address, QString value) { for (auto& lineedit : plcViwerLineedits[address]) { - lineedit->setText(QString::number(value)); + lineedit->setText(value); } } +protected: + //重写showEvent,使窗口显示在屏幕中心 + void showEvent(QShowEvent* event) override { + QWidget::showEvent(event); + //获取屏幕的几何信息 + QScreen* screen = QGuiApplication::primaryScreen(); + QRect screenGeometry = screen->geometry(); + + int x = (screenGeometry.width() - this->width()) / 2; + int y = (screenGeometry.height() - this->height()) / 2; + this->move(x, y); + } + private: - int sizeX = 1000, sizeY = 800; + int sizeX = 1000, sizeY = 720; QListWidget* btnWidgets; - QVBoxLayout* plcValueViewerLayout; + // 用 QListView + 模型 替代原来的垂直布局 + QListView* plcValueViewerList; + QStandardItemModel* plcValueViewerModel; QMap>plcViwerLineedits; }; #pragma once \ No newline at end of file diff --git a/src/launcher/GUI/uiModule/uiModule_softwareDataDisplay.h b/src/launcher/GUI/uiModule/uiModule_softwareDataDisplay.h index 311fe53..aff8368 100644 --- a/src/launcher/GUI/uiModule/uiModule_softwareDataDisplay.h +++ b/src/launcher/GUI/uiModule/uiModule_softwareDataDisplay.h @@ -77,7 +77,7 @@ public: "QLineEdit { background-color: #333; color: white; border-radius: 4px; padding: 2px; } " "QLabel { color: white; } " ); - this->setFixedWidth(200); + //this->setFixedHeight(150); } protected: @@ -118,7 +118,7 @@ private slots: public slots: void onTriggerSateChanged(int loopCounterPerMin, int maxInterval) { int activeThreadCount = QThreadPool::globalInstance()->activeThreadCount(); - triggerState->setText(QString::number(loopCounterPerMin) + "L " + QString::number(maxInterval) + "ms " + QString::number(activeThreadCount) + "T"); + triggerState->setText(QString::number(loopCounterPerMin) + "L " + QString::number(maxInterval) + "ms " + QString::number(activeThreadCount) +"/" + QString::number(QThreadPool::globalInstance()->maxThreadCount()) + "T"); } private: QTimer* timer; diff --git a/src/launcher/appConfig/appConfig.cpp b/src/launcher/appConfig/appConfig.cpp index 6ccfe95..786c9e8 100644 --- a/src/launcher/appConfig/appConfig.cpp +++ b/src/launcher/appConfig/appConfig.cpp @@ -1,4 +1,4 @@ -//#include +//#include #include #include namespace fs = std::filesystem; @@ -15,7 +15,15 @@ void appConfig::parsingSettingJson() objectSelect = settingJson["objectSelect"]; loginPassWord = settingJson["loginPassWord"]; + missionJsonPath = appWorkPath.string() + "/config/" + objectSelect + ".json"; + + if (QFile(QString::fromStdString(missionJsonPath)).exists() == false) { + std::cout << "Mission config file not found: " << (appWorkPath.string() + "/config/" + objectSelect + ".json") << std::endl; + std::cout << "任务配置文件未找到: " << (appWorkPath.string() + "/config/" + objectSelect + ".json") << std::endl; + exit(1); + } missionJson = readOrderedJsonFromPath(appWorkPath.string() + "/config/" + objectSelect +".json"); + softwareTile = missionJson["softwareTile"]; imgSavePath = missionJson["backend"]["imgSaveBasePath"]; //viewer @@ -29,8 +37,7 @@ void appConfig::parsingSettingJson() v.viewers.assign(data.value()["viewer"].begin(), data.value()["viewer"].end()); viewDatas.push_back(v); } - missionJsonPath = appWorkPath.string() + "/config/" + objectSelect + ".json"; - + if (missionJson["backend"]["missionSettings"].contains("存储原图") == false) { missionJson["backend"]["missionSettings"]["存储原图"] = true; } @@ -43,4 +50,10 @@ void appConfig::parsingSettingJson() if (missionJson["backend"]["missionSettings"].contains("存储Cls图") == false) { missionJson["backend"]["missionSettings"]["存储Cls图"] = true; } + if (missionJson["backend"]["missionSettings"].contains("原图存图天数") == false) { + missionJson["backend"]["missionSettings"]["原图存图天数"] = true; + } + if (missionJson["backend"]["missionSettings"].contains("结果图存图天数") == false) { + missionJson["backend"]["missionSettings"]["结果图存图天数"] = true; + } } diff --git a/src/launcher/appConfig/auth.hpp b/src/launcher/appConfig/auth.hpp index 5942fc1..ae2e32e 100644 --- a/src/launcher/appConfig/auth.hpp +++ b/src/launcher/appConfig/auth.hpp @@ -127,9 +127,8 @@ namespace Auth { } std::string hashPassword(const std::string& password) { - // 简单的密码哈希实现(实际项目中应该使用更安全的哈希算法如bcrypt) std::hash hasher; - size_t hashValue = hasher(password + "salt_string_2023"); + size_t hashValue = hasher(password + "waibibabu_xixihaha"); return std::to_string(hashValue); } diff --git a/src/launcher/appCore.hpp b/src/launcher/appCore.hpp index 115522a..390de44 100644 --- a/src/launcher/appCore.hpp +++ b/src/launcher/appCore.hpp @@ -17,11 +17,10 @@ public: HMODULE hDll; bool loadCore(SQLiteDB* db, qtFuncBridge* bridgeIn, nlohmann::ordered_json& jsonIn) { bridge = bridgeIn; - json = jsonIn; hDll = LoadLibrary("core.dll"); if (!hDll) { - std::cerr << "Failed to load DLL!" << std::endl; - return false; + std::cerr << "Failed to load Core DLL!" << std::endl; + exit(1); } CreateBackend = (CreateBackendFunc)GetProcAddress(hDll, "CreateBackend"); @@ -31,14 +30,15 @@ public: backend = CreateBackend(); } else { - std::cerr << "Failed to get function pointers!" << std::endl; - return false; + std::cerr << "Failed to get Core function pointers!" << std::endl; + exit(1); } backend->init(db,bridgeIn, jsonIn); return true; } - void releaseCore() { + void releaseCore(nlohmann::ordered_json& json) { if (backend != nullptr) { + json = backend->getJson(); backend->cleanup(); ReleaseBackend(backend); backend = nullptr; @@ -52,7 +52,6 @@ public: } private: qtFuncBridge* bridge; - nlohmann::json json; CreateBackendFunc CreateBackend; ReleaseBackendFunc ReleaseBackend; }; @@ -67,8 +66,8 @@ public: initConfig(); } ~appCore(){ - _backEnd.releaseCore(); auto& config = appConfig::getInstance(); + _backEnd.releaseCore(config.missionJson["backend"]); if(config.dataBase) delete config.dataBase; } diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index 39d2b65..15d3d07 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -1,4 +1,4 @@ -#include +#include #include "client/windows/handler/exception_handler.h" #include #include @@ -133,6 +133,9 @@ void connectSignalAndSlot(appCore* _core, MainWindow* _MainWindow, qtFuncBridge* QObject::connect(bridge, &qtFuncBridge::histogramWidgetAddData, _MainWindow, [=](std::vectordefects) {_MainWindow->_displayWidget->_dayNightShiftHistogramWidget->addDefect(defects); }); + QObject::connect(bridge, &qtFuncBridge::waveFormAddData, + _MainWindow, [=](QString windowName, double value) + {_MainWindow->_SummuaryDataPage.addData(windowName, value); }); QObject::connect(bridge, &qtFuncBridge::setPlcConnectionError, _MainWindow, [=]() {_MainWindow->_displayWidget->_toolBarWidget->_ConnectionWidget->setPlcDisconnected(); }); QObject::connect(bridge, &qtFuncBridge::setPlcConnectionOk, @@ -167,18 +170,22 @@ void connectSignalAndSlot(appCore* _core, MainWindow* _MainWindow, qtFuncBridge* //性能监测 QObject::connect(bridge, &qtFuncBridge::sendTriggerManagerLoopData, - _MainWindow->_displayWidget->_softwareDataDisplay, &softwareDataDisplay::onTriggerSateChanged); + _MainWindow->_displayWidget->_softwareDataDisplay, &softwareDataDisplay::onTriggerSateChanged); + + QObject::connect(bridge, &qtFuncBridge::triggerAndCaptureImage, + _core->_backEnd.backend, &IBackend::triggerAndCaptureImage,Qt::DirectConnection); + QObject::connect(bridge, &qtFuncBridge::getCameraNames, + _core->_backEnd.backend, &IBackend::getCameraNames, Qt::DirectConnection); + QObject::connect(bridge, &qtFuncBridge::getAllParams, + _core->_backEnd.backend, &IBackend::getAllParams, Qt::DirectConnection); + QObject::connect(bridge, &qtFuncBridge::editParam, + _core->_backEnd.backend, &IBackend::editParam, Qt::DirectConnection); } int main(int argc, char* argv[]) { - //if (!isRunAsAdmin()) - //{ - // restartAsAdmin(); - // exit(0); - //} - #pragma region 设置显示控制栏 + disableConsoleSelection(); HWND hWnd = GetConsoleWindow(); ShowWindow(hWnd, SW_SHOW); #pragma endregion @@ -199,11 +206,11 @@ int main(int argc, char* argv[]) appCore _core; auto& config = appConfig::getInstance(); config.consoleHandle = hWnd; - qtFuncBridge bridge; // 创建 C++ 对象 + auto bridge = qtFuncBridge::getInstance(); // 创建 C++ 对象 //检测是否已运行 if (appIsRuning() == false) exit(0); - ////////// + ////////// #pragma region 注册信号槽变量 qRegisterMetaType("matPtr"); qRegisterMetaType("stdString"); @@ -213,28 +220,25 @@ int main(int argc, char* argv[]) qRegisterMetaType("showImgPack"); qRegisterMetaType("initStatus"); qRegisterMetaType("mat"); + qRegisterMetaType< std::vector*>("vecmatptr"); qRegisterMetaType>("vectorstring"); qRegisterMetaType>>("constvpqi"); - + qRegisterMetaType("PlcValueViewerStruct"); + qRegisterMetaType>>("vecQStringQVarint"); #pragma endregion QApplication a(argc, argv); _core.initDatabase(); // 初始化数据库 QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + "/sqldrivers"); QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); - MainWindow w(&config,&bridge, config.missionJson); - connectSignalAndSlot(&_core, &w, &bridge); // 连接信号和槽 + MainWindow w(&config,bridge, config.missionJson); w.show(); QThread::msleep(100); // 创建后端对象 - _core._backEnd.loadCore(config.dataBase,&bridge, config.missionJson["backend"]); // 加载核心功能 -#if HIDE_CONSOLE - if (config.consoleHandle) { - ShowWindow(config.consoleHandle, SW_HIDE); - _core._logService->disableConsoleOutput(); - }// 隐藏控制台窗口 -#endif + _core._backEnd.loadCore(config.dataBase,bridge, config.missionJson["backend"]); // 加载核心功能 + connectSignalAndSlot(&_core, &w, bridge); // 连接信号和槽 w.registerQuitFunc([&]() { - _core._backEnd.releaseCore(); // 释放核心功能 + auto& config = appConfig::getInstance(); + _core._backEnd.releaseCore(config.missionJson["backend"]); QThread::msleep(100); if (config.dataBase) delete config.dataBase; config.dataBase = nullptr; diff --git a/src/mission/581-BaoMo-Cam1213/CMakeLists.txt b/src/mission/581-BaoMo-Cam1213/CMakeLists.txt index 76bbff3..26347b4 100644 --- a/src/mission/581-BaoMo-Cam1213/CMakeLists.txt +++ b/src/mission/581-BaoMo-Cam1213/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) diff --git a/src/mission/581-BaoMo-Cam1234/581-BaoMo.h b/src/mission/581-BaoMo-Cam1234/581-BaoMo.h index 342bd28..0a9ba18 100644 --- a/src/mission/581-BaoMo-Cam1234/581-BaoMo.h +++ b/src/mission/581-BaoMo-Cam1234/581-BaoMo.h @@ -53,7 +53,7 @@ public: // void resultHandle(std::shared_ptr data, _581_Result resultData) { // _serviceManager->_triggerManager->writeShort("D" + QByteArray::number(data->additional["触发地址"].toInt() + 106) + "_1");//发送结果 // _serviceManager->_triggerManager->writeShort("D" + QByteArray::number(data->additional["完成信号"].toInt()) + "_0");//完成信号复位 -// _serviceManager->_missionResultHandle->handleAllResult(data->SNcode, resultData.result, resultData.defects); +// _serviceManager->_missionResultHandle->toBarChart(data->SNcode, resultData.result, resultData.defects); // // } //private: diff --git a/src/mission/581-BaoMo-Cam1234/CMakeLists.txt b/src/mission/581-BaoMo-Cam1234/CMakeLists.txt index bba2e81..d94ce25 100644 --- a/src/mission/581-BaoMo-Cam1234/CMakeLists.txt +++ b/src/mission/581-BaoMo-Cam1234/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) diff --git a/src/mission/581-BaoMo-Cam5678/581-BaoMo.h b/src/mission/581-BaoMo-Cam5678/581-BaoMo.h index b5fe8a3..7665f0d 100644 --- a/src/mission/581-BaoMo-Cam5678/581-BaoMo.h +++ b/src/mission/581-BaoMo-Cam5678/581-BaoMo.h @@ -131,7 +131,7 @@ static bool BaoMoHou_ShangDaMian(std::shared_ptr data, baoMoHouDaMianD } data->resultImgs.push_back(Paint); - data->service->_missionResultHandle->handleAllResult(data->SNcode, defects.size() == 0, defects); + data->service->_missionResultHandle->toBarChart(data->SNcode, defects.size() == 0, defects); data->service->_triggerManager->writeFloat("D" + address.resultAddr + "_1");//发送OK return true; } @@ -177,7 +177,7 @@ static bool BaoMoHou_ShangDaMian(std::shared_ptr data, baoMoHouDaMianD // void resultHandle(std::shared_ptr data, _581_BaoMoResult resultData) { // _serviceManager->_triggerManager->writeShort("D" + QByteArray::number(data->additional["触发地址"].toInt() + 106) + "_1");//发送结果 // _serviceManager->_triggerManager->writeShort("D" + QByteArray::number(data->additional["完成信号"].toInt()) + "_0");//完成信号复位 -// _serviceManager->_missionResultHandle->handleAllResult(data->SNcode, resultData.result, resultData.defects); +// _serviceManager->_missionResultHandle->toBarChart(data->SNcode, resultData.result, resultData.defects); // // } //private: @@ -297,7 +297,7 @@ static bool BaoMoHou_XiaDaMian(std::shared_ptr data, baoMoHouDaMianDet // void resultHandle(std::shared_ptr data, _581_BaoMoResult resultData) { // _serviceManager->_triggerManager->writeShort("D" + QByteArray::number(data->additional["触发地址"].toInt() + 106) + "_1");//发送结果 // _serviceManager->_triggerManager->writeShort("D" + QByteArray::number(data->additional["完成信号"].toInt()) + "_0");//完成信号复位 -// _serviceManager->_missionResultHandle->handleAllResult(data->SNcode, resultData.result, resultData.defects); +// _serviceManager->_missionResultHandle->toBarChart(data->SNcode, resultData.result, resultData.defects); // // } //private: @@ -329,7 +329,7 @@ public: //baoMoHouShangDaMianTrigger::resultHandleCallback resultCallback_BaoMoHouShangDaMian = // ([=](yh_581_address address, _581_BaoMoResult resultData, std::shared_ptr data ) { // _serviceManager->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 - // _serviceManager->_missionResultHandle->handleAllResult(data->SNcode, resultData.result, resultData.defects); + // _serviceManager->_missionResultHandle->toBarChart(data->SNcode, resultData.result, resultData.defects); // }); _station_1.init("Camera1", "包膜后大面外观检测相机5(上大面1)",18800,2,1, &_baoMoHouDaMianDetParams, _serviceManager,bridge, BaoMoHou_ShangDaMian); _station_3.init("Camera3", "包膜后大面外观检测相机7(上大面2)", 19200, 2, 1, &_baoMoHouDaMianDetParams, _serviceManager, bridge, BaoMoHou_ShangDaMian); @@ -337,7 +337,7 @@ public: //baoMoHouXiaDaMianTrigger::resultHandleCallback resultCallback_BaoMoHouXiaDaMian = // ([=](yh_581_address address, _581_BaoMoResult resultData, std::shared_ptr data) { // _serviceManager->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 - // _serviceManager->_missionResultHandle->handleAllResult(data->SNcode, resultData.result, resultData.defects); + // _serviceManager->_missionResultHandle->toBarChart(data->SNcode, resultData.result, resultData.defects); // }); _station_2.init("Camera2", "包膜后大面外观检测相机6(下大面四角1)", 19000, 2, 1, &_baoMoHouDaMianDetParams, _serviceManager, bridge, BaoMoHou_XiaDaMian); _station_4.init("Camera4", "包膜后大面外观检测相机8(下大面四角2)",19400,2,1, &_baoMoHouDaMianDetParams, _serviceManager, bridge, BaoMoHou_XiaDaMian); diff --git a/src/mission/581-BaoMo-Cam5678/CMakeLists.txt b/src/mission/581-BaoMo-Cam5678/CMakeLists.txt index 76bbff3..26347b4 100644 --- a/src/mission/581-BaoMo-Cam5678/CMakeLists.txt +++ b/src/mission/581-BaoMo-Cam5678/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) diff --git a/src/mission/581-BaoMo-Cam91011/581-BaoMo.cpp b/src/mission/581-BaoMo-Cam91011/581-BaoMo.cpp index 6953511..6d208e8 100644 --- a/src/mission/581-BaoMo-Cam91011/581-BaoMo.cpp +++ b/src/mission/581-BaoMo-Cam91011/581-BaoMo.cpp @@ -113,7 +113,7 @@ bool ALGO_BaoMoHouCeMian(std::shared_ptr data, baoMoHouDaMianDetParams defects.push_back("算法不良"); } data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 - data->service->_missionResultHandle->handleAllResult(data->SNcode, !defects.size()>0, defects); + data->service->_missionResultHandle->toBarChart(data->SNcode, !defects.size()>0, defects); data->resultImgs.push_back(Paint); return true; diff --git a/src/mission/581-BaoMo-Cam91011/581-BaoMo.h b/src/mission/581-BaoMo-Cam91011/581-BaoMo.h index 3657ca2..9ccb4f8 100644 --- a/src/mission/581-BaoMo-Cam91011/581-BaoMo.h +++ b/src/mission/581-BaoMo-Cam91011/581-BaoMo.h @@ -31,9 +31,6 @@ public: HDevEngineCpp::HDevEngine myEngine; myEngine.SetProcedurePath("./halconAlgoFiles/AlgoLibrary_602.hdpl"); - _baoMoHouDaMianDetParams.seg = _serviceManager->_handleAiModelManager->getSegModel("seg"); - _baoMoHouDaMianDetParams.cls = _serviceManager->_handleAiModelManager->getClsModel("cls"); - _baoMoHouDaMianDetParams.cls2 = _serviceManager->_handleAiModelManager->getClsModel("cls2"); } bool initCustom() { diff --git a/src/mission/581-BaoMo-Cam91011/CMakeLists.txt b/src/mission/581-BaoMo-Cam91011/CMakeLists.txt index bba2e81..d94ce25 100644 --- a/src/mission/581-BaoMo-Cam91011/CMakeLists.txt +++ b/src/mission/581-BaoMo-Cam91011/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) diff --git a/src/mission/581-Dgh-CB-1/CMakeLists.txt b/src/mission/581-Dgh-CB-1/CMakeLists.txt index 12062b6..26de14b 100644 --- a/src/mission/581-Dgh-CB-1/CMakeLists.txt +++ b/src/mission/581-Dgh-CB-1/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) diff --git a/src/mission/581-Dgh-CB-2/CMakeLists.txt b/src/mission/581-Dgh-CB-2/CMakeLists.txt index 12062b6..26de14b 100644 --- a/src/mission/581-Dgh-CB-2/CMakeLists.txt +++ b/src/mission/581-Dgh-CB-2/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) diff --git a/src/mission/581-Dgh-DB-1/CMakeLists.txt b/src/mission/581-Dgh-DB-1/CMakeLists.txt index 12062b6..26de14b 100644 --- a/src/mission/581-Dgh-DB-1/CMakeLists.txt +++ b/src/mission/581-Dgh-DB-1/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) diff --git a/src/mission/581-Dgh-DB-2/CMakeLists.txt b/src/mission/581-Dgh-DB-2/CMakeLists.txt index 12062b6..26de14b 100644 --- a/src/mission/581-Dgh-DB-2/CMakeLists.txt +++ b/src/mission/581-Dgh-DB-2/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) diff --git a/src/mission/581-Mfd_djy_sd/CMakeLists.txt b/src/mission/581-Mfd_djy_sd/CMakeLists.txt index bba2e81..d94ce25 100644 --- a/src/mission/581-Mfd_djy_sd/CMakeLists.txt +++ b/src/mission/581-Mfd_djy_sd/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) diff --git a/src/mission/581-Mfd_yh_mh_hh/581-Mfd.h b/src/mission/581-Mfd_yh_mh_hh/581-Mfd.h index 990740a..0e38554 100644 --- a/src/mission/581-Mfd_yh_mh_hh/581-Mfd.h +++ b/src/mission/581-Mfd_yh_mh_hh/581-Mfd.h @@ -1,273 +1,94 @@ #include #include -#include "cal_disJiao.h" class mfd_detParams { public: int biaoding = 0; }; #pragma region 算法 - #include #include #include -# include "HalconCpp.h" -using namespace HDevEngineCpp; -using namespace HalconCpp; void exceptionResume(std::shared_ptr data, mfd_detParams* _detParams, yh_581_address address, QString why = "") { qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode) << why; cv::Mat Paint; data->oriImgs[0].copyTo(Paint); if (Paint.channels() == 1) cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); - TextRendererZH textPainter(Paint.cols, Paint.rows, 15); - textPainter.putText(Paint, "NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + TextRendererZH textPainter(Paint.cols, Paint.rows, 100); + textPainter.putText(Paint, "NG", cv::Point(100, 50), cv::Scalar(0, 255, 0)); cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); data->resultImgs.push_back(Paint); - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.01");//结果反馈 data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_0.01");//结果反馈 } +using namespace HDevEngineCpp; static bool ManHan(std::shared_ptr data, mfd_detParams* _detParams, yh_581_address address){ - HObject ho_in_image; - - HTuple hv_out_standardPar; - HTuple hv_out_flat; - HTuple in_ProductType; - HTuple hv_out_disLeftVertical; - HTuple hv_out_LeftVerticalLinePar; - HTuple hv_out_disLeftLevel; - HTuple hv_out_LeftLevelLinePar; - HTuple hv_out_disRightVertical; - HTuple hv_out_RightVerticalLinePar; - HTuple hv_out_disRightLevel; - HTuple hv_out_RightLevelLinePar; - cv::Mat img = cv::imread("D:/CCD/2025-10-04-19-57-14-928_04QCB6CJ27501JFA40006167_camera4_3.bmp", -1); - cv::cvtColor(img, img, cv::COLOR_BGR2GRAY); - cv::threshold(img, img, 60, 255, cv::THRESH_BINARY); - std::vector> contours; - cv::findContours(img, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); - if (contours.size() != 0) - { - for (int j = 0; j < contours.size(); j++) - { - cv::Rect rect = cv::boundingRect(contours[j]); - - if (rect.width > 1000 && rect.height > 900 ) - { - return true; - } - } - } - //cv::cvtColor(img, img, cv::COLOR_GRAY2BGR); - ho_in_image = MatToHObject(img); - in_ProductType = "181-2"; - cal_distanceJiao_4(ho_in_image, in_ProductType, &hv_out_standardPar, &hv_out_flat, - &hv_out_disLeftVertical, &hv_out_LeftVerticalLinePar, &hv_out_disLeftLevel, - &hv_out_LeftLevelLinePar, &hv_out_disRightVertical, &hv_out_RightVerticalLinePar, - &hv_out_disRightLevel, &hv_out_RightLevelLinePar); - int falg = hv_out_flat.I(); - return true; - std::map sqlMapData; - sqlMapData["项目"] = "上钉"; - sqlMapData["相机"] = "相机" + address.triggerAddr; - qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; - - ///////////////////取图//处理////////////////// - if (data->oriImgs.size() != 1) - { - qCritical() << "图片数量不满足要求:" << data->oriImgs.size(); - return false; - } - if (data->oriImgs[0].rows == 100) { - cv::Mat Paint; - data->oriImgs[0].copyTo(Paint); - cv::putText(Paint, "time out", cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255), 2); - return false; - } - - cv::Mat Paint; - cv::Mat src1 = data->oriImgs[0]; - if (src1.channels() != 3) - cv::cvtColor(src1, Paint, cv::COLOR_GRAY2BGR); - else - src1.copyTo(Paint); - //处理算法 -#pragma region halcon处理 - QString singal_sign = data->additional["触发地址"].toString(); - std::string cameraNumberStr = "1", FixtureNumber = "1"; - - if (singal_sign.contains("20000")) { - cameraNumberStr = "1"; - FixtureNumber = "1"; - } - else if (singal_sign.contains("20002")) { - cameraNumberStr = "1"; - FixtureNumber = "2"; - } - else if (singal_sign.contains("20200")) { - cameraNumberStr = "2"; - FixtureNumber = "3"; - } - else if (singal_sign.contains("20202")) { - cameraNumberStr = "2"; - FixtureNumber = "4"; - } - else if (singal_sign.contains("20400")) { - cameraNumberStr = "3"; - FixtureNumber = "5"; - } - else if (singal_sign.contains("20402")) { - cameraNumberStr = "3"; - FixtureNumber = "6"; - } - else if (singal_sign.contains("20600")) { - cameraNumberStr = "4"; - FixtureNumber = "7"; - } - else if (singal_sign.contains("20602")) { - cameraNumberStr = "4"; - FixtureNumber = "8"; - } - HalconCpp::HObject in_Image = MatToHObject(src1); - HDevEngineCpp::HDevProcedure proc("_9_manhan"); - HDevProcedureCall procedureCall(proc); - procedureCall.SetInputIconicParamObject("in_Image", in_Image); - procedureCall.SetInputCtrlParamTuple("in_ProductionType", _detParams->biaoding); - procedureCall.SetInputCtrlParamTuple("in_CameraNumber", HTuple(cameraNumberStr.c_str())); - procedureCall.SetInputCtrlParamTuple("in_FixtureNumber", HTuple(FixtureNumber.c_str())); - procedureCall.SetInputCtrlParamTuple("in_FilePath1", HTuple("./halconFiles/template_manHan/")); - procedureCall.SetInputCtrlParamTuple("in_FilePath2", HTuple("./halconFiles/base_manHan/")); - procedureCall.Execute(); - // *output(HTuple) : < / c> - // *out_Circle, 圆心坐标(y, x, radius) < / c > - // * out_Flag, 算法执行成功标志位, 0 : 算法执行失败 1 : 算法执行成功< / c> - // *out_OffsetX : 模组X轴的补偿量(mm) < / c > - // *out_OffsetY : 模组Y轴的补偿量(mm) < / c > - HTuple out_Circle = procedureCall.GetOutputCtrlParamTuple("out_Circle"); - HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); - HTuple out_OffsetX = procedureCall.GetOutputCtrlParamTuple("out_OffsetX"); - HTuple out_OffsetY = procedureCall.GetOutputCtrlParamTuple("out_OffsetY"); -#pragma endregion - int outFlag = out_Flag.D(); - //qDebug() << __FUNCTION__ << " line:" << __LINE__ << "camera::放钉" << "_SN::" << SN << ",放钉算法结果;" << out_Flag[0].D(); - TextRendererZH textPainter(Paint.cols, Paint.rows, 150); - if (outFlag == 1) { //-1为失败,无结果 - //先处理数据 - double offsetX; - double offsetY; - offsetX = 0 - out_OffsetX.D(); - offsetY = 0 - out_OffsetY.D(); - //if (singal_sign.contains("D18800")) - // offsetX -= 0.2; - //else if (singal_sign.contains("D19000")) { - // offsetX -= 0.15; - // offsetY += 0.1; - //} - //else if (singal_sign.contains("D19400")) { - // offsetY -= 0.2; - //} - if (abs(offsetX) >= 6000 || abs(offsetY) >= 6000) - { - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123");//发送偏移X - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_0.0123");//发送偏移Y - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 - qDebug() << __FUNCTION__ << " line:" << __LINE__ << " 满焊X偏移abs(resultData[0]) >= 6000,may err,set 0"; - data->halconOK = false; - } - else { - sqlMapData["偏移X"] = offsetX; - sqlMapData["偏移Y"] = offsetY; - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(offsetX));//发送偏移X - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_" + QByteArray::number(offsetY));//发送偏移Y - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 - data->halconOK = true; - } - //圆及十字 - cv::circle(Paint, cv::Point(out_Circle[1].D(), out_Circle[0].D()), out_Circle[2].D(), cv::Scalar(0, 255, 0), 20); - cv::line(Paint, cv::Point(out_Circle[1].D() - 300, out_Circle[0].D()), cv::Point(out_Circle[1].D() + 300, out_Circle[0].D()), cv::Scalar(0, 255, 0), 15); - cv::line(Paint, cv::Point(out_Circle[1].D(), out_Circle[0].D() - 300), cv::Point(out_Circle[1].D(), out_Circle[0].D() + 300), cv::Scalar(0, 255, 0), 15); - - textPainter.putText(Paint, "X偏移:" + std::to_string(offsetX), cv::Point(50, 150), cv::Scalar(0, 255, 0)); - textPainter.putText(Paint, "Y偏移:" + std::to_string(offsetY), cv::Point(50, 350), cv::Scalar(0, 255, 0)); - textPainter.putText(Paint, "相机:" + cameraNumberStr, cv::Point(50, 0), cv::Scalar(0, 255, 0)); - - - } - else{ - data->halconOK = false; - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123");//发送偏移X - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_0.0123");//发送偏移Y - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 - textPainter.putText(Paint, "算法异常", cv::Point(50, 300), cv::Scalar(0, 0, 255)); - } - - if (data->halconOK == true) { - textPainter.putText(Paint, "OK", cv::Point(50, 550), cv::Scalar(0, 255, 0)); - sqlMapData["结果"] = "OK"; - } - else { - textPainter.putText(Paint, "NG", cv::Point(50, 500), cv::Scalar(0, 0, 255)); - sqlMapData["结果"] = "NG"; - } - data->service->_csvWriter->addData(QString::fromStdString(data->SNcode), sqlMapData); - data->resultImgs.push_back(Paint); - - return true; -} -static bool YuHan(std::shared_ptr data, mfd_detParams* _detParams, yh_581_address address){ try { - qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; std::map sqlMapData; - sqlMapData["项目"] = "预焊"; + sqlMapData["项目"] = "满焊"; sqlMapData["相机"] = "相机" + address.triggerAddr; + qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; + ///////////////////取图//处理////////////////// - if (data->oriImgs.size() != 1) { + if (data->oriImgs.size() != 1) + { qCritical() << "图片数量不满足要求:" << data->oriImgs.size(); return false; } - if (data->oriImgs[0].rows == 100) { - cv::Mat Paint; - data->oriImgs[0].copyTo(Paint); - cv::putText(Paint, "time out", cv::Point(0, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255), 2); - return false; - } - cv::Mat Paint; cv::Mat src1 = data->oriImgs[0]; + if (src1.channels() != 3) cv::cvtColor(src1, Paint, cv::COLOR_GRAY2BGR); else src1.copyTo(Paint); //处理算法 #pragma region halcon处理 - QString singal_sign = address.triggerAddr; std::string cameraNumberStr = "1", FixtureNumber = "1"; - if (singal_sign.contains("19600")) { + + if (address.triggerAddr == 20000) { cameraNumberStr = "1"; FixtureNumber = "1"; } - else if (singal_sign.contains("19602")) { + else if (address.triggerAddr == 20002) { cameraNumberStr = "1"; - FixtureNumber = "3"; - } - else if (singal_sign.contains("19800")) { - cameraNumberStr = "2"; FixtureNumber = "2"; } - else if (singal_sign.contains("19802")) { + else if (address.triggerAddr == 20200) { + cameraNumberStr = "2"; + FixtureNumber = "3"; + } + else if (address.triggerAddr == 20202) { cameraNumberStr = "2"; FixtureNumber = "4"; } - HDevEngineCpp::HDevProcedure proc("_9_yuhan"); + else if (address.triggerAddr == 20400) { + cameraNumberStr = "3"; + FixtureNumber = "5"; + } + else if (address.triggerAddr == 20402) { + cameraNumberStr = "3"; + FixtureNumber = "6"; + } + else if (address.triggerAddr == 20600) { + cameraNumberStr = "4"; + FixtureNumber = "7"; + } + else if (address.triggerAddr == 20602) { + cameraNumberStr = "4"; + FixtureNumber = "8"; + } + data->customPath = QString::fromStdString("{imgSaveRootDir}/{yyyy-MM-dd}/{HH}H/{imageType}/{processResult}/相机" + cameraNumberStr + "孔" + FixtureNumber + "/"); HalconCpp::HObject in_Image = MatToHObject(src1); + HDevEngineCpp::HDevProcedure proc("_9_manhan"); HDevProcedureCall procedureCall(proc); procedureCall.SetInputIconicParamObject("in_Image", in_Image); - procedureCall.SetInputCtrlParamTuple("in_ProductionType", 0); - procedureCall.SetInputCtrlParamTuple("in_Resolution", HTuple(0.00749026)); + procedureCall.SetInputCtrlParamTuple("in_ProductionType", _detParams->biaoding); procedureCall.SetInputCtrlParamTuple("in_CameraNumber", HTuple(cameraNumberStr.c_str())); procedureCall.SetInputCtrlParamTuple("in_FixtureNumber", HTuple(FixtureNumber.c_str())); - procedureCall.SetInputCtrlParamTuple("in_FilePath1", HTuple("./halconFiles/yuhan_template/")); - procedureCall.SetInputCtrlParamTuple("in_FilePath2", HTuple("./halconFiles/yuhan_base/")); + procedureCall.SetInputCtrlParamTuple("in_FilePath1", HTuple("./halconFiles/template_manHan/")); + procedureCall.SetInputCtrlParamTuple("in_FilePath2", HTuple("./halconFiles/base_manHan/")); procedureCall.Execute(); // *output(HTuple) : < / c> // *out_Circle, 圆心坐标(y, x, radius) < / c > @@ -282,18 +103,141 @@ static bool YuHan(std::shared_ptr data, mfd_detParams* _detParams, yh_ int outFlag = out_Flag.D(); //qDebug() << __FUNCTION__ << " line:" << __LINE__ << "camera::放钉" << "_SN::" << SN << ",放钉算法结果;" << out_Flag[0].D(); TextRendererZH textPainter(Paint.cols, Paint.rows, 150); + textPainter.putText(Paint, "相机:" + cameraNumberStr + " 孔" + FixtureNumber, cv::Point(50, 0), cv::Scalar(0, 255, 0)); if (outFlag == 1) { //-1为失败,无结果 - double offsetX; - double offsetY; - offsetX = 0 - out_OffsetX.D(); - offsetY = 0 - out_OffsetY.D(); - //if (singal_sign.contains("D18800")) + //先处理数据 + double offsetX = 0 - out_OffsetX.D() * 1000;//和激光交流后要求我们*1000 + double offsetY = 0 - out_OffsetY.D() * 1000; + double X_Circle = out_Circle[1].D(); + double Y_Circle = out_Circle[0].D(); + + + if (abs(offsetX) >= 6000 || abs(offsetY) >= 6000) + { + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123");//发送偏移X + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_0.0123");//发送偏移Y + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 + qDebug() << __FUNCTION__ << " line:" << __LINE__ << " 满焊X偏移abs(resultData[0]) >= 6000,may err,set 0"; + data->halconOK = false; + } + else { + sqlMapData["偏移X"] = offsetX; + sqlMapData["偏移Y"] = offsetY; + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_" + QByteArray::number(offsetX));//发送偏移X + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_" + QByteArray::number(offsetY));//发送偏移Y + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//发送结果 + data->halconOK = true; + } + //圆及十字 + cv::circle(Paint, cv::Point(out_Circle[1].D(), out_Circle[0].D()), out_Circle[2].D(), cv::Scalar(0, 255, 0), 20); + cv::line(Paint, cv::Point(out_Circle[1].D() - 300, out_Circle[0].D()), cv::Point(out_Circle[1].D() + 300, out_Circle[0].D()), cv::Scalar(0, 255, 0), 15); + cv::line(Paint, cv::Point(out_Circle[1].D(), out_Circle[0].D() - 300), cv::Point(out_Circle[1].D(), out_Circle[0].D() + 300), cv::Scalar(0, 255, 0), 15); + + textPainter.putText(Paint, "X偏移:" + std::to_string(offsetX), cv::Point(50, 150), cv::Scalar(0, 255, 0)); + textPainter.putText(Paint, "Y偏移:" + std::to_string(offsetY), cv::Point(50, 350), cv::Scalar(0, 255, 0)); + //textPainter.putText(Paint, "圆心X坐标:" + std::to_string(X_Circle), cv::Point(50, 150), cv::Scalar(0, 255, 0)); + //textPainter.putText(Paint, "圆心Y坐标:" + std::to_string(offsetY), cv::Point(50, 350), cv::Scalar(0, 255, 0)); + + + } + else { + data->halconOK = false; + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.0123");//发送偏移X + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_0.0123");//发送偏移Y + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//发送结果 + textPainter.putText(Paint, "算法异常", cv::Point(50, 300), cv::Scalar(0, 0, 255)); + } + + if (data->halconOK == true) { + textPainter.putText(Paint, "OK", cv::Point(50, 550), cv::Scalar(0, 255, 0)); + sqlMapData["结果"] = "OK"; + } + else { + textPainter.putText(Paint, "NG", cv::Point(50, 500), cv::Scalar(0, 0, 255)); + sqlMapData["结果"] = "NG"; + } + data->service->_csvWriter->addData(QString::fromStdString(data->SNcode), sqlMapData); + data->resultImgs.push_back(Paint); + + return true; + } + CATCH_EXCEPTIONS + exceptionResume(data, _detParams, address, "异常"); +} +static bool YuHan(std::shared_ptr data, mfd_detParams* _detParams, yh_581_address address){ + try { + std::map sqlMapData; + sqlMapData["项目"] = "预焊"; + sqlMapData["相机"] = "相机" + address.triggerAddr; + ///////////////////取图//处理////////////////// + if (data->oriImgs.size() != 1) { + qCritical() << "图片数量不满足要求:" << data->oriImgs.size(); + return false; + } + + cv::Mat Paint; + cv::Mat src1 = data->oriImgs[0]; + //src1 = cv::imread("D://ceshi/15H/1/333.bmp",-1); + if (src1.channels() != 3) + cv::cvtColor(src1, Paint, cv::COLOR_GRAY2BGR); + else + src1.copyTo(Paint); + //处理算法 +#pragma region halcon处理 + std::string cameraNumberStr = "1", FixtureNumber = "1"; + if (address.triggerAddr == 19600) { + cameraNumberStr = "1"; + FixtureNumber = "1"; + } + else if (address.triggerAddr == 19602) { + cameraNumberStr = "1"; + FixtureNumber = "3"; + } + else if (address.triggerAddr == 19800) { + cameraNumberStr = "2"; + FixtureNumber = "2"; + } + else if (address.triggerAddr == 19802) { + cameraNumberStr = "2"; + FixtureNumber = "4"; + } + data->SNcode = "相机" + cameraNumberStr + "孔" + FixtureNumber + "_" + data->SNcode; + HDevEngineCpp::HDevProcedure proc("_9_yuhan"); + HalconCpp::HObject in_Image = MatToHObject(src1); + HDevProcedureCall procedureCall(proc); + procedureCall.SetInputIconicParamObject("in_Image", in_Image); + procedureCall.SetInputCtrlParamTuple("in_ProductionType", 0); + procedureCall.SetInputCtrlParamTuple("in_Resolution", HTuple(0.0069699)); + procedureCall.SetInputCtrlParamTuple("in_CameraNumber", HTuple(cameraNumberStr.c_str())); + procedureCall.SetInputCtrlParamTuple("in_FixtureNumber", HTuple(FixtureNumber.c_str())); + procedureCall.SetInputCtrlParamTuple("in_FilePath1", HTuple("./halconFiles/template_yuhan/")); + procedureCall.SetInputCtrlParamTuple("in_FilePath2", HTuple("./halconFiles/base_yuhan/")); + procedureCall.SetInputCtrlParamTuple("in_NailType", 1);//1:平钉 2:凹钉; + procedureCall.Execute(); + // *output(HTuple) : < / c> + // *out_Circle, 圆心坐标(y, x, radius) < / c > + // * out_Flag, 算法执行成功标志位, 0 : 算法执行失败 1 : 算法执行成功< / c> + // *out_OffsetX : 模组X轴的补偿量(mm) < / c > + // *out_OffsetY : 模组Y轴的补偿量(mm) < / c > + HTuple out_Circle = procedureCall.GetOutputCtrlParamTuple("out_Circle"); + HTuple out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); + HTuple out_OffsetX = procedureCall.GetOutputCtrlParamTuple("out_OffsetX"); + HTuple out_OffsetY = procedureCall.GetOutputCtrlParamTuple("out_OffsetY"); +#pragma endregion + int outFlag = out_Flag.D(); + //qDebug() << __FUNCTION__ << " line:" << __LINE__ << "camera::放钉" << "_SN::" << SN << ",放钉算法结果;" << out_Flag[0].D(); + TextRendererZH textPainter(Paint.cols, Paint.rows, 150); + textPainter.putText(Paint, "相机:" + cameraNumberStr + " 孔位" + FixtureNumber, cv::Point(50, 0), cv::Scalar(0, 255, 0)); + if (outFlag == 1) { //-1为失败,无结果 + double offsetX = out_OffsetX.D(); + double offsetY = out_OffsetY.D(); + //if (address.triggerAddr == 19600) // offsetX -= 0.2; - //else if (singal_sign.contains("D19000")) { + //else if (address.triggerAddr == 19602) { // offsetX -= 0.15; // offsetY += 0.1; //} - //else if (singal_sign.contains("D19400")) { + //else (address.triggerAddr == 19800) { // offsetY -= 0.2; //} if (abs(offsetX) >= 6000 || abs(offsetY) >= 6000) { @@ -318,7 +262,9 @@ static bool YuHan(std::shared_ptr data, mfd_detParams* _detParams, yh_ textPainter.putText(Paint, "X偏移:" + std::to_string(offsetX), cv::Point(50, 150), cv::Scalar(0, 255, 0)); textPainter.putText(Paint, "Y偏移:" + std::to_string(offsetY), cv::Point(50, 350), cv::Scalar(0, 255, 0)); - textPainter.putText(Paint, "相机:" + cameraNumberStr + " 孔位" + FixtureNumber, cv::Point(50, 0), cv::Scalar(0, 255, 0)); + + textPainter.putText(Paint, "圆心坐标X:" + std::to_string(out_Circle[1].D()), cv::Point(50, 750), cv::Scalar(0, 255, 0)); + textPainter.putText(Paint, "圆心坐标Y:" + std::to_string(out_Circle[0].D()), cv::Point(50, 950), cv::Scalar(0, 255, 0)); } else { data->halconOK = false; @@ -342,10 +288,8 @@ static bool YuHan(std::shared_ptr data, mfd_detParams* _detParams, yh_ return true; } CATCH_EXCEPTIONS - exceptionResume(data, _detParams, address, "NG"); + exceptionResume(data, _detParams, address, "异常"); } - - #pragma endregion class _581_Mfd : public mission { public: @@ -365,20 +309,20 @@ public: detParams _detParams; void onConfigReloaded() { HDevEngineCpp::HDevEngine myEngine; - myEngine.SetProcedurePath("./halconAlgoFiles/AlgoLibrary_602.hdpl"); + myEngine.SetProcedurePath("./halconAlgo/AlgoLibrary_602.hdpl"); } bool initCustom() { #pragma region 注册触发器回调 - yh_1.init("Camera1", "预焊1", 19600, 2, 1, &_params, _serviceManager, bridge, YuHan); - yh_2.init("Camera2", "预焊2", 19800, 2, 1, &_params, _serviceManager, bridge, YuHan); + yh_1.init("Camera1", "预焊1", 19600, 2, 1, &_params, _serviceManager, bridge, YuHan, "预焊CCD"); + yh_2.init("Camera2", "预焊2", 19800, 2, 1, &_params, _serviceManager, bridge, YuHan, "预焊CCD"); - mh_1.init("Camera3", "满焊1", 20000, 2, 1, &_params, _serviceManager, bridge, ManHan); - mh_2.init("Camera4", "满焊2", 20200, 2, 1, &_params, _serviceManager, bridge, ManHan); - mh_3.init("Camera5", "满焊3", 20400, 2, 1, &_params, _serviceManager, bridge, ManHan); - mh_4.init("Camera6", "满焊4", 20600, 2, 1, &_params, _serviceManager, bridge, ManHan); + mh_1.init("Camera3", "满焊1", 20000, 2, 1, &_params, _serviceManager, bridge, ManHan, "满焊CCD"); + mh_2.init("Camera4", "满焊2", 20200, 2, 1, &_params, _serviceManager, bridge, ManHan, "满焊CCD"); + mh_3.init("Camera5", "满焊3", 20400, 2, 1, &_params, _serviceManager, bridge, ManHan, "满焊CCD"); + mh_4.init("Camera6", "满焊4", 20600, 2, 1, &_params, _serviceManager, bridge, ManHan, "满焊CCD"); - hh_2D.init("Camera7", "焊后2d", 20800, 2, 1, &_detParams, _serviceManager, bridge, generalAlgoTest); - hh_3D.init("Camera8", "焊后3d", 21000, 2, 1, &_detParams, _serviceManager, bridge, generalAlgoTest); + hh_2D.init("Camera7", "焊后2d", 20800, 2, 1, &_detParams, _serviceManager, bridge, generalAlgoTest, "焊后CCD"); + hh_3D.init("Camera8", "焊后3d", 21000, 2, 1, &_detParams, _serviceManager, bridge, generalAlgoTest, "焊后CCD"); #pragma endregion return true; } diff --git a/src/mission/581-Mfd_yh_mh_hh/CMakeLists.txt b/src/mission/581-Mfd_yh_mh_hh/CMakeLists.txt index bba2e81..d94ce25 100644 --- a/src/mission/581-Mfd_yh_mh_hh/CMakeLists.txt +++ b/src/mission/581-Mfd_yh_mh_hh/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) diff --git a/src/mission/581-Ruke/CMakeLists.txt b/src/mission/581-Ruke/CMakeLists.txt index 76bbff3..26347b4 100644 --- a/src/mission/581-Ruke/CMakeLists.txt +++ b/src/mission/581-Ruke/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) diff --git a/src/mission/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.cpp b/src/mission/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.cpp index e0da6a5..0d966f3 100644 --- a/src/mission/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.cpp +++ b/src/mission/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.cpp @@ -6,8 +6,9 @@ void exceptionResume(std::shared_ptr data, canyeParams* _detParams, sx_ data->oriImgs[0].copyTo(Paint); if (Paint.channels() == 1) cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); - TextRendererZH textPainter(Paint.cols, Paint.rows, 15); - textPainter.putText(Paint, "NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); + + TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + textPainter.putText(Paint, why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); data->resultImgs.push_back(Paint); data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 @@ -26,6 +27,7 @@ bool yizhuCanYe(std::shared_ptr data, canyeParams* _detParams, sx_581_a QByteArray plcResult = "1"; QString detResult = "OK"; cv::Mat sourceImg = data->oriImgs[0]; + //sourceImg = cv::imread("D://readSnEmpty13-12-30-14Camera1_13-12-32-38_Count0.bmp"); cv::Mat Paint; sourceImg.copyTo(Paint); @@ -40,6 +42,10 @@ bool yizhuCanYe(std::shared_ptr data, canyeParams* _detParams, sx_581_a //cv::resize(workImg, workImg, cv::Size(), 0.25, 0.25); cv::Mat dingWeiResultImg = ~_detParams->segDingWei->Evaluate(workImg); cv::Rect dingWeiRect = findMaxAreaContour(dingWeiResultImg); + if (dingWeiRect.x == 0 && dingWeiRect.y == 0 || dingWeiRect.width < 10 || dingWeiRect.height < 10) { + exceptionResume(data, _detParams, address, "定位注液孔失败"); + return true; + } cv::Point2f center(dingWeiRect.x + dingWeiRect.width / 2, dingWeiRect.y + dingWeiRect.height / 2); cv::Mat centerImg; cv::Rect cutArea = cv::Rect(center.x - 64, center.y - 64, 128, 128); @@ -47,14 +53,15 @@ bool yizhuCanYe(std::shared_ptr data, canyeParams* _detParams, sx_581_a //////////////////////////////////// int res = 0; res = _detParams->clsCanYe->Evaluate(centerImg); + data->segOriginImgs.push_back(centerImg); if (res == 0){ - cv::putText(Paint, "OK", cv::Point(Paint.cols - 500, 0), 10,10,cv::Scalar(0, 255, 0)); + cv::putText(Paint, "OK", cv::Point(100, 500), 1,20,cv::Scalar(0, 255, 0), 10); cv::rectangle(Paint, cv::Rect(cutArea.x * 4, cutArea.y * 4, cutArea.width * 4, cutArea.height * 4), cv::Scalar(0, 255, 0), 3); } else{ - cv::putText(Paint, "NG", cv::Point(Paint.cols - 500, 0), 10, 10, cv::Scalar(0, 255, 0)); + cv::putText(Paint, "NG", cv::Point(100, 500), 1, 20, cv::Scalar(0, 255, 0), 10); cv::rectangle(Paint, cv::Rect(cutArea.x * 4, cutArea.y * 4, cutArea.width * 4, cutArea.height * 4), cv::Scalar(0, 0, 255), 3); plcResult = "2"; data->aiDetOK = false; diff --git a/src/mission/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.h b/src/mission/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.h index 8dc5953..24e91d3 100644 --- a/src/mission/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.h +++ b/src/mission/581_YiCiZhuYe_canye/581_YiCiZhuYe_canye.h @@ -12,7 +12,7 @@ public: handdleAI::pSegmentation* segDanYe = nullptr; handdleAI::pClassification* clsCanYe = nullptr; bool YiZhu = true; - bool DOGLIVE = false; + bool DOGLIVE = true; }; bool yizhuCanYe(std::shared_ptr data, canyeParams* _detParams, sx_581_address address); @@ -32,9 +32,9 @@ public: for (auto¶m : canyeDetParams) { if (param.DOGLIVE == false) continue; - param.segDingWei = handdleAI::CreateSegObjS("./handleaiModels/canye/pSeg_DingWei.BHD", 16, 0); - param.segDanYe = handdleAI::CreateSegObjS("./handleaiModels/canye/pSeg_CanYe.BHD", 16, 0); - param.clsCanYe = handdleAI::CreateClsObjV3("./handleaiModels/canye/pCls_CanYe.BHD", 0, 1); + param.segDingWei = handdleAI::CreateSegObjS("./handleaiModels/yiCiZhuYe/pSeg_DingWei.BHD", 16, 0); + param.segDanYe = handdleAI::CreateSegObjS("./handleaiModels/yiCiZhuYe/pSeg_CanYe.BHD", 16, 0); + param.clsCanYe = handdleAI::CreateClsObjV3("./handleaiModels/yiCiZhuYe/pCls_CanYe.BHD", 0, 1); } initOnce = true; } diff --git a/src/mission/581_YiCiZhuYe_canye/CMakeLists.txt b/src/mission/581_YiCiZhuYe_canye/CMakeLists.txt index af9bb43..26347b4 100644 --- a/src/mission/581_YiCiZhuYe_canye/CMakeLists.txt +++ b/src/mission/581_YiCiZhuYe_canye/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) @@ -40,14 +40,15 @@ FILE(GLOB UI_FILES "./*.ui") #find_package(Qt5 COMPONENTS Core REQUIRED) -find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network Sql REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Sql REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) add_library(${CURRENT_FOLDER} SHARED ${HEAD_FILES} ${SRC_FILES} ${UI_FILES} - ${PROJECT_ROOT_PATH}/include/qtBridge.h) + ${PROJECT_ROOT_PATH}/include/qtBridge.h + ) @@ -63,7 +64,6 @@ target_link_libraries(${CURRENT_FOLDER} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network - Qt${QT_VERSION_MAJOR}::Sql ) diff --git a/src/mission/581_YuHan_hanhou/CMakeLists.txt b/src/mission/581_YuHan_hanhou/CMakeLists.txt index bba2e81..26347b4 100644 --- a/src/mission/581_YuHan_hanhou/CMakeLists.txt +++ b/src/mission/581_YuHan_hanhou/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) @@ -40,8 +40,8 @@ FILE(GLOB UI_FILES "./*.ui") #find_package(Qt5 COMPONENTS Core REQUIRED) -find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network Sql REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Sql REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) add_library(${CURRENT_FOLDER} SHARED ${HEAD_FILES} @@ -64,7 +64,6 @@ target_link_libraries(${CURRENT_FOLDER} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network - Qt${QT_VERSION_MAJOR}::Sql ) diff --git a/src/mission/581_YuHan_hanqian/CMakeLists.txt b/src/mission/581_YuHan_hanqian/CMakeLists.txt index bba2e81..26347b4 100644 --- a/src/mission/581_YuHan_hanqian/CMakeLists.txt +++ b/src/mission/581_YuHan_hanqian/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) @@ -40,8 +40,8 @@ FILE(GLOB UI_FILES "./*.ui") #find_package(Qt5 COMPONENTS Core REQUIRED) -find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network Sql REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network Sql REQUIRED) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Network REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Network REQUIRED) add_library(${CURRENT_FOLDER} SHARED ${HEAD_FILES} @@ -64,7 +64,6 @@ target_link_libraries(${CURRENT_FOLDER} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network - Qt${QT_VERSION_MAJOR}::Sql ) diff --git a/src/mission/581_ZhongHan_hanhou/581_ZhongHan_hanhou.cpp b/src/mission/581_ZhongHan_hanhou/581_ZhongHan_hanhou.cpp index 07fbb71..7b47f5a 100644 --- a/src/mission/581_ZhongHan_hanhou/581_ZhongHan_hanhou.cpp +++ b/src/mission/581_ZhongHan_hanhou/581_ZhongHan_hanhou.cpp @@ -1,5 +1,343 @@ #include <581_ZhongHan_hanhou.h> +void exceptionResume(std::shared_ptr data, zhongHan3DParams* _detParams, sx_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("¼ì²âʧ°Ü!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->oriImgs[0].copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + textPainter.putText(Paint, why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->resultImgs.push_back(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//½á¹û·´À¡ +} +void exceptionResume(std::shared_ptr data, zhongHan2dCeLiangParams* _detParams, sx_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("¼ì²âʧ°Ü!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->oriImgs[0].copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + textPainter.putText(Paint, why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->resultImgs.push_back(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//½á¹û·´À¡ +} +void exceptionResume(std::shared_ptr data, zhongHan2dCeMianParams* _detParams, sx_581_address address, QString why = "") { + qDebug() << "[Error] " << QString::fromUtf8("¼ì²âʧ°Ü!") << data->uuid << QString::fromStdString(data->SNcode) << why; + cv::Mat Paint; + data->oriImgs[0].copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + + TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + textPainter.putText(Paint, why.toStdString(), cv::Point(500, 50), cv::Scalar(0, 255, 0)); + cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); + data->resultImgs.push_back(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//½á¹û·´À¡ +} +bool zhongHan_hanHouCeLiang(std::shared_ptr data, zhongHan2dCeLiangParams* _detParams, sx_581_address address) { + try + { + + QByteArray plcResult = "2"; + //img2D = cv::imread("D:\\Öպ¸CCD´æͼ\\º¸ºóÕýÃæ²âÁ¿CCD\\2025-07-28\\ԭͼ\\21H\\errSN\\ORI0_errSN_DA1806128_0_21-34-01-10.bmp", -1); + cv::Mat sourceImg = data->oriImgs[0]; + cv::Rect cutRect; + if (_detParams->isTong == true) { + cutRect = cv::Rect(2000, 0, 3000, 3648); + } + if (_detParams->isTong == true) { + cutRect = cv::Rect(400, 0, 3000, 3648); + } + cv::Mat cutImage = sourceImg(cutRect & (cv::Rect(0, 0, sourceImg.cols, sourceImg.rows))); + + cv::Mat Paint; + cutImage.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + cv::Mat segProcessMat; + float resizeRate = 5.0; + cv::resize(cutImage, segProcessMat, cv::Size(), 1 / resizeRate, 1 / resizeRate); + cv::Mat segResult = _detParams->seg_hanHouDingWei->Evaluate(segProcessMat); + data->segOriginImgs.push_back(segProcessMat); + data->segOriginImgs.push_back(cutImage); + + HDevEngineCpp::HDevProcedure proc("_2_Cal_Welding_Distance"); + HDevEngineCpp::HDevProcedureCall procedureCall(proc); + HalconCpp::HObject in_Image = MatToHObject(cutImage); + HalconCpp::HObject in_ImageSeg = MatToHObject(segResult); + + HTuple in_Resolution(0.0346699); + HTuple in_FilePath("./halconFiles/"); + procedureCall.SetInputIconicParamObject("in_Image", in_Image); + procedureCall.SetInputIconicParamObject("in_ImageSeg", in_ImageSeg); + procedureCall.SetInputCtrlParamTuple("in_Resolution", in_Resolution); + procedureCall.SetInputCtrlParamTuple("in_FilePath", in_FilePath); + procedureCall.Execute(); + HTuple out_BaseLineCor, out_LineCor, + out_Distance1, out_Distance2, out_Distance3, out_Distance4, + out_ColorType, out_Flag; + out_BaseLineCor = procedureCall.GetOutputCtrlParamTuple("out_BaseLineCor"); + out_LineCor = procedureCall.GetOutputCtrlParamTuple("out_LineCor"); + out_Distance1 = procedureCall.GetOutputCtrlParamTuple("out_Distance1"); + out_Distance2 = procedureCall.GetOutputCtrlParamTuple("out_Distance2"); + out_ColorType = procedureCall.GetOutputCtrlParamTuple("out_ColorType"); + out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); + TextRendererZH textPainter(Paint.cols, Paint.rows, 120); + if (out_Flag.D() == 1) + { + { + int out_BaseLineCor_L = out_BaseLineCor.Length(); + for (auto i = 0; i < out_BaseLineCor_L / 4; i++) + cv::line(Paint, cv::Point(out_BaseLineCor[i * 4 + 1].D(), out_BaseLineCor[i * 4].D()), + cv::Point(out_BaseLineCor[i * 4 + 3].D(), out_BaseLineCor[i * 4 + 2].D()), + cv::Scalar(0, 255, 0), 20); + + int out_LineCor_L = out_LineCor.Length(); + for (auto i = 0; i < out_LineCor_L / 4; i++) + cv::line(Paint, cv::Point(out_LineCor[i * 4 + 1].D(), out_LineCor[i * 4].D()), + cv::Point(out_LineCor[i * 4 + 3].D(), out_LineCor[i * 4 + 2].D()), + cv::Scalar(0, 255, 0), 20); + } + //1 + { + textPainter.putText(Paint, "ˮƽ¼ä¾à:" + std::to_string(out_Distance1[0].D()), cv::Point(50, 25), cv::Scalar(0, 255, 0)); + textPainter.putText(Paint, "Êúֱ¼ä¾à:" + std::to_string(out_Distance1[1].D()), cv::Point(50, 25), cv::Scalar(0, 255, 0)); + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + QByteArray::number(out_Distance1[1].D())); + } + //2 + { + textPainter.putText(Paint, "ˮƽ¼ä¾à:" + std::to_string(out_Distance2[0].D()), cv::Point(50, 450), cv::Scalar(0, 255, 0)); + textPainter.putText(Paint, "Êúֱ¼ä¾à:" + std::to_string(out_Distance2[1].D()), cv::Point(50, 675), cv::Scalar(0, 255, 0)); + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + QByteArray::number(out_Distance2[1].D())); + } + } + else + { + textPainter.putText(Paint, "NG", cv::Point(100, 50), cv::Scalar(0, 255, 0)); + data->halconOK = false; + plcResult = "2"; + } + plcResult = "1";//ǿÖÆOK + data->resultImgs.push_back(Paint); + sourceImg.release(); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//½á¹û·´À¡ + return true; + } + CATCH_EXCEPTIONS + exceptionResume(data, _detParams, address, "ERROR"); + return true; +} +std::vector aiEvaluate(cv::Mat input, cv::Mat& Paint, bool& isNG, zhongHan2dCeMianParams* _detParams) +{ + std::vector clsImgs; + cv::Mat forSeg; + input.copyTo(forSeg); + cv::Mat segRes = ~_detParams->seg_ceMian->Evaluate(forSeg); + std::vector>contours; + cv::findContours(segRes, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); + int clsIndex = 0; + TextRendererZH textPainter(Paint.cols, Paint.rows, 50); + for (auto& contour : contours) { + clsIndex++; + cv::Rect rect = cv::boundingRect(contour); + if (rect.width < 200 || rect.height < 200) + continue; + cv::Mat clsImg = cutImg2Cls(rect, input); + //cv::Mat clsImg = cutLabelImg(input, 128, 128, rect, 0, 1, 0); + int claResult = _detParams->cls_ceMian->Evaluate(clsImg); + clsImgs.push_back(clsImg); + switch (claResult) + { + case 0://OK + cv::rectangle(Paint, rect, cv::Scalar(0, 255, 0), 3); + break; + case 1://NG + cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); + textPainter.putText(Paint, "NG" , cv::Point(rect.x, rect.y), cv::Scalar(0, 0, 255)); + isNG = true; + break; + case 2://ÆøÅÝ + textPainter.putText(Paint, "˺ÁÑ", cv::Point(rect.x, rect.y), cv::Scalar(0, 0, 255)); + cv::rectangle(Paint, rect, cv::Scalar(0, 0, 255), 3); + isNG = true; + break; + } + } + return clsImgs; +} +bool zhongHan_hanHouCeMian(std::shared_ptr data, zhongHan2dCeMianParams* _detParams, sx_581_address address) +{ + try + { + cv::Rect cutROI; + if (data->cameraKeyName == "Camera2") { + cutROI = cv::Rect(2000, 1550, 1500, 1300); + } + else if (data->cameraKeyName == "Camera3") { + cutROI = cv::Rect(2000, 1450, 1500, 1300); + } + else if (data->cameraKeyName == "Camera5") { + cutROI = cv::Rect(1900, 1380, 1500, 1300); + } + else if (data->cameraKeyName == "Camera6") { + cutROI = cv::Rect(2070, 1500, 1500, 1300); + } + else { + cutROI = cv::Rect(1900, 1350, 1600, 1500); + } + cv::Mat img2D = data->oriImgs[0]; + cv::Mat cutImage = img2D(cutROI & cv::Rect(0, 0, img2D.cols, img2D.rows)).clone();//²üô³ö¼ì²âÇøÓ򣬼ӿ촦Àí + cv::Mat Paint; + cutImage.copyTo(Paint); + bool isNG = false; + + std::vector clsImgs; + clsImgs.resize(0); + + //µ÷»úʱÆÁ±Σ¬ÅÜÁϴò¿ª + clsImgs = aiEvaluate(cutImage, Paint, isNG,_detParams); + TextRendererZH textPainter(Paint.cols, Paint.rows, 120); + + if (isNG == true) { + textPainter.putText(Paint, "NG", cv::Point(0, 50), cv::Scalar(0, 255, 0)); + } + else { + textPainter.putText(Paint, "OK", cv::Point(0, 50), cv::Scalar(0, 255, 0)); + } + for (int i = 0; i < clsImgs.size(); i++) { + data->clsOriginImgs.push_back({ std::to_string(i),clsImgs[i] }); + } + data->resultImgs.push_back(Paint); + data->segOriginImgs.push_back(cutImage); + + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//½á¹û·´À¡ + } + CATCH_EXCEPTIONS + exceptionResume(data, _detParams, address, "ERROR"); + return true; + +} +halconResult halconProcess(cv::Mat img2d, cv::Mat img3d, int cnt, handdleAI::pSegmentation* ptr) +{ + halconResult hResult; + img2d.copyTo(hResult.Paint); + if (hResult.Paint.channels() == 1) + cvtColor(hResult.Paint, hResult.Paint, cv::COLOR_GRAY2BGR); + + try + { + HDevEngineCpp::HDevProcedure proc("FinalWelding_HY_Height"); + HDevProcedureCall procedureCall(proc); + + HObject in_ImageG, in_ImageH, in_ImageSeg; + HTuple in_xResolution(0.0125), in_yResolution(0.0125), in_zResolution(0.0005); + HTuple out_HYNumber, out_HYPointX, out_HYPointY, out_HYHeightValue, out_Flag; + in_ImageG = MatToHObject(img2d); + in_ImageH = MatToHObject(img3d); + hResult.segResult = ptr->Evaluate(img2d); + in_ImageSeg = MatToHObject(hResult.segResult); + procedureCall.SetInputIconicParamObject("in_ImageG", in_ImageG); + procedureCall.SetInputIconicParamObject("in_ImageH", in_ImageH); + procedureCall.SetInputIconicParamObject("in_ImageSeg", in_ImageSeg); + + + procedureCall.SetInputCtrlParamTuple("in_xResolution", in_xResolution); + procedureCall.SetInputCtrlParamTuple("in_yResolution", in_yResolution); + procedureCall.SetInputCtrlParamTuple("in_zResolution", in_zResolution); + procedureCall.Execute();//ִÐÐhalcon³ÌÐò + + out_HYNumber = procedureCall.GetOutputCtrlParamTuple("out_HYNumber");//»ñȡÊä³ö¿ØÖÆÀà±äÁ¿ + out_HYPointX = procedureCall.GetOutputCtrlParamTuple("out_HYPointX");//»ñȡÊä³ö¿ØÖÆÀà±äÁ¿ + out_HYPointY = procedureCall.GetOutputCtrlParamTuple("out_HYPointY");//»ñȡÊä³ö¿ØÖÆÀà±äÁ¿ + out_HYHeightValue = procedureCall.GetOutputCtrlParamTuple("out_HYHeightValue");//»ñȡÊä³ö¿ØÖÆÀà±äÁ¿ + out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag");//»ñȡÊä³öͼÏñÀà±äÁ¿ + + int flag = out_Flag.D(); + TextRendererZH textPainter(hResult.Paint.cols, hResult.Paint.rows, 120); + if (flag == 1) + { + hResult.hyCount = (int)out_HYNumber[0].D() + (int)out_HYNumber[1].D(); + textPainter.putText(hResult.Paint, "ÉϺ¸ӡÊýÁ¿:" + std::to_string((int)out_HYNumber[0].D()), cv::Point(0, 50), cv::Scalar(0, 255, 0)); + textPainter.putText(hResult.Paint, "Ϻ¸ӡÊýÁ¿:" + std::to_string((int)out_HYNumber[1].D()), cv::Point(0, 150), cv::Scalar(0, 255, 0)); + + hResult.data.topHanYinCount = (int)out_HYNumber[0].D(); + hResult.data.bottomHanYinCount = (int)out_HYNumber[1].D(); + std::vector data; + if (out_HYPointX.Length() == out_HYPointY.Length() && out_HYPointX.Length() == out_HYHeightValue.Length()) + for (int i = 0; i < out_HYPointX.Length(); i++) + { + Cross(hResult.Paint, cv::Point(out_HYPointX[i].D(), out_HYPointY[i].D()), 5, 2, cv::Scalar(0, 255, 0)); + putText(hResult.Paint, + std::to_string(out_HYHeightValue[i].D()), + cv::Point(out_HYPointX[i].D() - 30, out_HYPointY[i].D() - 15), + 1, 1, cv::Scalar(0, 255, 0), 1); + data.push_back(out_HYHeightValue[i].D()); + } + else + textPainter.putText(hResult.Paint, "ÊýÁ¿²»ƥÅä", cv::Point(0, 250), cv::Scalar(0, 255, 0)); + + double sum = 0.0; + double min_val = std::numeric_limits::infinity(); + double max_val = -std::numeric_limits::infinity(); + + for (double value : data) { + sum += value; + if (value < min_val) { + hResult.data.minHanYinDepth = value; // ¸üÐÂ×îСֵ + } + if (value > max_val) { + hResult.data.maxHanYinDepth = value; // ¸üÐÂ×î´óֵ + } + } + + // ¼ÆËãƽ¾ùֵ + hResult.data.avgHanYinDepth = sum / data.size(); + } + else { + textPainter.putText(hResult.Paint, "Ë㷨´íÎó", cv::Point(0, 250), cv::Scalar(0, 255, 0)); + hResult.result = false; + } + } + CATCH_EXCEPTIONS + return hResult; +} +bool zhongHan_hanHou3D(std::shared_ptr data, zhongHan3DParams* _detParams, sx_581_address address) +{ + try + { + exceptionResume(data, _detParams, address,"OK"); + return true; + cv::Mat img2D1 = data->oriImgs[0], img3D1 = data->oriImgs[1], img2D2 = data->oriImgs[2], img3D2 = data->oriImgs[3]; + //ASYNC// + auto furture1 = std::async(std::launch::async, &halconProcess, img2D1, img3D1, 0, _detParams->seg_3dHanYinDingWei); + auto furture2 = std::async(std::launch::async, &halconProcess, img2D2, img3D2, 1, _detParams->seg_3dHanYinDingWei); + furture1.wait(); + halconResult result1 = furture1.get(); + furture2.wait(); + halconResult result2 = furture2.get(); + data->segOriginImgs.push_back(result1.segResult); + data->segOriginImgs.push_back(result2.segResult); + + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr) + "_1");//½á¹û·´À¡ + data->service->_triggerManager->writeShort("D" + QByteArray::number(address.valueOffsetAddr + 1) + "_1");//½á¹û·´À¡ + + cv::Mat PaintSum(result1.Paint.rows, result1.Paint.cols + result2.Paint.cols, CV_8UC3); + result1.Paint.copyTo(PaintSum(cv::Rect(0, 0, result1.Paint.cols, result1.Paint.rows))); + result2.Paint.copyTo(PaintSum(cv::Rect(result1.Paint.cols, 0, result1.Paint.cols, result1.Paint.rows))); + data->resultImgs.push_back(PaintSum); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//½á¹û·´À¡ + } + CATCH_EXCEPTIONS + exceptionResume(data, _detParams, address,"ERROR"); + return true; + +} + mission* createMission() { return new _581_ZhongHan_hanhou(); } diff --git a/src/mission/581_ZhongHan_hanhou/581_ZhongHan_hanhou.h b/src/mission/581_ZhongHan_hanhou/581_ZhongHan_hanhou.h index 60ef0ab..4004ae5 100644 --- a/src/mission/581_ZhongHan_hanhou/581_ZhongHan_hanhou.h +++ b/src/mission/581_ZhongHan_hanhou/581_ZhongHan_hanhou.h @@ -6,144 +6,62 @@ #include using namespace HDevEngineCpp; #pragma endregion -class zhongHanParams { +class zhongHan2dCeLiangParams { public: handdleAI::pSegmentation* seg_hanHouDingWei = nullptr; bool isTong = true; }; -bool zhongHan_hanHouCeLiang(std::shared_ptr data, zhongHanParams* _detParams, sx_581_address address) { - try - { -#if 0 - /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓调试用↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/ - plcResult = "1"; - QString resultTag = "OK"; - cv::Mat resultImage = img2D.clone(); - putTextHusky(resultImage, utf8_to_gbk("OK").c_str(), cv::Point(0, 50), cv::Scalar(0, 255, 0), 500); - cv::resize(resultImage, resultImage, cv::Size(), 0.5, 0.5); - resMat.push_back(resultImage); - if (resMat.size() > 0) - { - addImgToSaveStruct(resMat[0], "0", getResSaveFormat(SN, resultTag));//保存结果图 - } - return true; - /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑调试用↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/ +class zhongHan2dCeMianParams { +public: + handdleAI::pSegmentation* seg_ceMian = nullptr; + handdleAI::pClassification* cls_ceMian = nullptr; + bool isTong = true; +}; +class zhongHan3DParams { +public: + handdleAI::pSegmentation* seg_3dHanYinDingWei = nullptr; + bool isTong = true; +}; +struct halconResult { + struct halconData { + int topHanYinCount = 0; + int bottomHanYinCount = 0; + double avgHanYinDepth = 0; + double maxHanYinDepth = 0; + double minHanYinDepth = 0; + bool detResultIsOk = true; + }; + cv::Mat Paint; + int hyCount; + cv::Mat segResult; + bool result; + halconData data; +}; +bool zhongHan_hanHouCeLiang(std::shared_ptr data, zhongHan2dCeLiangParams* _detParams, sx_581_address address); +std::vector aiEvaluate(cv::Mat input, cv::Mat& Paint, bool& isNG, zhongHan2dCeMianParams* _detParams); +bool zhongHan_hanHouCeMian(std::shared_ptr data, zhongHan2dCeMianParams* _detParams, sx_581_address address); +halconResult halconProcess(cv::Mat img2d, cv::Mat img3d, int cnt, handdleAI::pSegmentation* ptr); +bool zhongHan_hanHou3D(std::shared_ptr data, zhongHan3DParams* _detParams, sx_581_address address); -#endif - QByteArray plcResult = "2"; - //img2D = cv::imread("D:\\终焊CCD存图\\焊后正面测量CCD\\2025-07-28\\原图\\21H\\errSN\\ORI0_errSN_DA1806128_0_21-34-01-10.bmp", -1); - cv::Mat sourceImg = data->oriImgs[0]; - cv::Rect cutRect; - if (_detParams->isTong == true) { - cutRect = cv::Rect(2000, 0, 3000, 3648); - } - if (_detParams->isTong == true) { - cutRect = cv::Rect(400, 0, 3000, 3648); - } - cv::Mat cutImage = sourceImg(cutRect & (cv::Rect(0, 0, sourceImg.cols, sourceImg.rows))); - - cv::Mat Paint; - cutImage.copyTo(Paint); - if (Paint.channels() == 1) - cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); - cv::Mat segProcessMat; - float resizeRate = 5.0; - cv::resize(cutImage, segProcessMat, cv::Size(), 1 / resizeRate, 1 / resizeRate); - cv::Mat segResult = _detParams->seg_hanHouDingWei->Evaluate(segProcessMat); - data->segOriginImgs.push_back(segProcessMat); - data->segOriginImgs.push_back(cutImage); - - HDevEngineCpp::HDevProcedure proc("_2_Cal_Welding_Distance"); - HDevEngineCpp::HDevProcedureCall procedureCall(proc); - HalconCpp::HObject in_Image = MatToHObject(cutImage); - HalconCpp::HObject in_ImageSeg = MatToHObject(segResult); - - HTuple in_Resolution(0.0346699); - HTuple in_FilePath("./halconFiles/"); - procedureCall.SetInputIconicParamObject("in_Image", in_Image); - procedureCall.SetInputIconicParamObject("in_ImageSeg", in_ImageSeg); - procedureCall.SetInputCtrlParamTuple("in_Resolution", in_Resolution); - procedureCall.SetInputCtrlParamTuple("in_FilePath", in_FilePath); - procedureCall.Execute(); - HTuple out_BaseLineCor, out_LineCor, - out_Distance1, out_Distance2, out_Distance3, out_Distance4, - out_ColorType, out_Flag; - out_BaseLineCor = procedureCall.GetOutputCtrlParamTuple("out_BaseLineCor"); - out_LineCor = procedureCall.GetOutputCtrlParamTuple("out_LineCor"); - out_Distance1 = procedureCall.GetOutputCtrlParamTuple("out_Distance1"); - out_Distance2 = procedureCall.GetOutputCtrlParamTuple("out_Distance2"); - out_ColorType = procedureCall.GetOutputCtrlParamTuple("out_ColorType"); - out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag"); - TextRendererZH textPainter(Paint.cols, Paint.rows, 120); - if (out_Flag.D() == 1) - { - { - int out_BaseLineCor_L = out_BaseLineCor.Length(); - for (auto i = 0; i < out_BaseLineCor_L / 4; i++) - cv::line(Paint, cv::Point(out_BaseLineCor[i * 4 + 1].D(), out_BaseLineCor[i * 4].D()), - cv::Point(out_BaseLineCor[i * 4 + 3].D(), out_BaseLineCor[i * 4 + 2].D()), - cv::Scalar(0, 255, 0), 20); - - int out_LineCor_L = out_LineCor.Length(); - for (auto i = 0; i < out_LineCor_L / 4; i++) - cv::line(Paint, cv::Point(out_LineCor[i * 4 + 1].D(), out_LineCor[i * 4].D()), - cv::Point(out_LineCor[i * 4 + 3].D(), out_LineCor[i * 4 + 2].D()), - cv::Scalar(0, 255, 0), 20); - } - //1 - { - textPainter.putText(Paint, "水平间距:" + std::to_string(out_Distance1[0].D()), cv::Point(50, 25), cv::Scalar(0, 255, 0)); - textPainter.putText(Paint, "竖直间距:" + std::to_string(out_Distance1[1].D()), cv::Point(50, 25), cv::Scalar(0, 255, 0)); - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + QByteArray::number(out_Distance1[1].D())); - } - //2 - { - textPainter.putText(Paint, "水平间距:" + std::to_string(out_Distance2[0].D()), cv::Point(50, 450), cv::Scalar(0, 255, 0)); - textPainter.putText(Paint, "竖直间距:" + std::to_string(out_Distance2[1].D()), cv::Point(50, 675), cv::Scalar(0, 255, 0)); - data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + QByteArray::number(out_Distance2[1].D())); - } - } - else - { - textPainter.putText(Paint, "NG", cv::Point(100, 50), cv::Scalar(0, 255, 0)); - data->halconOK = false; - plcResult = "2"; - } - plcResult = "1";//强制OK - data->resultImgs.push_back(Paint); - sourceImg.release(); - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 - return true; - } - CATCH_EXCEPTIONS - - qDebug() << "[Error] " << QString::fromUtf8("检测失败!") << data->uuid << QString::fromStdString(data->SNcode); - cv::Mat Paint; - data->oriImgs[0].copyTo(Paint); - if (Paint.channels() == 1) - cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); - TextRendererZH textPainter(Paint.cols, Paint.rows, 15); - textPainter.putText(Paint, "NG", cv::Point(1000, 50), cv::Scalar(0, 255, 0)); - cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); - data->resultImgs.push_back(Paint); - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 - return true; -} class _581_ZhongHan_hanhou : public mission { public: - _581_SX_Single_triggerTemplate< zhongHanParams> lv_top_1; - _581_SX_Single_triggerTemplate< zhongHanParams> lv_left45_2; - _581_SX_Single_triggerTemplate< zhongHanParams> lv_right45_3; - _581_SX_Single_triggerTemplate< zhongHanParams> _3D_1; + _581_SX_Single_triggerTemplate< zhongHan2dCeLiangParams> lv_top_1; + _581_SX_Single_triggerTemplate< zhongHan2dCeMianParams> lv_left45_2; + _581_SX_Single_triggerTemplate< zhongHan2dCeMianParams> lv_right45_3; + _581_SX_Single_triggerTemplate< zhongHan3DParams> _3D_1; - _581_SX_Single_triggerTemplate< zhongHanParams> tong_top_5; - _581_SX_Single_triggerTemplate< zhongHanParams> tong_left45_6; - _581_SX_Single_triggerTemplate< zhongHanParams> tong_right45_7; - _581_SX_Single_triggerTemplate< zhongHanParams> _3D_2; - zhongHanParams tongDetParams; - zhongHanParams lvDetParams; + _581_SX_Single_triggerTemplate< zhongHan2dCeLiangParams> tong_top_5; + _581_SX_Single_triggerTemplate< zhongHan2dCeMianParams> tong_left45_6; + _581_SX_Single_triggerTemplate< zhongHan2dCeMianParams> tong_right45_7; + _581_SX_Single_triggerTemplate< zhongHan3DParams> _3D_2; + zhongHan2dCeLiangParams tongZhongHan2dCeLiangParams; + zhongHan2dCeLiangParams lvZhongHan2dCeLiangParams; + std::vector< zhongHan2dCeMianParams> tongZhongHan2dCeMianParams; + std::vector< zhongHan2dCeMianParams> lvZhongHan2dCeMianParams; + zhongHan3DParams tongZhongHan3DParams; + zhongHan3DParams lvZhongHan3DParams; bool initOnce = false; void onConfigReloaded() { HDevEngineCpp::HDevEngine myEngine; @@ -161,22 +79,20 @@ public: // params.clsJiErFanZhe = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanqian/60_2_hanqian_clsL.BHD", 1, 1); // params.isTong = false; //} - tongDetParams.isTong = true; - lvDetParams.isTong = false; initOnce = true; } } bool initCustom() { #pragma region 注册触发器回调 - lv_top_1.init("Camera1", "焊印检测-内-铜-相机1(90°)", { { 5360, 1,5340,"5362","5364",-1} }, 1, &lvDetParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); - lv_left45_2.init("Camera2", "翻折撕裂-内-铜-相机2(45°)", { { 5370, 1,5340,"5372","5374",-1} }, 1, &lvDetParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); - lv_right45_3.init("Camera3", "翻折撕裂-内-铜-相机3(45°)", { { 5380,1,5340,"5382","5384",-1} }, 1, &tongDetParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); - _3D_1.init("Camera4", "焊后检测-3D相机1" , { { 5400, 1,5340,"5402","5404",-1} }, 1, & tongDetParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); + lv_top_1.init("Camera1", "焊印检测-内-铜-相机1(90°)", { { 5360, 1,5340,"5362","5364",-1} }, 1, &tongZhongHan2dCeLiangParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); + lv_left45_2.init("Camera2", "翻折撕裂-内-铜-相机2(45°)", { { 5370, 1,5340,"5372","5374",-1} }, 1, &tongZhongHan2dCeMianParams[0], _serviceManager, bridge, zhongHan_hanHouCeMian); + lv_right45_3.init("Camera3", "翻折撕裂-内-铜-相机3(45°)", { { 5380,1,5340,"5382","5384",5406} }, 1, &tongZhongHan2dCeMianParams[1], _serviceManager, bridge, zhongHan_hanHouCeMian); + _3D_1.init("Camera4", "焊后检测-3D相机1" , { { 5400, 1,5340,"5402","5404",-1} }, 1, &tongZhongHan3DParams, _serviceManager, bridge, zhongHan_hanHou3D); - tong_top_5.init("Camera5", "焊印检测-外-铝-相机5(90°)", { { 5440, 1,5420,"5442","5444",-1} }, 1, &tongDetParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); - tong_left45_6.init("Camere6", "翻折撕裂-外-铝-相机6(45°)", { { 5450,1,5420,"5452","5454",-1} }, 1, &lvDetParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); - tong_right45_7.init("Camera7", "翻折撕裂-外-铝-相机7(45°)", { { 5460, 1,5420,"5462","5464",-1} }, 1, &lvDetParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); - _3D_2.init("Camera8", "焊后检测-3D相机2", { { 5480, 1,5420,"5482","5484",-1} }, 1, &tongDetParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); + tong_top_5.init("Camera5", "焊印检测-外-铝-相机5(90°)", { { 5440, 1,5420,"5442","5444",-1} }, 1, &lvZhongHan2dCeLiangParams, _serviceManager, bridge, zhongHan_hanHouCeLiang); + tong_left45_6.init("Camere6", "翻折撕裂-外-铝-相机6(45°)", { { 5450,1,5420,"5452","5454",-1} }, 1, &lvZhongHan2dCeMianParams[0], _serviceManager, bridge, zhongHan_hanHouCeMian); + tong_right45_7.init("Camera7", "翻折撕裂-外-铝-相机7(45°)", { { 5460, 1,5420,"5462","5464",5486} }, 1, &lvZhongHan2dCeMianParams[1], _serviceManager, bridge, zhongHan_hanHouCeMian); + _3D_2.init("Camera8", "焊后检测-3D相机2", { { 5480, 1,5420,"5482","5484",-1} }, 1, &lvZhongHan3DParams, _serviceManager, bridge, zhongHan_hanHou3D); #pragma endregion return true; } diff --git a/src/mission/581_ZhongHan_hanhou/CMakeLists.txt b/src/mission/581_ZhongHan_hanhou/CMakeLists.txt index bba2e81..d94ce25 100644 --- a/src/mission/581_ZhongHan_hanhou/CMakeLists.txt +++ b/src/mission/581_ZhongHan_hanhou/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) diff --git a/src/mission/581_ZhongHan_hanqian/CMakeLists.txt b/src/mission/581_ZhongHan_hanqian/CMakeLists.txt index bba2e81..d94ce25 100644 --- a/src/mission/581_ZhongHan_hanqian/CMakeLists.txt +++ b/src/mission/581_ZhongHan_hanqian/CMakeLists.txt @@ -15,7 +15,7 @@ message(" ENV_TARGET : " ${ENV_TARGET} ) include_directories( ${PROJECT_ROOT_PATH}/src/core ${PROJECT_ROOT_PATH}/src/core/handleAi - ${PROJECT_ROOT_PATH}/src/core/back + ${PROJECT_ROOT_PATH}/src/core/service ${PROJECT_ROOT_PATH}/src/core/sql ${ENV_INCLUDE} ) diff --git a/subModule/Hd_CameraModule_HIK_hard_GIGE/Hd_CameraModule_HIK_hard_GIGE.cpp b/subModule/Hd_CameraModule_HIK_hard_GIGE/Hd_CameraModule_HIK_hard_GIGE.cpp index 7b1a3be..e79ee85 100644 --- a/subModule/Hd_CameraModule_HIK_hard_GIGE/Hd_CameraModule_HIK_hard_GIGE.cpp +++ b/subModule/Hd_CameraModule_HIK_hard_GIGE/Hd_CameraModule_HIK_hard_GIGE.cpp @@ -96,7 +96,7 @@ int SearchDevice() { memset(&m_stDevList, 0, sizeof(MV_CC_DEVICE_INFO_LIST)); // ch:枚举子网内所有设备 | en:Enumerate all devices within subnet - int nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE || MV_USB_DEVICE, &m_stDevList); + int nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &m_stDevList); for (int i = 0; i < m_stDevList.nDeviceNum; i++) { //SN @@ -535,7 +535,6 @@ bool Hd_CameraModule_HIK_hard_PCIE::run() { n_mutex->unlock(); result = true; - std::cout << "\n gotImg" << std::endl; break; } if (cnt > 120) { diff --git a/subModule/Hd_CameraModule_HIK_hard_PCIE/Hd_CameraModule_HIK_hard_PCIE.cpp b/subModule/Hd_CameraModule_HIK_hard_PCIE/Hd_CameraModule_HIK_hard_PCIE.cpp index be63ad9..074e3dd 100644 --- a/subModule/Hd_CameraModule_HIK_hard_PCIE/Hd_CameraModule_HIK_hard_PCIE.cpp +++ b/subModule/Hd_CameraModule_HIK_hard_PCIE/Hd_CameraModule_HIK_hard_PCIE.cpp @@ -133,7 +133,7 @@ int SearchDevice() return -1; } -bool connctDevice() +bool connectDevice() { //MV_CC_Initialize(); index = -1; @@ -196,6 +196,7 @@ void SetGain(float GainValue) void __stdcall ReconnectDevice(unsigned int nMsgType, void* pUser) { mv_Status = "disconnect"; + std::cout << "相机重连中..." << std::endl; qDebug() << "[Error] " << " MV camera disconnects!"; moduleStatus = false; if (nMsgType == MV_EXCEPTION_DEV_DISCONNECT) @@ -207,7 +208,7 @@ void __stdcall ReconnectDevice(unsigned int nMsgType, void* pUser) while (1) { Sleep(100); - if (connctDevice() == true) + if (connectDevice() == true) { qWarning() << "[Hd_CameraModule_HIK] " << " Hd_CameraModule_HIK create success again! "; moduleStatus = true; @@ -335,10 +336,13 @@ Hd_CameraModule_HIK_hard_PCIE::Hd_CameraModule_HIK_hard_PCIE() //关闭设备 void CloseDevice() { - + if (handle == nullptr) + return; unsigned int nRet = MV_CC_StopGrabbing(handle); - if(nRet == 0) - CloseHandle(handle); + if (nRet == 0) { + MV_CC_CloseDevice(handle); + MV_CC_DestroyHandle(handle); + } //nRet = MV_CC_CloseDevice(handle); //nRet = MV_CC_DestroyHandle(handle); } @@ -490,7 +494,7 @@ bool Hd_CameraModule_HIK_hard_PCIE::initParas(QByteArray& initData) } } - bool ifOk = connctDevice(); + bool ifOk = connectDevice(); if (ifOk == true) { if (expourseTimeList.size() > 0) @@ -529,15 +533,15 @@ bool Hd_CameraModule_HIK_hard_PCIE::run() std::cout << "\n gotImg" << std::endl; break; } - if (cnt > 2000) { + if (cnt > 150) { n_mutex->lock(); - queuepic.push_back(cv::imread("D://Image_20250809202445856.bmp", -1)); - //queuepic.push_back(cv::Mat(200, 200, CV_8UC3, cv::Scalar(0, 255, 0))); //返回空图 + //queuepic.push_back(cv::imread("D://Image_20250809202445856.bmp", -1)); + queuepic.push_back(cv::Mat(200, 200, CV_8UC3, cv::Scalar(0, 255, 0))); //返回空图 std::cout << "timeout" << std::endl; n_mutex->unlock(); return true; } - QThread::msleep(1); + QThread::msleep(10); } //考虑到误触发、漏触发的图,在这里进行设置。 //需要设置曝光时 diff --git a/subModule/Hd_CameraModule_HIK_hard_USB/Hd_CameraModule_HIK_hard_USB.cpp b/subModule/Hd_CameraModule_HIK_hard_USB/Hd_CameraModule_HIK_hard_USB.cpp index 28db71d..8c242aa 100644 --- a/subModule/Hd_CameraModule_HIK_hard_USB/Hd_CameraModule_HIK_hard_USB.cpp +++ b/subModule/Hd_CameraModule_HIK_hard_USB/Hd_CameraModule_HIK_hard_USB.cpp @@ -663,14 +663,14 @@ bool Hd_CameraModule_HIK_soft::run() std::cout << "\n gotImg" << std::endl; break; } - if (cnt > 20000) { + if (cnt > 150) { queuepic.push_back(cv::Mat(200, 200, CV_8UC3, cv::Scalar(0, 255, 0))); //返回空图 std::cout << "timeout" << std::endl; n_mutex->unlock(); return true; } n_mutex->unlock(); - QThread::msleep(1); + QThread::msleep(10); } //qDebug() << "[INFO] " << " expourseTimeList " << QString::fromStdString(Username); diff --git a/subModule/Hd_CameraModule_HIK_soft_GIGE/Hd_CameraModule_HIK_soft_GIGE.cpp b/subModule/Hd_CameraModule_HIK_soft_GIGE/Hd_CameraModule_HIK_soft_GIGE.cpp index b5b0ec2..32eadfd 100644 --- a/subModule/Hd_CameraModule_HIK_soft_GIGE/Hd_CameraModule_HIK_soft_GIGE.cpp +++ b/subModule/Hd_CameraModule_HIK_soft_GIGE/Hd_CameraModule_HIK_soft_GIGE.cpp @@ -95,7 +95,7 @@ int SearchDevice() { memset(&m_stDevList, 0, sizeof(MV_CC_DEVICE_INFO_LIST)); // ch:枚举子网内所有设备 | en:Enumerate all devices within subnet - int nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE || MV_USB_DEVICE, &m_stDevList); + int nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &m_stDevList); for (int i = 0; i < m_stDevList.nDeviceNum; i++) { //SN