From 4ce5fc04457e9f3d9f46aec5f1982bc452e3284d Mon Sep 17 00:00:00 2001 From: RCi <15089880081@163.com> Date: Sat, 15 Nov 2025 15:48:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=87=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/service/imgSaveThreadManager.h | 16 +- src/core/service/paramSyncManager.h | 27 ++- src/cutImage/CMakeLists.txt | 69 +++++++ src/cutImage/baoMo.hpp | 21 +++ src/cutImage/main.cpp | 7 + src/launcher/GUI/pages/settingWidget.h | 1 - src/launcher/GUI/paramEditorDialog.h | 143 ++++++++------ src/mission/581-Mfd_yh_mh_hh/581-Mfd.h | 1 + .../581_YuHan_hanhou/581_YuHan_hanhou.cpp | 84 ++++++--- .../YuHan/581_YuHan_hanhou/581_YuHan_hanhou.h | 46 ++--- .../581_YuHan_hanqian/581_YuHan_hanqian.h | 15 +- .../581_YuHan_jixing/581_YuHan_jixing.cpp | 8 + .../YuHan/581_YuHan_jixing/581_YuHan_jixing.h | 177 ++++++++++++++++++ .../YuHan/581_YuHan_jixing/CMakeLists.txt | 77 ++++++++ subModule.zip | Bin 0 -> 33324 bytes 15 files changed, 565 insertions(+), 127 deletions(-) create mode 100644 src/cutImage/CMakeLists.txt create mode 100644 src/cutImage/baoMo.hpp create mode 100644 src/cutImage/main.cpp create mode 100644 src/mission/YuHan/581_YuHan_jixing/581_YuHan_jixing.cpp create mode 100644 src/mission/YuHan/581_YuHan_jixing/581_YuHan_jixing.h create mode 100644 src/mission/YuHan/581_YuHan_jixing/CMakeLists.txt create mode 100644 subModule.zip diff --git a/src/core/service/imgSaveThreadManager.h b/src/core/service/imgSaveThreadManager.h index d26cdeb..ac50b40 100644 --- a/src/core/service/imgSaveThreadManager.h +++ b/src/core/service/imgSaveThreadManager.h @@ -254,19 +254,19 @@ public: imgSaveThreadManager(std::string pathIn, paramSyncManager* paramSyncManagerIn) { _ImageSavePathFormatter.rootDir = QString::fromStdString(pathIn); _paramSyncManager = paramSyncManagerIn; - saveOri = _paramSyncManager->get(_paramSyncManager->getGroup(0), "存储原图", true).toBool(); - saveRes = _paramSyncManager->get(_paramSyncManager->getGroup(0), "存储结果图", true).toBool(); - saveSeg = _paramSyncManager->get(_paramSyncManager->getGroup(0), "存储Seg图", true).toBool(); - saveCls = _paramSyncManager->get(_paramSyncManager->getGroup(0), "存储Cls图", true).toBool(); + saveOri = _paramSyncManager->get("通用设置", "存储原图", true).toBool(); + saveRes = _paramSyncManager->get("通用设置", "存储结果图", true).toBool(); + saveSeg = _paramSyncManager->get("通用设置", "存储Seg图", true).toBool(); + saveCls = _paramSyncManager->get("通用设置", "存储Cls图", true).toBool(); } ~imgSaveThreadManager() {}; imgSaveThread saveThread; void saveImage(std::shared_ptr data) { if (getParamTimer.elapsed() > 5000) { - saveOri = _paramSyncManager->get(_paramSyncManager->getGroup(0), "存储原图", true).toBool(); - saveRes = _paramSyncManager->get(_paramSyncManager->getGroup(0), "存储结果图", true).toBool(); - saveSeg = _paramSyncManager->get(_paramSyncManager->getGroup(0), "存储Seg图", true).toBool(); - saveCls = _paramSyncManager->get(_paramSyncManager->getGroup(0), "存储Cls图", true).toBool(); + saveOri = _paramSyncManager->get("通用设置", "存储原图", true).toBool(); + saveRes = _paramSyncManager->get("通用设置", "存储结果图", true).toBool(); + saveSeg = _paramSyncManager->get("通用设置", "存储Seg图", true).toBool(); + saveCls = _paramSyncManager->get("通用设置", "存储Cls图", true).toBool(); getParamTimer.restart(); } diff --git a/src/core/service/paramSyncManager.h b/src/core/service/paramSyncManager.h index 9ba2fd8..f1c56c4 100644 --- a/src/core/service/paramSyncManager.h +++ b/src/core/service/paramSyncManager.h @@ -76,8 +76,15 @@ public: QMutexLocker locker(&globalMutex); std::vector> result; updateJsonFromMap(); - for (auto it = map.begin(); it != map.end(); ++it) { - result.emplace_back(it.key(), it.value()); + for (auto it = json.begin(); it != json.end(); ++it) { + if (it->is_object()) { + std::string groupKey = it.key(); + for (auto it = json[groupKey].begin(); it != json[groupKey].end(); ++it) { + QString key = QString::fromStdString(it.key()); + QVariant value = jsonToQVariant(it.value()); + result.emplace_back(QString::fromStdString(groupKey) + "." + key, value); + } + } } return result; } @@ -126,14 +133,16 @@ public: } for (auto missionIt = jsonInput.begin(); missionIt != jsonInput.end(); ++missionIt) { - QString groupName = QString::fromStdString(missionIt.key()); - missionKey.push_back(groupName); + if (missionIt->is_object()) { + QString groupName = QString::fromStdString(missionIt.key()); + missionKey.push_back(groupName); - const auto& innerObject = *missionIt; - for (auto innerIt = innerObject.begin(); innerIt != innerObject.end(); ++innerIt) { - QString key = groupName + "." + QString::fromStdString(innerIt.key()); - QVariant value = jsonToQVariant(innerIt.value()); - map.insert(key, value); + const auto& innerObject = *missionIt; + for (auto innerIt = innerObject.begin(); innerIt != innerObject.end(); ++innerIt) { + QString key = groupName + "." + QString::fromStdString(innerIt.key()); + QVariant value = jsonToQVariant(innerIt.value()); + map.insert(key, value); + } } } } diff --git a/src/cutImage/CMakeLists.txt b/src/cutImage/CMakeLists.txt new file mode 100644 index 0000000..92e41bf --- /dev/null +++ b/src/cutImage/CMakeLists.txt @@ -0,0 +1,69 @@ +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) +STRING(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) +set(DLL_NAMES "halcon" "opencv" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${ENV_INCLUDE} + ${PROJECT_ROOT_PATH}/lib/${CMAKE_BUILD_TYPE} +) + +link_directories ( + ${ENV_LIB} +) +add_compile_options(/utf-8) +# qt引用 +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +find_package(Qt5 COMPONENTS ${qtlist} REQUIRED ) +foreach(item ${qtlist}) + list(APPEND lib_list Qt5::${item}) +endforeach() + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /openmp") + + +FILE(GLOB_RECURSE APP_HEADERPP_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp" ) +FILE(GLOB_RECURSE APP_HEADER_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.h" ) +FILE(GLOB_RECURSE APP_SRC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) +set(RESOURCES_DIR ${CMAKE_SOURCE_DIR}/resources/resources.qrc) +set(WIN_RC_DIR ${CMAKE_SOURCE_DIR}/resources/ff.rc) +include_directories(${CMAKE_BINARY_DIR}) +add_executable( ${CURRENT_FOLDER} + ${APP_HEADERPP_FILES} + ${APP_HEADER_FILES} + ${APP_SRC_FILES} + ${BREAKPAD_WINDOWS_CC_FILES} + ${APP_UI_FILES} + ${WIN_RC_DIR} + ${RESOURCES_DIR} ) +# 添加预处理定义 + +SET(ALL_DLL + ${ENV_TARGET} + ${lib_list} +) +#add_custom_target( +# run_cmd ALL +# COMMAND cmd /c "${ENVPATH}/qt5/bin/moc.exe ${PROJECT_ROOT_PATH}/include/frontendSignalHub.h -o ${CMAKE_CURRENT_SOURCE_DIR}/moc_qtBridge.cpp" +# COMMENT "Running command on every build" +#) +message("all_lib :" ${ALL_DLL}) +target_link_libraries(${CURRENT_FOLDER} ${ALL_DLL} ) +string(TIMESTAMP RUN_TIME) +message("C++ Standard: ${CMAKE_CXX_STANDARD}") +message( " model end : " ${CURRENT_FOLDER}:${RUN_TIME}) \ No newline at end of file diff --git a/src/cutImage/baoMo.hpp b/src/cutImage/baoMo.hpp new file mode 100644 index 0000000..7c6ab22 --- /dev/null +++ b/src/cutImage/baoMo.hpp @@ -0,0 +1,21 @@ +#include +#include +#include +using namespace std; + + +void baomoXiaDMianCut(const std::string path, const std::string format, const std::string outputPath) { + //int + //handdleAI::pSegmentation* seg = handdleAI::CreateSegObjS("./cutImgFiles/baoMoXiaDaMian_seg.BHD", 1, 0); + //std::vector images; + //cv::glob(path + "/*." + format, images, false); + //for (size_t i = 0; i < images.size(); i++) { + // cv::Mat srcImage = cv::imread(images[i],-1); + // if (srcImage.empty()) { + // std::cout << "ͼƬΪ¿գ¡" << std::endl; + // continue; + // } + // auto SegResult = ~seg->Evaluate(srcImage); + + //} +} \ No newline at end of file diff --git a/src/cutImage/main.cpp b/src/cutImage/main.cpp new file mode 100644 index 0000000..0b3a49d --- /dev/null +++ b/src/cutImage/main.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc, char* argv[]) +{ + std::cout << "hello world" << std::endl; + return 1; +} \ No newline at end of file diff --git a/src/launcher/GUI/pages/settingWidget.h b/src/launcher/GUI/pages/settingWidget.h index a3f9157..578121e 100644 --- a/src/launcher/GUI/pages/settingWidget.h +++ b/src/launcher/GUI/pages/settingWidget.h @@ -19,7 +19,6 @@ #include #include #include -#include "git_version.h" #include #include #include diff --git a/src/launcher/GUI/paramEditorDialog.h b/src/launcher/GUI/paramEditorDialog.h index 00df9c9..4fd03aa 100644 --- a/src/launcher/GUI/paramEditorDialog.h +++ b/src/launcher/GUI/paramEditorDialog.h @@ -65,8 +65,13 @@ public: void setParams(std::vector> params) { //保存所有参数 - m_allParams = params; - + for(auto& data : params) { + m_allParams.insert(data.first, data.second); + } + //生成参数修改控件的缓存 + //for (auto& it = params.begin(); it != params.end(); it++) { + // m_paramsEditItemsCache.insert(it->first, createEditorWidget(it->second, it->second.type())); + //} //清空现有数据 m_tableModel->setRowCount(0); m_editorWidgets.clear(); @@ -76,7 +81,7 @@ public: //提取分组信息 QSet groups; - QMap>> groupedParams; + QMap> groupedParams; for (auto& data : params) { const QString& fullKey = data.first; @@ -89,7 +94,7 @@ public: } groups.insert(groupName); - groupedParams[groupName].push_back(data); + groupedParams[groupName].push_back(fullKey); } //添加分组到侧边栏 @@ -103,9 +108,11 @@ public: } //保存分组参数 - m_groupedParams = groupedParams; - m_groupedParams["所有参数"] = params;// 添加所有参数选项 - + m_groupedParamsNames = groupedParams; + for (auto& it = params.begin(); it != params.end(); it++) { + m_groupedParamsNames["所有参数"].push_back(it->first); + } + //默认显示所有参数 displayGroupParams("所有参数"); @@ -116,51 +123,65 @@ public: 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 fullKey = keyItem->data(Qt::UserRole).toString();//获取完整键名 - if (fullKey.isEmpty()) { - QMessageBox::warning(this, "错误", QString("第 %1 行 key 为空").arg(row + 1)); - return; - } - - //解析分组组名和参数名 - QString group, name; - int dotIndex = fullKey.indexOf('.'); - if (dotIndex != -1) { - group = fullKey.left(dotIndex); - name = fullKey.mid(dotIndex + 1); - } - else { - group = "默认分组"; - name = fullKey; - } - - // 获取原始类型 - if (m_originalTypes.find(fullKey) == m_originalTypes.end()) { - QMessageBox::warning(this, "错误", QString("第 %1 行找不到原始类型信息").arg(row + 1)); - return; - } - - auto expectedType = m_originalTypes[fullKey]; - + //更新当前数据 + for (auto& it : m_editorWidgets) { + auto expectedType = m_originalTypes[it.first]; // 从编辑器获取值 QVariant convertedValue; QString errorMsg; - if (!getValueFromEditor(fullKey, expectedType, convertedValue, errorMsg)) { - QMessageBox::critical(this, "类型验证失败", QString("第 %1 行值验证失败:\n%2").arg(row + 1).arg(errorMsg)); - return; + if (!getValueFromEditor(it.first, expectedType, convertedValue, errorMsg)) { + qCritical() << (this, "本地类型验证失败", QString("%1值验证失败:\n%2").arg(it.first).arg(errorMsg)); + continue; } + if (m_allParams.contains(it.first)) { + m_allParams[it.first] = convertedValue; + } + } + std::vector> result; + + for (auto& it = m_allParams.begin(); it != m_allParams.end(); it++) { + //解析分组组名和参数名 + QString group, name; + int dotIndex = it.key().indexOf('.'); + if (dotIndex != -1) { + group = it.key().left(dotIndex); + name = it.key().mid(dotIndex + 1); + } + else { + group = "默认分组"; + name = it.key(); + } + emit fSignalHub::getInstance()->editParam(group, name, it.value()); + //QStandardItem* keyItem = m_tableModel->item(row, 0); + //if (!keyItem) { + // QMessageBox::warning(this, "错误", QString("第 %1 行数据不完整").arg(row + 1)); + // return; + //} + + //QString fullKey = keyItem->data(Qt::UserRole).toString();//获取完整键名 + //if (fullKey.isEmpty()) { + // QMessageBox::warning(this, "错误", QString("第 %1 行 key 为空").arg(row + 1)); + // return; + //} + + //// 获取原始类型 + //if (m_originalTypes.find(fullKey) == m_originalTypes.end()) { + // QMessageBox::warning(this, "错误", QString("第 %1 行找不到原始类型信息").arg(row + 1)); + // return; + //} + + //auto expectedType = m_originalTypes[fullKey]; + + //// 从编辑器获取值 + //QVariant convertedValue; + //QString errorMsg; + //if (!getValueFromEditor(fullKey, expectedType, convertedValue, errorMsg)) { + // QMessageBox::critical(this, "类型验证失败", QString("第 %1 行值验证失败:\n%2").arg(row + 1).arg(errorMsg)); + // return; + //} // 发送参数编辑信号 - emit fSignalHub::getInstance()->editParam(group, name, convertedValue); + //emit fSignalHub::getInstance()->editParam(group, name, convertedValue); } this->close(); delete this; @@ -187,8 +208,10 @@ private: QFrame* m_dividerLine; // 参数数据 - std::vector> m_allParams; - QMap>> m_groupedParams; + QMap m_allParams; + QMap> m_groupedParamsNames;//分组 名字 + //QMap m_paramsEditItemsCache; + void createSidebar() { m_sidebarWidget = new QWidget(this); @@ -367,20 +390,33 @@ private: // 清空表格 m_tableModel->setRowCount(0); + for(auto& it:m_editorWidgets) { + auto expectedType = m_originalTypes[it.first]; + // 从编辑器获取值 + QVariant convertedValue; + QString errorMsg; + if (!getValueFromEditor(it.first, expectedType, convertedValue, errorMsg)) { + qCritical()<<(this, "本地类型验证失败", QString("%1值验证失败:\n%2").arg(it.first).arg(errorMsg)); + continue; + } + if(m_allParams.contains(it.first)) { + m_allParams[it.first] = convertedValue; + } + } m_editorWidgets.clear(); - if (!m_groupedParams.contains(groupName)) { + if (!m_groupedParamsNames.contains(groupName)) { return; } - const auto& params = m_groupedParams[groupName]; + const auto& params = m_groupedParamsNames[groupName]; int row = 0; for (auto data : params) { m_tableModel->insertRow(row); - const QString& fullKey = data.first; - const QVariant& value = data.second; + const QString& fullKey = data; + const QVariant& value = m_allParams[fullKey]; QString displayName = fullKey; //分组显示,去掉分组前缀 @@ -432,7 +468,7 @@ private: case QVariant::Type::Int: { QSpinBox* spinBox = new QSpinBox(); spinBox->setMaximumWidth(160); - spinBox->setRange(-1e6, 1e6); + spinBox->setRange(INT_MIN, INT_MAX); spinBox->setValue(value.toInt()); spinBox->setToolTip(generateTypeHint(type)); return spinBox; @@ -441,6 +477,7 @@ private: QDoubleSpinBox* spinBox = new QDoubleSpinBox(); spinBox->setMaximumWidth(160); spinBox->setDecimals(3);//显示小位数 + spinBox->setRange(-1e6, 1e6); spinBox->setValue(value.toDouble()); spinBox->setToolTip(generateTypeHint(type)); return spinBox; 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 79d22d2..e6aa369 100644 --- a/src/mission/581-Mfd_yh_mh_hh/581-Mfd.h +++ b/src/mission/581-Mfd_yh_mh_hh/581-Mfd.h @@ -25,6 +25,7 @@ void exceptionResume(std::shared_ptr data, mfd_detParams* _detParams, y using namespace HDevEngineCpp; static bool ManHan(std::shared_ptr data, mfd_detParams* _detParams, yh_581_address address){ try { + data->service->_triggerManager->writeFloat("D100_1,2,3,4,5,6,7,8,9,9"); std::map sqlMapData; sqlMapData["项目"] = "满焊"; sqlMapData["相机"] = "相机" + address.triggerAddr; diff --git a/src/mission/YuHan/581_YuHan_hanhou/581_YuHan_hanhou.cpp b/src/mission/YuHan/581_YuHan_hanhou/581_YuHan_hanhou.cpp index 574994d..73cb514 100644 --- a/src/mission/YuHan/581_YuHan_hanhou/581_YuHan_hanhou.cpp +++ b/src/mission/YuHan/581_YuHan_hanhou/581_YuHan_hanhou.cpp @@ -88,8 +88,8 @@ halconResult halconAlgoProcess(cv::Mat& img, cv::Mat& Paint, handdleAI::pSegment if(out_Distance.Length() > 6) { double hanYin_Width = out_Distance[0].D();//焊印的宽度 double hanYin_Height = out_Distance[1].D();//焊印的高度 - double hanYIn_Area = out_Distance[2].D();//焊印的面积 - double jiEr_Width = out_Distance[3].D() * 0.013682 * 0.013682;//极耳的宽度 + double hanYIn_Area = out_Distance[2].D() * 0.013682 * 0.013682;//焊印的面积 + double jiEr_Width = out_Distance[3].D();//极耳的宽度 double jiEr_Height = out_Distance[4].D();//极耳的高度 double hanYin_Distance_Up = out_Distance[5].D();//焊印上边缘到极耳上边缘的距离 double hanYin_Distance_Down = out_Distance[6].D();//焊印下边缘到极耳下边缘的距离 @@ -114,10 +114,10 @@ halconResult halconAlgoProcess(cv::Mat& img, cv::Mat& Paint, handdleAI::pSegment return result; } bool hanHouCeLiang(std::shared_ptr data, yuHanHanHouParams* _detParams, sx_581_address address) { - QByteArray plcResult = "2"; + QByteArray plcResult = "1"; try { + //data->oriImgs[0] = cv::imread("D:/testImage/L.bmp", -1); cv::Mat sourceImg = data->oriImgs[0]; - //sourceImg = cv::imread("./testImages/" + std::to_string(_detParams->cameraIndex) + ".bmp"); cv::Mat Paint; sourceImg.copyTo(Paint); @@ -163,31 +163,58 @@ bool hanHouCeLiang(std::shared_ptr data, yuHanHanHouParams* _detParams } else { textPainter.putText(Paint, "撕裂/翻折NG", cv::Point(4200, 50), cv::Scalar(0, 0, 255)); - plcResult = "2"; + //plcResult = "2"; data->aiDetOK = false; } - if (halconResult.result == true) { - textPainter.putText(Paint, "算法OK", cv::Point(4200, 350), cv::Scalar(0, 255, 0)); + + silieResultText = "1"; fanzheResultText = "1"; +#pragma region 判断结果以及储存结果 + double hanyinChangLimitHigh = data->service->_paramSyncManager->get("焊印长","焊印长(上限)", 30.5).toDouble(); + double hanyinChangLimitLow = data->service->_paramSyncManager->get("焊印长", "焊印长(下限)", 29.5).toDouble(); + bool hanyinChangEnable = data->service->_paramSyncManager->get("焊印长", "焊印长判定启用", false).toBool(); + + double hanyinKuanLimitHigh = data->service->_paramSyncManager->get("焊印宽", "焊印宽(上限)", 10.5).toDouble(); + double hanyinKuanLimitLow = data->service->_paramSyncManager->get("焊印宽", "焊印宽(下限)", 9.5).toDouble(); + bool hanyinKuanEnable = data->service->_paramSyncManager->get("焊印宽", "焊印宽判定启用", false).toBool(); + + double jierChangLimitHigh = data->service->_paramSyncManager->get("极耳长度", "极耳长度(上限)", 21).toDouble(); + double jierChangLimitLow = data->service->_paramSyncManager->get("极耳长度", "极耳长度(下限)", 19).toDouble(); + bool jierChangEnable = data->service->_paramSyncManager->get("极耳长度", "极耳长度判定启用", false).toBool(); + + if (halconResult.hanYin_Width > hanyinChangLimitHigh || halconResult.hanYin_Width < hanyinChangLimitLow) { + textPainter.putText(Paint, "焊印长度" + controlAccuracy(halconResult.hanYin_Width, 4) + "mm", cv::Point(100, 300), cv::Scalar(0, 0, 255));//焊印宽度 + if (hanyinChangEnable == true) + plcResult = "2"; } else { - textPainter.putText(Paint, "算法NG", cv::Point(4200, 350), cv::Scalar(0, 0, 255)); - plcResult = "2"; - data->halconOK = false; + textPainter.putText(Paint, "焊印长度" + controlAccuracy(halconResult.hanYin_Width, 4) + "mm", cv::Point(100, 300), cv::Scalar(0, 255, 0));//焊印宽度 + } + + if (halconResult.hanYin_Height > hanyinKuanLimitHigh || halconResult.hanYin_Height < hanyinKuanLimitLow) { + textPainter.putText(Paint, "焊印宽度" + controlAccuracy(halconResult.hanYin_Height, 4) + "mm", cv::Point(100, 550), cv::Scalar(0, 0, 255));//焊印高度 + if (hanyinKuanEnable == true) + plcResult = "2"; + } + else { + textPainter.putText(Paint, "焊印宽度" + controlAccuracy(halconResult.hanYin_Height, 4) + "mm", cv::Point(100, 550), cv::Scalar(0, 255, 0));//焊印高度 } - plcResult = "1"; silieResultText = "1"; fanzheResultText = "1"; -#pragma region 判断结果以及储存结果 - /*double hanyinChangLimitHigh = data->service->_paramSyncManager->get(data->service->_paramSyncManager->getGroup(1), "焊印长(上限)", 30.5).toDouble(); - double hanyinChangLimitLow = data->service->_paramSyncManager->get(data->service->_paramSyncManager->getGroup(1), "焊印长(下限)", 29.5).toDouble(); - bool hanyinChangEnable = data->service->_paramSyncManager->get(data->service->_paramSyncManager->getGroup(1), "焊印长判定启用", false).toBool(); + if (halconResult.jiErHeight > jierChangLimitHigh || halconResult.jiErHeight < jierChangLimitLow) { + textPainter.putText(Paint, "极耳裁切长度" + controlAccuracy(halconResult.jiErHeight, 4) + "mm", cv::Point(100, 50), cv::Scalar(0, 0, 255));//极耳高度 + if (hanyinKuanEnable == true) + plcResult = "2"; + } + else { + textPainter.putText(Paint, "极耳裁切长度" + controlAccuracy(halconResult.jiErHeight, 4) + "mm", cv::Point(100, 50), cv::Scalar(0, 255, 0));//极耳高度 + } + if (plcResult == "2" || halconResult.result == false) { + textPainter.putText(Paint, "算法NG", cv::Point(4200, 350), cv::Scalar(0, 0, 255)); + data->halconOK = false; + } + if(data->service->_paramSyncManager->get("通用设置","强制OK", false).toBool() == true) + plcResult = "1"; - double hanyinKuanLimitHigh = data->service->_paramSyncManager->get(data->service->_paramSyncManager->getGroup(1), "焊印宽(上限)", 10.5).toDouble(); - double hanyinKuanLimitLow = data->service->_paramSyncManager->get(data->service->_paramSyncManager->getGroup(1), "焊印宽(下限)", 9.5).toDouble(); - bool hanyinKuanEnable = data->service->_paramSyncManager->get(data->service->_paramSyncManager->getGroup(1), "焊印宽判定启用", false).toBool(); - - double jierChangLimitHigh = data->service->_paramSyncManager->get(data->service->_paramSyncManager->getGroup(1), "极耳长度(上限)", 10.5).toDouble(); - double jierChangLimitLow = data->service->_paramSyncManager->get(data->service->_paramSyncManager->getGroup(1), "极耳长度(下限)", 9.5).toDouble(); - bool jierChangEnable = data->service->_paramSyncManager->get(data->service->_paramSyncManager->getGroup(1), "极耳长度判定启用", false).toBool();*/ + data->sqlVec.insert({ {"焊印长度",halconResult.hanYin_Width }, { "焊印宽度",halconResult.hanYin_Height }, { "极耳裁切长度",halconResult.jiErHeight } }); #pragma endregion data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 @@ -201,9 +228,9 @@ bool hanHouCeLiang(std::shared_ptr data, yuHanHanHouParams* _detParams data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 12) + "_" + QByteArray::number(halconResult.hanYin_Height));//发送数据-极耳高 //textPainter.putText(Paint, "极耳裁切宽度" + std::to_string(halconResult.jiErWidth), cv::Point(100, 50), cv::Scalar(0, 255, 0));//极耳宽度,暂时不显示 - textPainter.putText(Paint, "极耳裁切长度" + controlAccuracy(halconResult.jiErHeight, 4) + "mm", cv::Point(100, 50), cv::Scalar(0, 255, 0));//极耳高度 - textPainter.putText(Paint, "焊印长度" + controlAccuracy(halconResult.hanYin_Width, 4) + "mm", cv::Point(100, 300), cv::Scalar(0, 255, 0));//焊印宽度 - textPainter.putText(Paint, "焊印宽度" + controlAccuracy(halconResult.hanYin_Height, 4) + "mm", cv::Point(100, 550), cv::Scalar(0, 255, 0));//焊印高度 + + + textPainter.putText(Paint, "焊印面积" + controlAccuracy(halconResult.hanYin_Area, 4) + "mm²", cv::Point(100, 800), cv::Scalar(0, 255, 0));//焊印面积 textPainter.putText(Paint, "焊印上边缘到极耳上边缘高度" + controlAccuracy(halconResult.hanYin_Distance_Up, 4) + "mm", cv::Point(100, 1050), cv::Scalar(0, 255, 0)); textPainter.putText(Paint, "焊印下边缘到极耳下边缘高度" + controlAccuracy(halconResult.hanYin_Distance_Down, 4) + "mm", cv::Point(100, 1300), cv::Scalar(0, 255, 0)); @@ -223,13 +250,16 @@ bool hanHouCeLiang(std::shared_ptr data, yuHanHanHouParams* _detParams textPainter.putText(Paint, "NG", cv::Point(100, 50), cv::Scalar(0, 0, 255)); cv::resize(Paint, Paint, cv::Size(), 0.5, 0.5); data->resultImgs.push_back(Paint); - data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + if (data->service->_paramSyncManager->get("通用设置","强制OK", false).toBool() == true) + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_1");//结果反馈 + else + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_2");//结果反馈 return true; } bool hanHouDown(std::shared_ptr data, yuHanHanHouParams* _detParams, sx_581_address address) { QByteArray plcResult = "2"; - try{ + try { cv::Mat sourceImg = data->oriImgs[0]; //sourceImg = cv::imread("./testImages/"+ std::to_string(_detParams->cameraIndex) +".bmp"); cv::Mat Paint; diff --git a/src/mission/YuHan/581_YuHan_hanhou/581_YuHan_hanhou.h b/src/mission/YuHan/581_YuHan_hanhou/581_YuHan_hanhou.h index b6ad81d..5504f1c 100644 --- a/src/mission/YuHan/581_YuHan_hanhou/581_YuHan_hanhou.h +++ b/src/mission/YuHan/581_YuHan_hanhou/581_YuHan_hanhou.h @@ -1,4 +1,4 @@ -#include +#include #include #pragma region 算法 #include @@ -62,47 +62,47 @@ public: for (int i = 0; i < 2; i++) { yuHanHanHouParams tong_90; - tong_90.segYuHanDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanDingWei-90-T-seg.BHD", 64, 0); + /*tong_90.segYuHanDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanDingWei-90-T-seg.BHD", 64, 0); tong_90.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/seg-hanYinZhuaQu-T.BHD", 64, 0); tong_90.segHanHou = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/seg-yuHanhanHou-T.BHD", 64, 0); - tong_90.clsHanHou = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/cls-yuHanhanHou-T.BHD", 0, 1); + tong_90.clsHanHou = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/cls-yuHanhanHou-T.BHD", 0, 1);*/ tong_90_DetParams.push_back(tong_90); yuHanHanHouParams lv_90; - lv_90.segYuHanDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanDingWei-90-L-seg.BHD", 64, 1); + /*lv_90.segYuHanDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanDingWei-90-L-seg.BHD", 64, 1); lv_90.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/seg-hanYinZhuaQu-L.BHD", 64, 1); lv_90.segHanHou = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/seg-yuHanhanHou-L.BHD", 64, 1); - lv_90.clsHanHou = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/cls-yuHanhanHou-L.BHD", 1, 1); + lv_90.clsHanHou = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/cls-yuHanhanHou-L.BHD", 1, 1);*/ lv_90_DetParams.push_back(lv_90); } yuHanHanHouParams dibu_45_lv; - dibu_45_lv.segYuHanDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanDingWei-45-L-seg.BHD", 64, 1); - dibu_45_lv.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/seg-hanYinZhuaQu-L.BHD", 64, 1); - dibu_45_lv.segHanHou = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanhanHou-dibu-45-L-seg.BHD", 64, 1); - dibu_45_lv.clsHanHou = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/yuHanhanHou-dibu-45-L-cls.BHD", 1, 1); + //dibu_45_lv.segYuHanDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanDingWei-45-L-seg.BHD", 64, 1); + //dibu_45_lv.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/seg-hanYinZhuaQu-L.BHD", 64, 1); + //dibu_45_lv.segHanHou = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanhanHou-dibu-45-L-seg.BHD", 64, 1); + //dibu_45_lv.clsHanHou = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/yuHanhanHou-dibu-45-L-cls.BHD", 1, 1); dibu_45_lv_DetParams.push_back(dibu_45_lv); yuHanHanHouParams dibu_45_tong; - dibu_45_tong.segYuHanDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanDingWei-45-T-seg.BHD", 64, 0); - dibu_45_tong.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/seg-hanYinZhuaQu-T.BHD", 64, 0); - dibu_45_tong.segHanHou = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanhanHou-dibu-45-T-seg.BHD", 64, 0); - dibu_45_tong.clsHanHou = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/yuHanhanHou-dibu-45-T-cls.BHD", 0, 1); + //dibu_45_tong.segYuHanDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanDingWei-45-T-seg.BHD", 64, 0); + //dibu_45_tong.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/seg-hanYinZhuaQu-T.BHD", 64, 0); + //dibu_45_tong.segHanHou = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanhanHou-dibu-45-T-seg.BHD", 64, 0); + //dibu_45_tong.clsHanHou = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/yuHanhanHou-dibu-45-T-cls.BHD", 0, 1); dibu_45_tong_DetParams.push_back(dibu_45_tong); yuHanHanHouParams dingbu_45_lv; - dingbu_45_lv.segYuHanDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanDingWei-45-L-seg.BHD", 64, 1); - dingbu_45_lv.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/seg-hanYinZhuaQu-L.BHD", 64, 1); - dingbu_45_lv.segHanHou = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanhanHou-dingbu-45-L-seg.BHD", 64, 1); - dingbu_45_lv.clsHanHou = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/yuHanhanHou-45-L-cls.BHD", 1, 1);//未更新 + //dingbu_45_lv.segYuHanDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanDingWei-45-L-seg.BHD", 64, 1); + //dingbu_45_lv.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/seg-hanYinZhuaQu-L.BHD", 64, 1); + //dingbu_45_lv.segHanHou = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanhanHou-dingbu-45-L-seg.BHD", 64, 1); + //dingbu_45_lv.clsHanHou = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/yuHanhanHou-45-L-cls.BHD", 1, 1);//未更新 dingbu_45_lv_DetParams.push_back(dingbu_45_lv); yuHanHanHouParams dingbu_45_tong; - dingbu_45_tong.segYuHanDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanDingWei-45-T-seg.BHD", 64, 0); - dingbu_45_tong.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/seg-hanYinZhuaQu-T.BHD", 64, 0); - dingbu_45_tong.segHanHou = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanhanHou-dingbu-45-T-seg.BHD", 64, 0); - dingbu_45_tong.clsHanHou = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/yuHanhanHou-dingbu-45-T-cls.BHD", 0, 1); + //dingbu_45_tong.segYuHanDingWei = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanDingWei-45-T-seg.BHD", 64, 0); + //dingbu_45_tong.segHanYinZhuaQu = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/seg-hanYinZhuaQu-T.BHD", 64, 0); + //dingbu_45_tong.segHanHou = handdleAI::CreateSegObjS("./handleaiModels/yuhan_hanhou/yuHanhanHou-dingbu-45-T-seg.BHD", 64, 0); + //dingbu_45_tong.clsHanHou = handdleAI::CreateClsObjV3("./handleaiModels/yuhan_hanhou/yuHanhanHou-dingbu-45-T-cls.BHD", 0, 1); dingbu_45_tong_DetParams.push_back(dingbu_45_tong); initOnce = true; @@ -122,7 +122,7 @@ public: } bool initCustom() { #pragma region 注册触发器回调 - /*if (_serviceManager->_paramSyncManager->get(_serviceManager->_paramSyncManager->getGroup(1), "hanhou","OUT") == "OUT") { + if (_serviceManager->_paramSyncManager->get("内外侧判断", "hanhou", "OUT") == "OUT") { lv_top90_1.init("Camera1", "极耳尺寸-外-铝-相机1(顶部90°)", { { 522, 1,{540},"523","530",820} }, 1, &lv_90_DetParams[0], _serviceManager, bridge, hanHouCeLiang); lv_down90_2.init("Camera2", "极耳尺寸-外-铝-相机2(底部90°)", { { 524, 1,{540},"525","532",-1} }, 1, &lv_90_DetParams[1], _serviceManager, bridge, hanHouDown); tong_top90_3.init("Camera3", "极耳尺寸-外-铜-相机3(顶部90°)", { { 526,1,{540},"527","534",800} }, 1, &tong_90_DetParams[0], _serviceManager, bridge, hanHouCeLiang); @@ -143,7 +143,7 @@ public: lv_down45_6.init("Camera6", "底部撕裂-内-铝-相机6(底部45°)", { { 644, 1,{580},"645","652",-1} }, 1, &dibu_45_lv_DetParams[0], _serviceManager, bridge, hanHouDown); tong_top45_7.init("Camera7", "底部撕裂-内-铜-相机7(顶部45°)", { { 646, 1,{580},"647","654",-1} }, 1, &dingbu_45_tong_DetParams[0], _serviceManager, bridge, hanHouDown); tong_down45_8.init("Camera8", "底部撕裂-内-铜-相机8(底部45°)", { { 648, 1,{580},"649","656",-1} }, 1, &dibu_45_tong_DetParams[0], _serviceManager, bridge, hanHouDown); - }*/ + } #pragma endregion return true; } diff --git a/src/mission/YuHan/581_YuHan_hanqian/581_YuHan_hanqian.h b/src/mission/YuHan/581_YuHan_hanqian/581_YuHan_hanqian.h index 3283e86..5d13766 100644 --- a/src/mission/YuHan/581_YuHan_hanqian/581_YuHan_hanqian.h +++ b/src/mission/YuHan/581_YuHan_hanqian/581_YuHan_hanqian.h @@ -148,11 +148,12 @@ bool jiErFanZhe(std::shared_ptr data, yuHanHanQianParams* _detParams, } addImgToSaveStruct(HikMat[0], "0", getOriSaveFormat(SN, okng_tag));//存原图图 - return true; + return true; /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑调试用↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/ #else + bool detEnable = data->service->_paramSyncManager->get("极耳翻折","极耳翻折判定启用", true).toBool(); //存图数据加保存队列,加锁,避免数据访问冲突 - QByteArray plcResult = "2"; + QByteArray plcResult = "1"; cv::Mat sourceImg; data->oriImgs[0].copyTo(sourceImg); cv::Mat Paint; @@ -185,9 +186,11 @@ bool jiErFanZhe(std::shared_ptr data, yuHanHanQianParams* _detParams, { textPainter.putText(Paint, "NG", cv::Point(100, 50), cv::Scalar(0, 0, 255)); data->aiDetOK = false; - plcResult = "2"; + if(data->service->_paramSyncManager->get("通用设置","极耳翻折判定启用", true).toBool() == true) + plcResult = "2"; } - plcResult = "1";//强制OK + if(data->service->_paramSyncManager->get("通用设置","强制OK", false).toBool() == true) + plcResult = "1";//强制OK data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 data->segOriginImgs.push_back(seg_CutImg); data->segOriginImgs.push_back(seg_LocateImg); @@ -246,8 +249,8 @@ public: #pragma region 注册触发器回调 in_lv_top_1.init("Camera1", "极耳翻折-铝-内-相机1(顶部)", { { 682, 1,{760},"683","670",-1} }, 1, &lvDetParams[0], _serviceManager, bridge, jiErFanZhe); in_lv_down_2.init("Camera2", "极耳翻折-铝-内-相机2(底部)", { { 684, 1,{760},"685","672",-1} }, 1, &lvDetParams[1], _serviceManager, bridge, jiErFanZhe); - in_tong_top_3.init("Camera3", "极耳翻折-铜-内-相机3(顶部)", { { 686,1, {760},"687","674",-1} }, 1, &tongDetParams[0], _serviceManager, bridge, jiErFanZhe); - in_tong_down_4.init("Camera4", "极耳翻折-铜-内-相机4(底部)", { { 688, 1, {760},"689","676",-1} }, 1, &tongDetParams[1], _serviceManager, bridge, jiErFanZhe); + in_tong_top_3.init("Camera3", "极耳翻折-铜-内-相机3(顶部)", { { 686,1,{760},"687","674",-1} }, 1, &tongDetParams[0], _serviceManager, bridge, jiErFanZhe); + in_tong_down_4.init("Camera4", "极耳翻折-铜-内-相机4(底部)", { { 688, 1,{760},"689","676",-1} }, 1, &tongDetParams[1], _serviceManager, bridge, jiErFanZhe); out_lv_top_5.init("Camera5", "极耳翻折-铝-外-相机5(顶部)", { { 702,1,{720},"703","710",-1} }, 1, &lvDetParams[2], _serviceManager, bridge, jiErFanZhe); out_lv_down_6.init("Camera6", "极耳翻折-铝-外-相机6(底部)", { { 704, 1,{720},"705","712",-1} }, 1, &lvDetParams[3], _serviceManager, bridge, jiErFanZhe); diff --git a/src/mission/YuHan/581_YuHan_jixing/581_YuHan_jixing.cpp b/src/mission/YuHan/581_YuHan_jixing/581_YuHan_jixing.cpp new file mode 100644 index 0000000..7760162 --- /dev/null +++ b/src/mission/YuHan/581_YuHan_jixing/581_YuHan_jixing.cpp @@ -0,0 +1,8 @@ +#include <581_YuHan_jixing.h> + +mission* createMission() { + return new _581_YuHan_hanqian(); +} +void destroyMission(mission* _mission) { + delete _mission; +} \ No newline at end of file diff --git a/src/mission/YuHan/581_YuHan_jixing/581_YuHan_jixing.h b/src/mission/YuHan/581_YuHan_jixing/581_YuHan_jixing.h new file mode 100644 index 0000000..d67e066 --- /dev/null +++ b/src/mission/YuHan/581_YuHan_jixing/581_YuHan_jixing.h @@ -0,0 +1,177 @@ +#include +#include +#pragma region 算法 +#include +#include +#include +using namespace HDevEngineCpp; +#pragma endregion +class yuHanParams { +public: + + bool isTong = true; +}; +bool halconProcess(cv::Mat& sourceImg, cv::Mat& Paint) { + try { + HDevEngineCpp::HDevProcedure proc("YH_Color_Inspection"); + HDevEngineCpp::HDevProcedureCall procedureCall(proc); + HObject in_Image = MatToHObject(sourceImg); + procedureCall.SetInputIconicParamObject("in_Image", in_Image);//设置输入图像类变量 + procedureCall.Execute();//执行halcon程序 + HTuple out_ColorType; + HTuple out_Flag; + out_ColorType = procedureCall.GetOutputCtrlParamTuple("out_ColorType");//获取输出图像类变量 + out_Flag = procedureCall.GetOutputCtrlParamTuple("out_Flag");//获取输出控制类变量 + //相机2 OK为铜 NG为铝 + if (out_Flag.D() == 1) + { + if (out_ColorType.D() == 1)//铜 + { + //cv::putText(Paint, "OK", cv::Point(0, 550), 2, 10, cv::Scalar(0, 255, 0), 10); + return true; + } + else//铝 + { + //cv::putText(Paint, "NG", cv::Point(0, 550), 2, 10, cv::Scalar(0, 0, 255), 10); + return false; + } + } + else + { + //cv::putText(Paint, "NG", cv::Point(0, 550), 2, 10, cv::Scalar(0, 0, 255), 10); + return false; + } + } + catch (HDevEngineCpp::HDevEngineException& ex) { + + std::cout << "HDevEngineException: " << ex.Message() << std::endl; \ + qDebug() << "HDevEngineException: " << ex.Message(); \ + } + catch (HalconCpp::HException& ex) { + + std::cout << "Halcon Exception Caught!" << std::endl; \ + std::cout << "Error Code: " << ex.ErrorCode() << std::endl; \ + std::cout << "Error Message: " << ex.ErrorMessage() << std::endl; \ + qDebug() << "Halcon Exception Caught!"; \ + qDebug() << "Error Code: " << ex.ErrorCode(); \ + qDebug() << "Error Message: " << ex.ErrorMessage(); \ + } +} +bool jixingjiance(std::shared_ptr data, yuHanParams* detParams, sx_581_address address) { + try{ +#if 0 + /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓调试用↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/ + cv::Mat resultImage = img2D.clone(); + if (resultImage.channels() == 1) { + cv::cvtColor(resultImage, resultImage, cv::COLOR_GRAY2BGR); + } + QString okng_tag = "OK"; + cv::Mat seg_detectImage; + cv::resize(img2D, seg_detectImage, cv::Size(), 1, 1); + plcResult = "1"; + bool isNG = false; + if (DOGLIVE) { + std::vector>clsImgs; + bool isNG = handleAiProcess(seg_detectImage, resultImage, seg_jierFanZhe, cls_jierFanZhe, clsImgs); + for (int i = 0; i < clsImgs.size(); i++) { + addImgToSaveStruct(clsImgs[i].second, "0", getClsSaveFormat(SN, okng_tag, i, clsImgs[i].first)); + } + } + if (isNG == false)//OK + { + putTextHusky(resultImage, utf8_to_gbk("OK").c_str(), cv::Point(50, 50), cv::Scalar(0, 255, 0), 200); + okng_tag = "OK"; + } + else + { + putTextHusky(resultImage, utf8_to_gbk("NG").c_str(), cv::Point(50, 50), cv::Scalar(0, 0, 255), 200); + plcResult = "2"; + } + cv::resize(resultImage, resultImage, cv::Size(), 0.5, 0.5); + resMat.push_back(resultImage); + if (resMat.size() > 0) + { + addImgToSaveStruct(seg_detectImage, "0", getSegSaveFormat(SN, okng_tag));//存seg图 + addImgToSaveStruct(resMat[0], "0", getResSaveFormat(SN, okng_tag));//存结果图 + } + addImgToSaveStruct(HikMat[0], "0", getOriSaveFormat(SN, okng_tag));//存原图图 + + return true; + /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑调试用↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/ +#else + //存图数据加保存队列,加锁,避免数据访问冲突 + bool jiXingEnable = data->service->_paramSyncManager->get("极性","极性判定启用", true).toBool(); + QByteArray plcResult = "1"; + cv::Mat sourceImg; + data->oriImgs[0].copyTo(sourceImg); + cv::Mat Paint; + sourceImg.copyTo(Paint); + if (Paint.channels() == 1) + cvtColor(Paint, Paint, cv::COLOR_GRAY2BGR); + bool isNG = halconProcess(sourceImg, Paint); + + TextRendererZH textPainter(Paint.cols, Paint.rows, 300); + if (isNG)//OK + { + textPainter.putText(Paint, "OK", cv::Point(100, 50), cv::Scalar(0, 255, 0)); + } + else + { + textPainter.putText(Paint, "NG", cv::Point(100, 50), cv::Scalar(0, 0, 255)); + data->halconOK = false; + if(jiXingEnable == true) + plcResult = "2"; + } + if (jiXingEnable == false) + plcResult = "1";//强制OK + data->resultImgs.push_back(Paint); + data->service->_triggerManager->writeShort("D" + address.resultAddr + "_" + plcResult);//结果反馈 + return true; +#endif + } + CATCH_EXCEPTIONS + + qDebug() << "[Error] " << QString::fromUtf8("检测失败!"); + 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 + "_1");//结果反馈 + return true; +} +class _581_YuHan_hanqian : public mission { +public: + _581_SX_Single_triggerTemplate< yuHanParams> in_lv_top_1; + _581_SX_Single_triggerTemplate< yuHanParams> in_lv_down_2; + _581_SX_Single_triggerTemplate< yuHanParams> in_tong_top_3; + _581_SX_Single_triggerTemplate< yuHanParams> in_tong_down_4; + + yuHanParams tongDetParams; + yuHanParams lvDetParams; + void onConfigReloaded() { + HDevEngineCpp::HDevEngine myEngine; + myEngine.SetProcedurePath("./halconAlgo/AlgoLibrary_602.hdpl"); + + lvDetParams.isTong = false; + } + bool initCustom() { +#pragma region 注册触发器回调 + in_lv_top_1.init("Camera1", "极性检测-相机1", { { 400, 1,{410},"401","402",-1} }, 1, &lvDetParams, _serviceManager, bridge, jixingjiance); + in_lv_down_2.init("Camera2", "极性检测-相机2", { { 430, 1,{440},"431","432",-1 }}, 1, & lvDetParams, _serviceManager, bridge, jixingjiance); + in_tong_top_3.init("Camera3", "极性检测-相机3", { { 460,1,{470},"461","462",-1} }, 1, &tongDetParams, _serviceManager, bridge, jixingjiance); + in_tong_down_4.init("Camera4", "极性检测-相机4", { { 490, 1,{500},"491","492",-1 }}, 1, & tongDetParams, _serviceManager, bridge, jixingjiance); + +#pragma endregion + return true; + } +}; + + + + + +#pragma once \ No newline at end of file diff --git a/src/mission/YuHan/581_YuHan_jixing/CMakeLists.txt b/src/mission/YuHan/581_YuHan_jixing/CMakeLists.txt new file mode 100644 index 0000000..76e9200 --- /dev/null +++ b/src/mission/YuHan/581_YuHan_jixing/CMakeLists.txt @@ -0,0 +1,77 @@ +# 获取获取当前目录名 +get_filename_component(CURRENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} NAME) +string(TIMESTAMP RUN_TIME) +message( " model start : " ${CURRENT_FOLDER}:${RUN_TIME}) + +set(DLL_NAMES "opencv" "halcon" "handdleAI") +foreach(child ${DLL_NAMES}) + LIST(APPEND ENV_INCLUDE ${${child}_include}) + LIST(APPEND ENV_LIB ${${child}_lib}) + LIST(APPEND ENV_TARGET ${${child}_target_link}) +endforeach() +message(" ENV_INCLUDE : " ${ENV_INCLUDE} ) +message(" ENV_LIB : " ${ENV_LIB} ) +message(" ENV_TARGET : " ${ENV_TARGET} ) +include_directories( + ${PROJECT_ROOT_PATH}/src/core + ${PROJECT_ROOT_PATH}/src/core/handleAi + ${PROJECT_ROOT_PATH}/src/core/service + ${PROJECT_ROOT_PATH}/src/core/sql + ${ENV_INCLUDE} + ) + +link_directories ( + ${ENV_LIB} + + ) +add_compile_options(/utf-8) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) +SET(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC OFF) +set(CMAKE_AUTORCC ON) + +FILE(GLOB HEAD_FILES "*.h" ) +FILE(GLOB SRC_FILES "./*.cpp") +FILE(GLOB UI_FILES "./*.ui") + + +#find_package(Qt5 COMPONENTS Core 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/interface.h + ) + + + +set_target_properties(${CURRENT_FOLDER} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} + ) + + +target_link_libraries(${CURRENT_FOLDER} PRIVATE + ${ENV_TARGET} + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Network + ) + + +# target_compile_definitions(${CURRENT_FOLDER} PRIVATE HD_AIDEMO_MODULE_LIBRARY) + + + + +string(TIMESTAMP RUN_TIME) +message( " modelx end : " ${CURRENT_FOLDER}:${RUN_TIME}) +message( " header files "${HEAD_FILES}) diff --git a/subModule.zip b/subModule.zip new file mode 100644 index 0000000000000000000000000000000000000000..8ffcd7ef4a18b9e9e750fa31a3b6d5368cb2beff GIT binary patch literal 33324 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
(OAfL9y?iXp$#`m^rLp*G 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