From 9e2cd6eefc697860fb8259c812413ad6b8267a2f Mon Sep 17 00:00:00 2001 From: RCi <15089880081@163.com> Date: Fri, 17 Oct 2025 10:02:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=86jsoneditdialog?= =?UTF-8?q?=E3=80=82=20=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=AF=B9=E9=80=9A?= =?UTF-8?q?=E8=AE=AF=E6=A3=80=E6=B5=8B=E7=9A=84=E6=80=A7=E8=83=BD=E7=9B=91?= =?UTF-8?q?=E6=8E=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/qtBridge.h | 3 + include/sxGlobal.h | 1 + include/yhGlobal.h | 4 +- src/Core/CMakeLists.txt | 4 +- src/Core/back/triggerManager.cpp | 23 +- src/Core/back/triggerManager.h | 2 +- src/launcher/CMakeLists.txt | 4 +- src/launcher/GUI/JsonEditorDialog.h | 341 +++++++++++++++--- .../GUI/uiModule/components/sliderSwitch.h | 287 +++++++++++++++ .../GUI/uiModule/uiModule_runCommand.h | 23 +- .../uiModule/uiModule_softwareDataDisplay.h | 16 +- src/launcher/appConfig/appConfig.cpp | 13 +- src/launcher/main.cpp | 4 + src/mission/581-Mfd_yh_mh_hh/581-Mfd.h | 245 +++++++------ .../2025-10-17/屏幕截图 2025-10-17 095806.png | Bin 0 -> 20334 bytes .../2025-10-17/屏幕截图 2025-10-17 095811.png | Bin 0 -> 8007 bytes 16 files changed, 772 insertions(+), 198 deletions(-) create mode 100644 src/launcher/GUI/uiModule/components/sliderSwitch.h create mode 100644 updateLog/2025-10-17/屏幕截图 2025-10-17 095806.png create mode 100644 updateLog/2025-10-17/屏幕截图 2025-10-17 095811.png diff --git a/include/qtBridge.h b/include/qtBridge.h index a75ea8f..2a2a177 100644 --- a/include/qtBridge.h +++ b/include/qtBridge.h @@ -68,5 +68,8 @@ signals: 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 sendTriggerManagerLoopData(int loopCounterPerMin,int maxInterval); }; #pragma once \ No newline at end of file diff --git a/include/sxGlobal.h b/include/sxGlobal.h index c725f9f..5c043f0 100644 --- a/include/sxGlobal.h +++ b/include/sxGlobal.h @@ -5,6 +5,7 @@ #include #include #pragma region 基础函数 + #define CATCH_EXCEPTIONS \ catch (HDevEngineCpp::HDevEngineException& ex) { \ std::cout << "HDevEngineException: " << ex.Message() << std::endl; \ diff --git a/include/yhGlobal.h b/include/yhGlobal.h index f84e450..fabc3ea 100644 --- a/include/yhGlobal.h +++ b/include/yhGlobal.h @@ -231,7 +231,7 @@ public: evaluate(i, byteArray);}, ""); //注册清除完成信号触发器 任意触发地址归零时触发一次 _serviceManager->_triggerManager->registerCallback(address[i].triggerAddr, 0, [this, i](int triggerPos, int triggerValue, QByteArray& byteArray) { - _serviceManager->_triggerManager->writeShort("D" + address[0].completeAddr + "_0"); }, "D" + address[0].completeAddr.toStdString() + "清零"); + _serviceManager->_triggerManager->writeShort("D" + QByteArray::number(address[0].completeAddr.toInt() + i * 2) + "_0"); }, "D" + address[0].completeAddr.toStdString() + "清零"); } for (int i = 0; i < address.size(); i++) { _serviceManager->_triggerManager->registerPlcValueViewer(viewerNameIN + "_" + QString::number(i), @@ -246,7 +246,7 @@ public: } //获取图片并进行检测 void evaluate(int batteryOffset, QByteArray& byteArray) { - _serviceManager->_triggerManager->writeShort("D" + address[0].completeAddr + "_1");//完成信号 固定只发在第0位 + _serviceManager->_triggerManager->writeShort("D" + QByteArray::number(address[0].completeAddr.toInt() + batteryOffset) + "_1");//完成信号 根据偏移发送 std::string sn = _serviceManager->_triggerManager->readSnFromQByteArray(byteArray, address[batteryOffset].snAddr, false, false, -1); auto camera = _serviceManager->_cameraManager->getCamera(cameraName); camera->clearCache(); diff --git a/src/Core/CMakeLists.txt b/src/Core/CMakeLists.txt index 5ab2d26..04689e6 100644 --- a/src/Core/CMakeLists.txt +++ b/src/Core/CMakeLists.txt @@ -56,8 +56,8 @@ FILE(GLOB_RECURSE APP_UI_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.ui") FILE(GLOB_RECURSE APP_UI_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.cc") INCLUDE_DIRECTORIES(${CMAEK_CURRENT_BINARY_DIR}) -set(RESOURCES_DIR ${CMAKE_SOURCE_DIR}"/resources/resources.qrc") -set(WIN_RC_DIR ${CMAKE_SOURCE_DIR}"/resources/ff.rc") +set(RESOURCES_DIR ${CMAKE_SOURCE_DIR}/resources/resources.qrc) +set(WIN_RC_DIR ${CMAKE_SOURCE_DIR}/resources/ff.rc) # 生成动态库(DLL/.so/.dylib) FILE(GLOB CORE_HEADER_FILES "${PROJECT_ROOT_PATH}/include/core/*.h") diff --git a/src/Core/back/triggerManager.cpp b/src/Core/back/triggerManager.cpp index 4cc3c11..d479160 100644 --- a/src/Core/back/triggerManager.cpp +++ b/src/Core/back/triggerManager.cpp @@ -33,11 +33,11 @@ void triggerManager::run(){ if(connectHost() == false) return; QThread::msleep(1000); - QElapsedTimer heartTimer, loopCounterCollectTimer; - int loopCounter = 0; - while (runFlag){ + QElapsedTimer heartTimer, loopCounterCollectTimer,loopIntervalTimer; + int loopCounter = 0,maxIntervalLog = 0,interval = 0; + loopIntervalTimer.start(); + while (runFlag){ reciveCommond(); - QThread::msleep(sleepIntervalMS); //心跳 if (heartTimer.elapsed()> heartBeatIntervalMS) { sendFinsMsg(heartBeatCmds[heartBeatCmdCnt++]); @@ -45,12 +45,15 @@ void triggerManager::run(){ heartBeatCmdCnt = 0; heartTimer.start(); } - // if (loopCounterCollectTimer.elapsed() > 1000) { - //qDebug() << "loop counter:" << loopCounter << " time:" << loopCounterCollectTimer.elapsed(); - // loopCounter = 0; - // loopCounterCollectTimer.start(); - // } - // loopCounter++; + if (loopCounterCollectTimer.elapsed() > 3000) { + emit bridge->sendTriggerManagerLoopData(loopCounter / 3, maxIntervalLog); + maxIntervalLog = 0; + loopCounter = 0; + loopCounterCollectTimer.start(); + } + loopCounter++; + QThread::msleep(qMax(0, sleepIntervalMS - (int)loopIntervalTimer.elapsed())); + maxIntervalLog = qMax(maxIntervalLog, (int)loopIntervalTimer.restart()); } } bool triggerManager::connectHost(){ diff --git a/src/Core/back/triggerManager.h b/src/Core/back/triggerManager.h index 830d162..0ee9189 100644 --- a/src/Core/back/triggerManager.h +++ b/src/Core/back/triggerManager.h @@ -238,7 +238,7 @@ public: std::map> unionTriggerCallbackFuncs; //联合条件的触发器注册 bool registerUnionCallback(std::vector> triggerAddressAndValue, - conditionMode mode, + conditionMode mode, uniontriggerCallback func, std::string funcName = "") { QMutexLocker locer(&mutex); if (triggerAddressAndValue.size() < 2) { diff --git a/src/launcher/CMakeLists.txt b/src/launcher/CMakeLists.txt index 41ffe9f..d5fdb58 100644 --- a/src/launcher/CMakeLists.txt +++ b/src/launcher/CMakeLists.txt @@ -58,8 +58,8 @@ FILE(GLOB_RECURSE APP_SRC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") FILE(GLOB_RECURSE APP_UI_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.ui") INCLUDE_DIRECTORIES(${CMAEK_CURRENT_BINARY_DIR}) -set(RESOURCES_DIR ${CMAKE_SOURCE_DIR}"/resources/resources.qrc") -set(WIN_RC_DIR ${CMAKE_SOURCE_DIR}"/resources/ff.rc") +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} diff --git a/src/launcher/GUI/JsonEditorDialog.h b/src/launcher/GUI/JsonEditorDialog.h index a270e8c..e2a6121 100644 --- a/src/launcher/GUI/JsonEditorDialog.h +++ b/src/launcher/GUI/JsonEditorDialog.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include @@ -17,6 +17,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include + class JsonParamTableEditor : public QDialog { Q_OBJECT @@ -35,12 +42,48 @@ public: : QDialog(parent) { this->setWindowTitle("JSON 表格编辑器"); this->resize(700, 450); - setWindowFlags(this->windowFlags() | Qt::FramelessWindowHint); // 去掉边框 //现在、、 + setWindowFlags(this->windowFlags() | Qt::FramelessWindowHint);// 去掉边框 //setAttribute(Qt::WA_TranslucentBackground); // 背景透明 this->setObjectName("JsonParamTableEditor"); this->setStyleSheet(QString("QDialog#JsonParamTableEditor {background-color: rgb(%1);border: 2px solid #000000;border-radius: 6px;}").arg(sColor_Blue)); - m_tableWidget = new QTableWidget(0, 2, this); - m_tableWidget->setHorizontalHeaderLabels({ "选项", "值" }); + + 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, ""); @@ -54,65 +97,119 @@ public: btnLayout->addWidget(cancelBtn); QVBoxLayout* mainLayout = new QVBoxLayout(this); - mainLayout->addWidget(m_tableWidget); + mainLayout->addWidget(m_tableView); mainLayout->addLayout(btnLayout); - - // 设置表格样式和属性 - m_tableWidget->setAlternatingRowColors(true); - m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); - m_tableWidget->horizontalHeader()->setStretchLastSection(true); - m_tableWidget->setColumnWidth(0, 200); // 选项列宽度 - m_tableWidget->setStyleSheet(QString(R"( - QTableWidget { - background-color: rgb(%1); - font-family: "Microsoft YaHei"; - color: #ffffff; - gridline-color: #555555; - } - QHeaderView::section { - background-color: #0078d7; - padding: 4px; - font-weight: bold; - color: #ffffff; - } - QTableWidget::item:selected { - background-color: #0078d7; - } - )").arg(sColor_Blue)); + 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 { + 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 setJsonObject(const nlohmann::json& json) { - m_tableWidget->setRowCount(0); + m_tableModel->setRowCount(0); m_originalJson = json; // 保存原始JSON m_originalTypes.clear(); // 清空原始类型映射 + m_editorWidgets.clear(); if (!json.is_object()) return; + int row = 0; for (auto it = json.begin(); it != json.end(); ++it) { - int row = m_tableWidget->rowCount(); - m_tableWidget->insertRow(row); + m_tableModel->insertRow(row); const std::string& key = it.key(); const nlohmann::json& value = it.value(); - + + //// 设置行号(不可编辑) + //QStandardItem* rowItem = new QStandardItem(QString::number(row + 1)); + //rowItem->setEditable(false); + //rowItem->setBackground(QColor(60, 60, 60)); + //rowItem->setForeground(QColor(255, 255, 255)); + //m_tableModel->setItem(row, 0, rowItem); + // 设置键名(不可编辑) - QTableWidgetItem* keyItem = new QTableWidgetItem(QString::fromStdString(key)); - keyItem->setFlags(keyItem->flags() & ~Qt::ItemIsEditable); - keyItem->setBackground(QColor(80, 80, 80)); - m_tableWidget->setItem(row, 0, keyItem); + QStandardItem* keyItem = new QStandardItem(QString::fromStdString(key)); + keyItem->setEditable(false); + keyItem->setBackground(QColor(60, 60, 60)); + keyItem->setForeground(QColor(255, 255, 255)); + m_tableModel->setItem(row, 0, keyItem); // 确定类型(内部使用,不显示) JsonValueType valueType = getJsonValueType(value); m_originalTypes[key] = valueType; - // 设置值(可编辑,但需要类型验证) - QString valueText = formatValueForDisplay(value, valueType); - QTableWidgetItem* valueItem = new QTableWidgetItem(valueText); - valueItem->setToolTip(generateTypeHint(valueType)); - m_tableWidget->setItem(row, 1, valueItem); + ////为所有类型都创建数据项 + //QString valueText = formatValueForDisplay(value, valueType); + //QStandardItem* valueItem = new QStandardItem(valueText); + //valueItem->setToolTip(generateTypeHint(valueType)); + //valueItem->setBackground(QColor(60, 60, 60)); + //valueItem->setForeground(QColor(255, 255, 255)); + //m_tableModel->setItem(row, 1, valueItem); + + //为特定类型创建专用编辑器控件 + QWidget* editorWidget = createEditorWidget(value, valueType); + if (editorWidget) { + m_tableView->setIndexWidget(m_tableModel->index(row, 1), editorWidget); + m_editorWidgets[key] = editorWidget; + } + else { + // 默认为文本显示 + QString valueText= formatValueForDisplay(value, valueType); + QStandardItem* valueItem = new QStandardItem(valueText); + valueItem->setToolTip(generateTypeHint(valueType)); + m_tableModel->setItem(row, 1, valueItem); + } + row++; } - m_tableWidget->resizeColumnsToContents(); } signals: @@ -122,24 +219,20 @@ private slots: void onAccept() { nlohmann::json result = nlohmann::json::object(); - for (int row = 0; row < m_tableWidget->rowCount(); ++row) { - auto* keyItem = m_tableWidget->item(row, 0); - auto* valItem = m_tableWidget->item(row, 1); - - if (!keyItem || !valItem) { + 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(); - QString valStr = valItem->text().trimmed(); - if (key.isEmpty()) { QMessageBox::warning(this, "错误", QString("第 %1 行 key 为空").arg(row + 1)); return; } - // 获取原始类型 + //获取原始类型 std::string keyStd = key.toStdString(); if (m_originalTypes.find(keyStd) == m_originalTypes.end()) { QMessageBox::warning(this, "错误", QString("第 %1 行找不到原始类型信息").arg(row + 1)); @@ -148,26 +241,160 @@ private slots: JsonValueType expectedType = m_originalTypes[keyStd]; - // 验证并转换值 + //从编辑器获取值 nlohmann::json convertedValue; QString errorMsg; - if (!validateAndConvertValue(valStr, expectedType, convertedValue, errorMsg)) { - QMessageBox::critical(this, "类型验证失败", - QString("第 %1 行值验证失败:\n%2").arg(row + 1).arg(errorMsg)); + if (!getValueFromEditor(keyStd, expectedType, convertedValue, errorMsg)) { + QMessageBox::critical(this, "类型验证失败",QString("第 %1 行值验证失败:\n%2").arg(row + 1).arg(errorMsg)); return; } - result[keyStd] = convertedValue; } + qDebug() << "Result JSON: " << QString::fromStdString(result.dump(4)); + emit jsonAccepted(result); accept(); } private: - QTableWidget* m_tableWidget; + QTableView* m_tableView; + QStandardItemModel* m_tableModel; nlohmann::json m_originalJson; // 保存原始JSON以便类型检查 - std::map m_originalTypes; // 保存每个键的原始类型 + std::map m_originalTypes;// 保存每个键的原始类型 + std::map m_editorWidgets;// 保存每个键对应的编辑器控件 + + // 根据类型创建不同的编辑器控件 + QWidget* createEditorWidget(const nlohmann::json& value, JsonValueType type) { + switch (type) { + case JsonValueType::Boolean: { + CircularSwitch* switchBtn = new CircularSwitch(); + switchBtn->setChecked(value.get()); + switchBtn->setToolTip(generateTypeHint(type)); + return switchBtn; + } + case JsonValueType::Number: { + QDoubleSpinBox* spinBox = new QDoubleSpinBox(); + spinBox->setRange(-1e18, 1e18); + spinBox->setDecimals(3);//显示小位数 + + if (value.is_number_integer()) { + spinBox->setValue(value.get()); + spinBox->setDecimals(0); + } + else { + spinBox->setValue(value.get()); + } + + spinBox->setToolTip(generateTypeHint(type)); + return spinBox; + } + case JsonValueType::String: { + QLineEdit* lineEdit = new QLineEdit(); + lineEdit->setText(QString::fromStdString(value.get())); + lineEdit->setToolTip(generateTypeHint(type)); + return lineEdit; + } + // 其他类型仍使用文本显示 + default: + return nullptr; + } + } + + bool getValueFromEditor(const std::string& key, JsonValueType expectedType, nlohmann::json& result, QString& errorMsg) { + auto it = m_editorWidgets.find(key); + + try { + if (it != m_editorWidgets.end()) { + // 有专用编辑器控件 + QWidget* widget = it->second; + switch (expectedType) { + case JsonValueType::Boolean: { + CircularSwitch* switchBtn = qobject_cast(widget); + if (switchBtn) { + bool value = switchBtn->isChecked(); + result = value; + // 更新模型数据 + updateModelData(key, value ? "true" : "false"); + return true; + } + errorMsg = "布尔值控件类型错误"; + return false; + } + case JsonValueType::Number: { + QDoubleSpinBox* spinBox = qobject_cast(widget); + if (spinBox) { + double value = spinBox->value(); + // 检查是否为整数 + if (spinBox->decimals() == 0 && value == floor(value)) { + result = static_cast(value); + updateModelData(key, QString::number(static_cast(value))); + } + else { + result = value; + updateModelData(key, QString::number(value)); + } + return true; + } + errorMsg = "数字控件类型错误"; + return false; + } + case JsonValueType::String: { + QLineEdit* lineEdit = qobject_cast(widget); + if (lineEdit) { + QString value = lineEdit->text().trimmed(); + result = value.toStdString(); + 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 std::string& key, const QString& value) { + 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) { + 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 ""; + } // 获取JSON值的类型 JsonValueType getJsonValueType(const nlohmann::json& value) { diff --git a/src/launcher/GUI/uiModule/components/sliderSwitch.h b/src/launcher/GUI/uiModule/components/sliderSwitch.h new file mode 100644 index 0000000..0b1378d --- /dev/null +++ b/src/launcher/GUI/uiModule/components/sliderSwitch.h @@ -0,0 +1,287 @@ +#ifndef CIRCULARSWITCH_H +#define CIRCULARSWITCH_H + +#include +#include +#include +#include +#include +#include + +class CircularSwitch : public QWidget +{ + Q_OBJECT + Q_PROPERTY(int knobPosition READ knobPosition WRITE setKnobPosition NOTIFY knobPositionChanged) + Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged) + +public: + explicit CircularSwitch(QWidget* parent = nullptr) + : QWidget(parent) + , m_checked(false) + , m_knobPosition(0) + , m_animationDuration(200) + , m_onColor(QColor("#4CD964")) + , m_offColor(QColor("#E5E5EA")) + , m_knobColor(Qt::white) + , m_borderColor(QColor("#CCCCCC")) + , m_textColor(Qt::white) + , m_knobDiameter(0) + , m_padding(3) + , m_borderWidth(1) + , m_showText(true) + , m_trueText("true") + , m_falseText("false") + { + m_animation = new QPropertyAnimation(this, "knobPosition", this); + m_animation->setEasingCurve(QEasingCurve::OutCubic); + m_animation->setDuration(m_animationDuration); + + setFixedSize(70, 34); // 稍微加宽以容纳文字 + setCursor(Qt::PointingHandCursor); + updateKnobGeometry(); + } + + // 获取和设置开关状态 + bool isChecked() const { return m_checked; } + + void setChecked(bool checked) { + if (m_checked == checked) return; + + m_checked = checked; + update(); + + // 启动动画 + m_animation->stop(); + m_animation->setStartValue(m_knobPosition); + m_animation->setEndValue(checked ? width() - m_knobDiameter - m_padding : m_padding); + m_animation->start(); + + // 延迟发射信号,让动画先执行 + QTimer::singleShot(10, this, [this, checked]() { + emit toggled(checked); + }); + } + + // 颜色设置 + QColor onColor() const { return m_onColor; } + void setOnColor(const QColor& color) { + m_onColor = color; + update(); + } + + QColor offColor() const { return m_offColor; } + void setOffColor(const QColor& color) { + m_offColor = color; + update(); + } + + QColor knobColor() const { return m_knobColor; } + void setKnobColor(const QColor& color) { + m_knobColor = color; + update(); + } + + QColor borderColor() const { return m_borderColor; } + void setBorderColor(const QColor& color) { + m_borderColor = color; + update(); + } + + QColor textColor() const { return m_textColor; } + void setTextColor(const QColor& color) { + m_textColor = color; + update(); + } + + QColor backgroundColor() const { + return m_checked ? m_onColor : m_offColor; + } + + void setBackgroundColor(const QColor& color) { + if (m_checked) { + m_onColor = color; + } + else { + m_offColor = color; + } + update(); + } + + // 动画设置 + int animationDuration() const { return m_animationDuration; } + void setAnimationDuration(int duration) { + m_animationDuration = duration; + m_animation->setDuration(duration); + } + + // 边框设置 + int borderWidth() const { return m_borderWidth; } + void setBorderWidth(int width) { + m_borderWidth = width; + update(); + } + + // 文字设置 + bool showText() const { return m_showText; } + void setShowText(bool show) { + m_showText = show; + update(); + } + + QString trueText() const { return m_trueText; } + void setTrueText(const QString& text) { + m_trueText = text; + update(); + } + + QString falseText() const { return m_falseText; } + void setFalseText(const QString& text) { + m_falseText = text; + update(); + } + +protected: + void paintEvent(QPaintEvent* event) override { + Q_UNUSED(event) + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + // 绘制背景 + QRect backgroundRect = rect().adjusted(m_padding, m_padding, -m_padding, -m_padding); + QPainterPath backgroundPath; + backgroundPath.addRoundedRect(backgroundRect, backgroundRect.height() / 2, backgroundRect.height() / 2); + + // 绘制边框 + if (m_borderWidth > 0) { + painter.setPen(QPen(m_borderColor, m_borderWidth)); + painter.setBrush(Qt::NoBrush); + painter.drawPath(backgroundPath); + } + + // 绘制填充背景 + painter.setPen(Qt::NoPen); + painter.setBrush(m_checked ? m_onColor : m_offColor); + painter.drawPath(backgroundPath); + + // 绘制背景文字 + if (m_showText) { + painter.setPen(m_textColor); + QFont font = painter.font(); + font.setPointSize(8); + font.setBold(true); + painter.setFont(font); + + // 计算文字位置 + QRect textRect = backgroundRect; + + if (m_checked) { + // 打开状态,文字在左侧 + textRect.setRight(width() / 2); + painter.drawText(textRect, Qt::AlignCenter, m_trueText); + } + else { + // 关闭状态,文字在右侧 + textRect.setLeft(width() / 2); + painter.drawText(textRect, Qt::AlignCenter, m_falseText); + } + } + + // 绘制滑块 + int knobY = height() / 2; + QRect knobRect(m_knobPosition, knobY - m_knobDiameter / 2, + m_knobDiameter, m_knobDiameter); + + QPainterPath knobPath; + knobPath.addEllipse(knobRect); + + // 滑块阴影效果 + QRadialGradient gradient(knobRect.center(), m_knobDiameter / 2); + gradient.setColorAt(0, m_knobColor.lighter(110)); + gradient.setColorAt(1, m_knobColor); + + painter.setPen(QPen(m_borderColor, 1)); + painter.setBrush(gradient); + painter.drawPath(knobPath); + + // 滑块内阴影效果 + if (m_checked) { + QRect innerKnobRect = knobRect.adjusted(3, 3, -3, -3); + QPainterPath innerKnobPath; + innerKnobPath.addEllipse(innerKnobRect); + + QRadialGradient innerGradient(innerKnobRect.center(), innerKnobRect.width() / 2); + innerGradient.setColorAt(0, QColor(255, 255, 255, 100)); + innerGradient.setColorAt(1, QColor(255, 255, 255, 0)); + + painter.setPen(Qt::NoPen); + painter.setBrush(innerGradient); + painter.drawPath(innerKnobPath); + } + } + + void mousePressEvent(QMouseEvent* event) override { + if (event->button() == Qt::LeftButton) { + event->accept(); + setChecked(!m_checked); + } + else { + event->ignore(); + } + } + + void resizeEvent(QResizeEvent* event) override { + Q_UNUSED(event) + updateKnobGeometry(); + // 更新滑块位置 + setKnobPosition(m_checked ? width() - m_knobDiameter - m_padding : m_padding); + } + +private: + int knobPosition() const { return m_knobPosition; } + + void setKnobPosition(int position) { + if (m_knobPosition != position) { + m_knobPosition = position; + update(); + emit knobPositionChanged(position); + } + } + + void updateKnobGeometry() { + m_knobDiameter = height() - 2 * m_padding - 2 * m_borderWidth; + // 确保滑块是圆形 + if (m_knobDiameter > width() / 2 - m_padding) { + m_knobDiameter = width() / 2 - m_padding; + } + } + +private: + bool m_checked; + int m_knobPosition; + int m_animationDuration; + + QColor m_onColor; + QColor m_offColor; + QColor m_knobColor; + QColor m_borderColor; + QColor m_textColor; + + QPropertyAnimation* m_animation; + + int m_knobDiameter; + int m_padding; + int m_borderWidth; + + // 文字相关属性 + bool m_showText; + QString m_trueText; + QString m_falseText; + +signals: + void toggled(bool checked); + void knobPositionChanged(int position); + void backgroundColorChanged(const QColor& color); +}; + +#endif // CIRCULARSWITCH_H \ No newline at end of file diff --git a/src/launcher/GUI/uiModule/uiModule_runCommand.h b/src/launcher/GUI/uiModule/uiModule_runCommand.h index 41c0ea8..0184918 100644 --- a/src/launcher/GUI/uiModule/uiModule_runCommand.h +++ b/src/launcher/GUI/uiModule/uiModule_runCommand.h @@ -55,18 +55,31 @@ 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;"); - plcValueViewerLayout = new QVBoxLayout(); - //plcValueViewerLayout->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;"); + + QScrollArea* scrollArea = new QScrollArea(this); + scrollArea->setWidgetResizable(true); + scrollArea->setFrameShape(QFrame::NoFrame); + scrollArea->setWidget(listContainer); + scrollArea->setStyleSheet("background: transparent;"); QGridLayout* layout = new QGridLayout(this); layout->addWidget(btnWidgets, 0, 0); - layout->addLayout(plcValueViewerLayout, 0, 1); + layout->addWidget(scrollArea, 0, 1); this->setFixedWidth(sizeX); + this->setMinimumHeight(sizeY); + //this->setMaximumHeight(sizeY); } signals: @@ -101,7 +114,7 @@ public slots: } } private: - int sizeX = 1000, sizeY = 500; + int sizeX = 1000, sizeY = 800; QListWidget* btnWidgets; QVBoxLayout* plcValueViewerLayout; QMap>plcViwerLineedits; diff --git a/src/launcher/GUI/uiModule/uiModule_softwareDataDisplay.h b/src/launcher/GUI/uiModule/uiModule_softwareDataDisplay.h index 73a898a..311fe53 100644 --- a/src/launcher/GUI/uiModule/uiModule_softwareDataDisplay.h +++ b/src/launcher/GUI/uiModule/uiModule_softwareDataDisplay.h @@ -60,10 +60,15 @@ public: runStateText->setFont(font); runStateText->setPalette(palette); + triggerState =new QLabel(""); + triggerState->setFont(font); + triggerState->setPalette(palette); + runStateLayout->addWidget(runStateIconLabel); runStateLayout->addWidget(runStateText, 1, Qt::AlignCenter); mainLayout->addLayout(runStateLayout); + mainLayout->addWidget(triggerState, 1, Qt::AlignCenter); } layout->addLayout(mainLayout); @@ -72,7 +77,7 @@ public: "QLineEdit { background-color: #333; color: white; border-radius: 4px; padding: 2px; } " "QLabel { color: white; } " ); - //this->setFixedHeight(150); + this->setFixedWidth(200); } protected: @@ -105,16 +110,19 @@ private slots: runStateText->setToolTip(tooltip); } - void onMonitorError(const QString& error) { runStateText->setText("监控异常"); runStateText->setStyleSheet("QLabel { color: #999; }"); runStateText->setToolTip(QString("监控错误: %1").arg(error)); } - +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"); + } private: QTimer* timer; - QLabel* softwareRunTimeValue,* runStateText; + QLabel* softwareRunTimeValue,* runStateText,* triggerState; QElapsedTimer startTimer; }; diff --git a/src/launcher/appConfig/appConfig.cpp b/src/launcher/appConfig/appConfig.cpp index 12136d0..6ccfe95 100644 --- a/src/launcher/appConfig/appConfig.cpp +++ b/src/launcher/appConfig/appConfig.cpp @@ -31,5 +31,16 @@ void appConfig::parsingSettingJson() } missionJsonPath = appWorkPath.string() + "/config/" + objectSelect + ".json"; - //if(missionJson["backend"]["missionSettings"].at) + if (missionJson["backend"]["missionSettings"].contains("存储原图") == false) { + missionJson["backend"]["missionSettings"]["存储原图"] = true; + } + if (missionJson["backend"]["missionSettings"].contains("存储结果图") == false) { + missionJson["backend"]["missionSettings"]["存储结果图"] = true; + } + if (missionJson["backend"]["missionSettings"].contains("存储Seg图") == false) { + missionJson["backend"]["missionSettings"]["存储Seg图"] = true; + } + if (missionJson["backend"]["missionSettings"].contains("存储Cls图") == false) { + missionJson["backend"]["missionSettings"]["存储Cls图"] = true; + } } diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index 22f457e..39d2b65 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -164,6 +164,10 @@ void connectSignalAndSlot(appCore* _core, MainWindow* _MainWindow, qtFuncBridge* _MainWindow->_displayWidget->_runCommandWidget, &runCommandWidget::addPlcValueViewer); QObject::connect(bridge, &qtFuncBridge::runCommand_refeshPlcValueViewer, _MainWindow->_displayWidget->_runCommandWidget, &runCommandWidget::refreshPlcValueViewer); + + //性能监测 + QObject::connect(bridge, &qtFuncBridge::sendTriggerManagerLoopData, + _MainWindow->_displayWidget->_softwareDataDisplay, &softwareDataDisplay::onTriggerSateChanged); } int main(int argc, char* argv[]) 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 e96f91d..990740a 100644 --- a/src/mission/581-Mfd_yh_mh_hh/581-Mfd.h +++ b/src/mission/581-Mfd_yh_mh_hh/581-Mfd.h @@ -13,7 +13,20 @@ public: # 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)); + 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->writeFloat("D" + QByteArray::number(address.valueOffsetAddr) + "_0.01");//结果反馈 + data->service->_triggerManager->writeFloat("D" + QByteArray::number(address.valueOffsetAddr + 2) + "_0.01");//结果反馈 +} static bool ManHan(std::shared_ptr data, mfd_detParams* _detParams, yh_581_address address){ HObject ho_in_image; @@ -202,130 +215,134 @@ static bool ManHan(std::shared_ptr data, mfd_detParams* _detParams, yh return true; } static bool YuHan(std::shared_ptr data, mfd_detParams* _detParams, yh_581_address address){ - qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; - std::map sqlMapData; - sqlMapData["项目"] = "预焊"; - sqlMapData["相机"] = "相机" + address.triggerAddr; - ///////////////////取图//处理////////////////// - 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; - } + try { + qDebug() << "SN" << QString::fromStdString(data->SNcode) << "algo start"; + std::map sqlMapData; + sqlMapData["项目"] = "预焊"; + sqlMapData["相机"] = "相机" + address.triggerAddr; + ///////////////////取图//处理////////////////// + 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); - //处理算法 + 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")) { - cameraNumberStr = "1"; - FixtureNumber = "1"; - } - else if (singal_sign.contains("19602")) { - cameraNumberStr = "1"; - FixtureNumber = "3"; - } - else if (singal_sign.contains("19800")) { - cameraNumberStr = "2"; - FixtureNumber = "2"; - } - else if (singal_sign.contains("19802")) { - cameraNumberStr = "2"; - FixtureNumber = "4"; - } - 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.00749026)); - 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.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"); + QString singal_sign = address.triggerAddr; + std::string cameraNumberStr = "1", FixtureNumber = "1"; + if (singal_sign.contains("19600")) { + cameraNumberStr = "1"; + FixtureNumber = "1"; + } + else if (singal_sign.contains("19602")) { + cameraNumberStr = "1"; + FixtureNumber = "3"; + } + else if (singal_sign.contains("19800")) { + cameraNumberStr = "2"; + FixtureNumber = "2"; + } + else if (singal_sign.contains("19802")) { + cameraNumberStr = "2"; + FixtureNumber = "4"; + } + 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.00749026)); + 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.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){ + 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 + " 孔位" + FixtureNumber, 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");//发送结果 - qDebug() << __FUNCTION__ << " line:" << __LINE__ << " 预焊X偏移abs(resultData[0]) >= 6000,may err,set 0"; - data->halconOK = false; + 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 { - 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; + textPainter.putText(Paint, "NG", cv::Point(50, 500), cv::Scalar(0, 0, 255)); + sqlMapData["结果"] = "NG"; } - //圆及十字 - 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); + data->service->_csvWriter->addData(QString::fromStdString(data->SNcode), sqlMapData); + data->resultImgs.push_back(Paint); - 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)); + return true; } - 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, "NG"); } diff --git a/updateLog/2025-10-17/屏幕截图 2025-10-17 095806.png b/updateLog/2025-10-17/屏幕截图 2025-10-17 095806.png new file mode 100644 index 0000000000000000000000000000000000000000..dee20f48f58579107b5d884263349a0699e3aa5a GIT binary patch literal 20334 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 IIVCg!0RJwJ`~Uy| literal 0 HcmV?d00001 diff --git a/updateLog/2025-10-17/屏幕截图 2025-10-17 095811.png b/updateLog/2025-10-17/屏幕截图 2025-10-17 095811.png new file mode 100644 index 0000000000000000000000000000000000000000..34d75cbdac28508be79986086c52f88cf6a55c6b GIT binary patch literal 8007 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