951 lines
36 KiB
C++
Raw Normal View History

2026-08-04 14:23:07 +08:00
#include "dialogalgoarg.h"
#include "ui_dialogalgoarg.h"
#include "workpiecesizeactions.h"
2026-08-04 14:23:07 +08:00
#include <QPushButton>
#include <QDoubleValidator>
#include <QGroupBox>
2026-09-19 09:44:46 +08:00
#include <QtMath>
#include <QTableWidget>
#include <QHeaderView>
#include <QColor>
#include <QComboBox>
#include <QPersistentModelIndex>
#include <algorithm>
#include <cmath>
2026-09-19 09:44:46 +08:00
#include "HandEyeCalibWidget.h"
2026-08-04 14:23:07 +08:00
#include "PathManager.h"
#include "StyledMessageBox.h"
#include "ToolExtrinsicWidget.h"
#include "NetworkConfigWidget.h"
#include "PointCloudFilterWidget.h"
#include "workpiecesizeactions.h"
2026-08-04 14:23:07 +08:00
#include "VrLog.h"
2026-09-19 09:44:46 +08:00
namespace
{
bool isIdentityMatrix(const double matrix[16])
{
for (int index = 0; index < 16; ++index)
{
const double expected = (index / 4 == index % 4) ? 1.0 : 0.0;
if (qAbs(matrix[index] - expected) > 1e-9)
{
return false;
}
}
return true;
}
}
2026-08-04 14:23:07 +08:00
DialogAlgoarg::DialogAlgoarg(ConfigManager* configManager, QWidget *parent)
: QDialog(parent)
, ui(new Ui::DialogAlgoarg)
, m_pConfigManager(configManager)
2026-09-19 09:44:46 +08:00
, m_handEyeCalibWidget(nullptr)
2026-08-04 14:23:07 +08:00
, m_toolExtrinsicWidget(nullptr)
{
try {
ui->setupUi(this);
m_pointCloudFilterWidget = ui->pointCloudFilterWidget;
2026-08-04 14:23:07 +08:00
2026-09-19 09:44:46 +08:00
if (ui->lineEdit_spacerLength)
{
ui->lineEdit_spacerLength->setValidator(
new QDoubleValidator(0.0, 1e6, 6, ui->lineEdit_spacerLength));
}
if (ui->lineEdit_spacerWidth)
{
ui->lineEdit_spacerWidth->setValidator(
new QDoubleValidator(0.0, 1e6, 6, ui->lineEdit_spacerWidth));
}
if (ui->lineEdit_layerRatio) ui->lineEdit_layerRatio->setValidator(new QDoubleValidator(-1.0, 10.0, 6, ui->lineEdit_layerRatio));
if (ui->lineEdit_distToSideRatio) ui->lineEdit_distToSideRatio->setValidator(new QDoubleValidator(0.0, 10.0, 6, ui->lineEdit_distToSideRatio));
if (ui->lineEdit_groundRemoveOffset) ui->lineEdit_groundRemoveOffset->setValidator(new QDoubleValidator(-1e6, 1e6, 6, ui->lineEdit_groundRemoveOffset));
2026-09-19 09:44:46 +08:00
InitHandEyeCalibTab();
2026-08-04 14:23:07 +08:00
InitNetworkConfigTab();
InitToolExtrinsicTab();
initWorkpieceSizesTab();
initModelParamsTab();
2026-08-04 14:23:07 +08:00
// 隐藏标题栏
// setWindowFlags(Qt::FramelessWindowHint);
// 获取配置文件路径
m_configFilePath = PathManager::GetInstance().GetConfigFilePath();
// 检查配置文件路径是否有效
if (m_configFilePath.isEmpty()) {
StyledMessageBox::critical(this, "错误", "无法获取配置文件路径!");
return;
}
// 从配置文件加载数据到界面
LoadConfigToUI();
} catch (const std::exception& e) {
StyledMessageBox::critical(this, "初始化错误", QString("对话框初始化失败: %1").arg(e.what()));
} catch (...) {
StyledMessageBox::critical(this, "初始化错误", "对话框初始化失败!(未知错误)");
}
}
DialogAlgoarg::~DialogAlgoarg()
{
delete ui;
}
void DialogAlgoarg::LoadConfigToUI()
{
if (!m_pConfigManager) {
StyledMessageBox::critical(this, "错误", "配置对象未初始化!");
return;
}
try {
// 从ConfigManager获取配置数据
ConfigResult configData = m_pConfigManager->GetConfigResult();
loadPointCloudFilterConfig(configData);
2026-08-04 14:23:07 +08:00
// 检查配置文件路径是否有效
if (m_configFilePath.isEmpty()) {
LOG_WARNING("Config file path is empty\n");
}
const VrToolParam& toolParam = configData.algorithmParams.toolParam;
LoadModelDetectionParamsToUI(configData.algorithmParams);
2026-08-17 14:11:44 +08:00
LoadBinDetectionParamsToUI(configData.algorithmParams.binParam);
LoadRotorCoreParamsToUI(configData.algorithmParams.rotorCoreParam);
2026-08-04 14:23:07 +08:00
if (m_toolExtrinsicWidget) {
const int count = std::max(1, static_cast<int>(configData.cameraList.size()));
for (int cameraIndex = 1; cameraIndex <= count; ++cameraIndex)
{
VrToolParam value = cameraIndex == 1 ? toolParam : VrToolParam();
for (const auto& item : configData.algorithmParams.toolParamsByCamera)
{
if (item.first == cameraIndex)
{
value = item.second;
}
}
m_toolExtrinsicWidget->setData(cameraIndex, value.eulerOrder, value.rotX, value.rotY,
value.rotZ, value.offsetX, value.offsetY, value.offsetZ);
}
2026-08-04 14:23:07 +08:00
}
// 加载网络配置(JSON TCP 端口 + Modbus 寄存器地址)
LoadPlcRobotServerConfigToUI(configData.plcRobotServerConfig);
2026-09-19 09:44:46 +08:00
LoadHandEyeCalibConfig();
2026-08-04 14:23:07 +08:00
} catch (const std::exception& e) {
LOG_ERROR("Exception in LoadConfigToUI: %s\n", e.what());
StyledMessageBox::warning(this, "警告",
QString("加载配置时发生异常: %1\n将使用默认参数显示").arg(e.what()));
ConfigResult configData;
loadPointCloudFilterConfig(configData);
LoadModelDetectionParamsToUI(configData.algorithmParams);
2026-08-17 14:11:44 +08:00
LoadBinDetectionParamsToUI(configData.algorithmParams.binParam);
LoadRotorCoreParamsToUI(configData.algorithmParams.rotorCoreParam);
2026-08-04 14:23:07 +08:00
const VrToolParam& toolParam = configData.algorithmParams.toolParam;
if (m_toolExtrinsicWidget) {
m_toolExtrinsicWidget->setData(
toolParam.eulerOrder,
toolParam.rotX, toolParam.rotY, toolParam.rotZ,
toolParam.offsetX, toolParam.offsetY, toolParam.offsetZ);
}
} catch (...) {
LOG_ERROR("Unknown exception in LoadConfigToUI\n");
StyledMessageBox::warning(this, "警告", "加载配置文件失败(未知错误),将使用默认参数显示");
ConfigResult configData;
loadPointCloudFilterConfig(configData);
LoadModelDetectionParamsToUI(configData.algorithmParams);
2026-08-17 14:11:44 +08:00
LoadBinDetectionParamsToUI(configData.algorithmParams.binParam);
LoadRotorCoreParamsToUI(configData.algorithmParams.rotorCoreParam);
2026-08-04 14:23:07 +08:00
const VrToolParam& toolParam = configData.algorithmParams.toolParam;
if (m_toolExtrinsicWidget) {
m_toolExtrinsicWidget->setData(
toolParam.eulerOrder,
toolParam.rotX, toolParam.rotY, toolParam.rotZ,
toolParam.offsetX, toolParam.offsetY, toolParam.offsetZ);
}
}
}
void DialogAlgoarg::InitToolExtrinsicTab()
{
if (!ui || m_toolExtrinsicWidget)
{
2026-08-04 14:23:07 +08:00
return;
}
m_toolExtrinsicWidget = ui->toolExtrinsicWidget;
m_toolExtrinsicWidget->setCameraSelectorVisible(true);
QVector<ToolExtrinsicCameraInfo> cameras;
const ConfigResult config = m_pConfigManager ? m_pConfigManager->GetConfigResult() : ConfigResult();
const int cameraCount = std::max(1, static_cast<int>(config.cameraList.size()));
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
{
ToolExtrinsicCameraInfo camera;
camera.cameraIndex = cameraIndex;
camera.displayName = QStringLiteral("相机 %1").arg(cameraIndex);
if (cameraIndex <= static_cast<int>(config.cameraList.size()) && !config.cameraList[cameraIndex - 1].name.empty())
{
camera.displayName = QString::fromStdString(config.cameraList[cameraIndex - 1].name);
}
cameras.append(camera);
}
m_toolExtrinsicWidget->setCameraList(cameras);
2026-08-04 14:23:07 +08:00
m_toolExtrinsicWidget->setTargetOffsetVisible(true);
m_toolExtrinsicWidget->setAxialAngleRangeVisible(false);
}
2026-09-19 09:44:46 +08:00
void DialogAlgoarg::InitHandEyeCalibTab()
{
if (!ui || m_handEyeCalibWidget)
2026-09-19 09:44:46 +08:00
{
return;
}
m_handEyeCalibWidget = ui->handEyeCalibWidget;
2026-09-19 09:44:46 +08:00
m_handEyeCalibWidget->setMatrixEditable(true);
m_handEyeCalibWidget->setDefaultCalibType(0);
m_handEyeCalibWidget->setDefaultFilePath(
PathManager::GetInstance().GetAppConfigDirectory());
QVector<HandEyeCalibCameraInfo> cameraList;
const ConfigResult cameraConfig = m_pConfigManager ? m_pConfigManager->GetConfigResult() : ConfigResult();
const int cameraCount = std::max(1, static_cast<int>(cameraConfig.cameraList.size()));
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
{
HandEyeCalibCameraInfo info;
info.cameraIndex = cameraIndex;
info.displayName = QStringLiteral("相机 %1").arg(cameraIndex);
if (cameraIndex <= static_cast<int>(cameraConfig.cameraList.size()) && !cameraConfig.cameraList[cameraIndex - 1].name.empty())
{
info.displayName = QString::fromStdString(cameraConfig.cameraList[cameraIndex - 1].name);
}
cameraList.append(info);
}
2026-09-19 09:44:46 +08:00
m_handEyeCalibWidget->setCameraList(cameraList);
m_handEyeCalibWidget->setCalibType(1, 0);
2026-09-19 09:44:46 +08:00
connect(m_handEyeCalibWidget, &HandEyeCalibWidget::calibMatrixLoaded,
this, &DialogAlgoarg::onCalibMatrixLoaded);
connect(m_handEyeCalibWidget, &HandEyeCalibWidget::saveCalibRequested,
this, &DialogAlgoarg::onSaveCalibRequested);
}
2026-08-04 14:23:07 +08:00
void DialogAlgoarg::InitNetworkConfigTab()
{
if (!ui || m_networkConfigWidget)
{
2026-08-04 14:23:07 +08:00
return;
}
m_networkConfigWidget = ui->networkConfigWidget;
2026-08-04 14:23:07 +08:00
m_networkConfigWidget->setExtrinsicControlsVisible(false);
m_networkConfigWidget->setPoseOutputOrderVisible(true);
if (QGroupBox* group = m_networkConfigWidget->findChild<QGroupBox*>("groupPlc")) {
group->setTitle(QStringLiteral("TCP端口与Modbus寄存器配置"));
}
const char* hideNames[] = {"labelPlcIp", "editPlcIp", "labelPlcPort", "editPlcPort"};
for (const char* name : hideNames) {
if (QWidget* widget = m_networkConfigWidget->findChild<QWidget*>(name)) {
widget->hide();
}
}
}
void DialogAlgoarg::loadPointCloudFilterConfig(const ConfigResult& configResult)
{
if (!m_pointCloudFilterWidget)
{
return;
}
QVector<PointCloudFilterCameraInfo> cameras;
const int cameraCount = std::max(1, static_cast<int>(configResult.cameraList.size()));
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
{
PointCloudFilterCameraInfo camera;
camera.cameraIndex = cameraIndex;
if (cameraIndex <= static_cast<int>(configResult.cameraList.size()))
{
camera.displayName = QString::fromStdString(configResult.cameraList[cameraIndex - 1].name);
}
if (camera.displayName.isEmpty())
{
camera.displayName = QStringLiteral("相机 %1").arg(cameraIndex);
}
cameras.append(camera);
}
m_pointCloudFilterWidget->setCameraList(cameras);
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
{
PointCloudFilterData data;
for (const VrPointCloudFilterParam& param : configResult.algorithmParams.pointCloudFilterParams)
{
if (param.cameraIndex == cameraIndex)
{
data.enabled = param.enabled;
data.minX = param.minX;
data.maxX = param.maxX;
data.minY = param.minY;
data.maxY = param.maxY;
data.minZ = param.minZ;
data.maxZ = param.maxZ;
break;
}
}
m_pointCloudFilterWidget->setData(cameraIndex, data);
}
}
bool DialogAlgoarg::savePointCloudFilterConfig(ConfigResult& configResult)
{
if (!m_pointCloudFilterWidget)
{
return false;
}
std::vector<VrPointCloudFilterParam> filterParams;
const int cameraCount = std::max(1, static_cast<int>(configResult.cameraList.size()));
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
{
PointCloudFilterData data;
if (!m_pointCloudFilterWidget->getData(cameraIndex, data))
{
return false;
}
if (data.minX > data.maxX || data.minY > data.maxY || data.minZ > data.maxZ)
{
StyledMessageBox::warning(this, QStringLiteral("点云过滤"),
QStringLiteral("相机 %1 的过滤范围下限不能大于上限!").arg(cameraIndex));
return false;
}
VrPointCloudFilterParam param;
param.cameraIndex = cameraIndex;
if (cameraIndex <= static_cast<int>(configResult.cameraList.size()))
{
param.cameraName = configResult.cameraList[cameraIndex - 1].name;
}
param.enabled = data.enabled;
param.minX = data.minX;
param.maxX = data.maxX;
param.minY = data.minY;
param.maxY = data.maxY;
param.minZ = data.minZ;
param.maxZ = data.maxZ;
filterParams.push_back(param);
}
configResult.algorithmParams.pointCloudFilterParams = filterParams;
return true;
2026-08-04 14:23:07 +08:00
}
bool DialogAlgoarg::SaveConfigFromUI()
{
if (!m_pConfigManager) {
return false;
}
try {
// 获取当前配置
SystemConfig systemConfig = m_pConfigManager->GetConfig();
if (!savePointCloudFilterConfig(systemConfig.configResult))
{
return false;
}
2026-08-04 14:23:07 +08:00
VrToolParam& toolParam = systemConfig.configResult.algorithmParams.toolParam;
if (!SaveModelDetectionParamsFromUI(systemConfig.configResult.algorithmParams)) {
2026-08-04 14:23:07 +08:00
StyledMessageBox::warning(this, "错误", "模型检测参数输入有误,请检查!");
return false;
}
2026-08-17 14:11:44 +08:00
if (!SaveBinDetectionParamsFromUI(systemConfig.configResult.algorithmParams.binParam)) {
StyledMessageBox::warning(this, "错误", "料框检测参数输入有误,请检查!");
return false;
}
if (!SaveRotorCoreParamsFromUI(systemConfig.configResult.algorithmParams.rotorCoreParam)) {
StyledMessageBox::warning(this, "错误", "转子钢芯算法参数输入有误,请检查!");
return false;
}
2026-08-04 14:23:07 +08:00
if (!m_toolExtrinsicWidget || !m_toolExtrinsicWidget->hasAcceptableInput()) {
StyledMessageBox::warning(this, "错误", "工具参数输入有误,请检查!");
return false;
}
systemConfig.configResult.algorithmParams.toolParamsByCamera.clear();
const int cameraCount = std::max(1, static_cast<int>(systemConfig.configResult.cameraList.size()));
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
{
VrToolParam value;
if (!m_toolExtrinsicWidget->getData(cameraIndex, value.eulerOrder, value.rotX, value.rotY,
value.rotZ, value.offsetX, value.offsetY, value.offsetZ))
{
StyledMessageBox::warning(this, tr("错误"), tr("相机 %1 的工具参数输入有误,请检查。").arg(cameraIndex));
return false;
}
systemConfig.configResult.algorithmParams.toolParamsByCamera.push_back(std::make_pair(cameraIndex, value));
if (cameraIndex == 1)
{
toolParam = value;
}
}
2026-08-04 14:23:07 +08:00
// 保存网络配置(JSON TCP 端口 + Modbus 寄存器地址)
if (!SavePlcRobotServerConfigFromUI(systemConfig.configResult.plcRobotServerConfig)) {
StyledMessageBox::warning(this, "错误", "网络配置输入有误,请检查!");
return false;
}
// 工具参数与手眼矩阵按同一相机编号成对保存,旧单份字段仅镜像相机 1。
std::vector<std::pair<int, VrHandEyeCalibMatrix>> matrices;
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
{
VrHandEyeCalibMatrix matrix = cameraIndex == 1
? systemConfig.configResult.handEyeCalibMatrix : VrHandEyeCalibMatrix();
for (const auto& item : systemConfig.configResult.handEyeCalibMatricesByCamera)
{
if (item.first == cameraIndex)
{
matrix = item.second;
}
}
bool calibrated = false;
if (!m_handEyeCalibWidget || !m_handEyeCalibWidget->getCalibData(cameraIndex, matrix.matrix, calibrated))
{
StyledMessageBox::warning(this, tr("错误"), tr("相机 %1 的手眼标定矩阵输入有误,请检查。").arg(cameraIndex));
return false;
}
matrices.push_back(std::make_pair(cameraIndex, matrix));
if (cameraIndex == 1)
{
systemConfig.configResult.handEyeCalibMatrix = matrix;
}
2026-08-04 14:23:07 +08:00
}
systemConfig.configResult.handEyeCalibMatricesByCamera = matrices;
2026-08-04 14:23:07 +08:00
// 更新并保存配置到文件
if (!m_pConfigManager->UpdateFullConfig(systemConfig)) {
return false;
}
return m_pConfigManager->SaveConfigToFile(m_configFilePath.toStdString());
} catch (const std::exception&) {
return false;
}
}
void DialogAlgoarg::LoadModelDetectionParamsToUI(const VrAlgorithmParams& algorithmParams)
2026-08-04 14:23:07 +08:00
{
if (!ui || !ui->tableWidget_modelParams) {
2026-08-04 14:23:07 +08:00
return;
}
ui->tableWidget_modelParams->setRowCount(0);
ui->doubleSpinBox_targetGray->setValue(algorithmParams.modelGraySelection.targetGray);
ui->doubleSpinBox_maxGrayDeviation->setValue(algorithmParams.modelGraySelection.maxGrayDeviation);
const VrModelDetectionRegion& region = algorithmParams.modelDetectionRegion;
ui->groupBox_modelDetectionRegion->setChecked(region.enabled);
ui->spinBox_modelRegionLeft->setValue(region.left);
ui->spinBox_modelRegionTop->setValue(region.top);
ui->spinBox_modelRegionRight->setValue(region.right);
ui->spinBox_modelRegionBottom->setValue(region.bottom);
const std::vector<VrWorkpieceModelParam> modelParams = algorithmParams.modelParams.empty()
? std::vector<VrWorkpieceModelParam>{VrWorkpieceModelParam()}
: algorithmParams.modelParams;
for (const auto& modelParam : modelParams)
{
addModelParamRow(modelParam);
}
refreshModelParamRows();
2026-08-04 14:23:07 +08:00
}
2026-08-17 14:11:44 +08:00
void DialogAlgoarg::LoadBinDetectionParamsToUI(const VrBinParam& binParam)
{
Q_UNUSED(binParam);
2026-08-17 14:11:44 +08:00
}
bool DialogAlgoarg::SaveBinDetectionParamsFromUI(VrBinParam& binParam)
{
Q_UNUSED(binParam);
2026-08-17 14:11:44 +08:00
return true;
}
void DialogAlgoarg::LoadRotorCoreParamsToUI(const VrRotorCoreParam& rotorParam)
{
loadWorkpieceSizes(rotorParam);
if (!ui || !ui->lineEdit_spacerLength || !ui->lineEdit_spacerWidth ||
2026-09-19 09:44:46 +08:00
!ui->lineEdit_layerRatio || !ui->lineEdit_distToSideRatio ||
!ui->lineEdit_groundRemoveOffset) {
2026-08-17 14:11:44 +08:00
return;
}
2026-09-19 09:44:46 +08:00
ui->lineEdit_spacerLength->setText(QString::number(rotorParam.spacerLength, 'f', 6));
ui->lineEdit_spacerWidth->setText(QString::number(rotorParam.spacerWidth, 'f', 6));
ui->lineEdit_layerRatio->setText(QString::number(rotorParam.layerRatio, 'f', 6));
ui->lineEdit_distToSideRatio->setText(QString::number(rotorParam.distToSideRatio, 'f', 6));
ui->lineEdit_groundRemoveOffset->setText(QString::number(rotorParam.groundRemoveOffset, 'f', 6));
ui->comboBox_toolNormalMode->setCurrentIndex(rotorParam.useGroundCalibNormal == 1 ? 1 : 0);
2026-08-17 14:11:44 +08:00
}
bool DialogAlgoarg::SaveRotorCoreParamsFromUI(VrRotorCoreParam& rotorParam)
{
if (!saveWorkpieceSizes(rotorParam))
{
return false;
}
if (!ui || !ui->lineEdit_spacerLength || !ui->lineEdit_spacerWidth ||
2026-09-19 09:44:46 +08:00
!ui->lineEdit_layerRatio || !ui->lineEdit_distToSideRatio ||
!ui->lineEdit_groundRemoveOffset) {
2026-08-17 14:11:44 +08:00
return false;
}
bool ok = false;
2026-09-19 09:44:46 +08:00
rotorParam.spacerLength = ui->lineEdit_spacerLength->text().toDouble(&ok);
if (!ok || rotorParam.spacerLength <= 0.0)
{
return false;
}
rotorParam.spacerWidth = ui->lineEdit_spacerWidth->text().toDouble(&ok);
if (!ok || rotorParam.spacerWidth <= 0.0)
{
return false;
}
rotorParam.layerRatio = ui->lineEdit_layerRatio->text().toDouble(&ok);
// -1 表示现场未配置分层系数;其余负数没有业务含义。
if (!ok || (rotorParam.layerRatio != -1.0 && rotorParam.layerRatio <= 0.0)) return false;
rotorParam.distToSideRatio = ui->lineEdit_distToSideRatio->text().toDouble(&ok);
if (!ok || rotorParam.distToSideRatio <= 0.0) return false;
rotorParam.groundRemoveOffset = ui->lineEdit_groundRemoveOffset->text().toDouble(&ok);
if (!ok) return false;
// .ui 选项顺序与算法约定一致:0=工件自身法向,1=调平后的地面法向。
const int normalMode = ui->comboBox_toolNormalMode->currentIndex();
if (normalMode != 0 && normalMode != 1)
{
return false;
}
rotorParam.useGroundCalibNormal = normalMode;
2026-08-17 14:11:44 +08:00
return true;
}
void DialogAlgoarg::initWorkpieceSizesTab()
2026-08-04 14:23:07 +08:00
{
// 页面和编辑触发方式定义在 .ui,此处只连接数据操作。
connect(ui->btn_workpieceAdd, &QPushButton::clicked, this, [this]()
{
if (ui->tableWidget_workpieceSizes->rowCount() >= 65535)
{
return;
}
const int defaultRow = ui->comboBox_defaultWorkpiece->currentIndex();
addWorkpieceSizeRow(VrRotorWorkpieceSize());
refreshWorkpieceRows(defaultRow);
ui->tableWidget_workpieceSizes->selectRow(ui->tableWidget_workpieceSizes->rowCount() - 1);
});
}
void DialogAlgoarg::removeWorkpieceRow(int row)
{
const int count = ui->tableWidget_workpieceSizes->rowCount();
if (row < 0 || row >= count || count <= 1)
{
return;
}
int defaultRow = ui->comboBox_defaultWorkpiece->currentIndex();
if (defaultRow == row)
{
defaultRow = 0;
}
else if (defaultRow > row)
{
--defaultRow;
2026-08-04 14:23:07 +08:00
}
ui->tableWidget_workpieceSizes->removeRow(row);
refreshWorkpieceRows(defaultRow);
ui->tableWidget_workpieceSizes->selectRow(std::min(row, ui->tableWidget_workpieceSizes->rowCount() - 1));
}
2026-08-04 14:23:07 +08:00
void DialogAlgoarg::addWorkpieceSizeRow(const VrRotorWorkpieceSize& size)
{
const int row = ui->tableWidget_workpieceSizes->rowCount();
ui->tableWidget_workpieceSizes->insertRow(row);
auto* index = new QTableWidgetItem(QString::number(row + 1));
index->setFlags(index->flags() & ~Qt::ItemIsEditable);
ui->tableWidget_workpieceSizes->setItem(row, 0, index);
for (int column = 1; column <= 2; ++column)
{
const double dimension = column == 1 ? size.radius : size.height;
// 使用文本项,双击时由 Qt 提供普通文本编辑器,不创建数值步进控件。
ui->tableWidget_workpieceSizes->setItem(row, column,
new QTableWidgetItem(QString::number(dimension, 'g', 15)));
2026-08-04 14:23:07 +08:00
}
auto* actions = new WorkpieceSizeActions(ui->tableWidget_workpieceSizes);
ui->tableWidget_workpieceSizes->setCellWidget(row, 3, actions);
// 持久索引跟随删除后的行号变化,按钮始终操作所在行,不依赖选中行。
const QPersistentModelIndex rowIndex(ui->tableWidget_workpieceSizes->model()->index(row, 0));
connect(actions, &WorkpieceSizeActions::moveRequested, this, [this, rowIndex](int offset)
{
if (rowIndex.isValid())
{
moveWorkpieceRow(rowIndex.row(), offset);
}
});
connect(actions, &WorkpieceSizeActions::removeRequested, this, [this, rowIndex]()
{
if (rowIndex.isValid())
{
removeWorkpieceRow(rowIndex.row());
}
});
}
2026-08-04 14:23:07 +08:00
void DialogAlgoarg::refreshWorkpieceRows(int defaultRow)
{
ui->comboBox_defaultWorkpiece->clear();
for (int row = 0; row < ui->tableWidget_workpieceSizes->rowCount(); ++row)
{
ui->tableWidget_workpieceSizes->item(row, 0)->setText(QString::number(row + 1));
ui->comboBox_defaultWorkpiece->addItem(QString::number(row + 1), row + 1);
}
ui->comboBox_defaultWorkpiece->setCurrentIndex(defaultRow >= 0 ? defaultRow : 0);
updateWorkpieceActions();
2026-08-04 14:23:07 +08:00
}
void DialogAlgoarg::updateWorkpieceActions()
2026-08-04 14:23:07 +08:00
{
const int count = ui->tableWidget_workpieceSizes->rowCount();
for (int row = 0; row < count; ++row)
{
auto* actions = qobject_cast<WorkpieceSizeActions*>(ui->tableWidget_workpieceSizes->cellWidget(row, 3));
if (actions)
{
actions->setRowPosition(row, count);
}
}
ui->btn_workpieceAdd->setEnabled(count < 65535);
}
void DialogAlgoarg::moveWorkpieceRow(int row, int offset)
{
const int targetRow = row + offset;
if (row < 0 || targetRow < 0 || targetRow >= ui->tableWidget_workpieceSizes->rowCount())
{
2026-08-04 14:23:07 +08:00
return;
}
int defaultRow = ui->comboBox_defaultWorkpiece->currentIndex();
for (int column = 1; column <= 2; ++column)
{
auto* source = ui->tableWidget_workpieceSizes->takeItem(row, column);
auto* target = ui->tableWidget_workpieceSizes->takeItem(targetRow, column);
ui->tableWidget_workpieceSizes->setItem(row, column, target);
ui->tableWidget_workpieceSizes->setItem(targetRow, column, source);
}
// 默认工件跟随尺寸项移动,序号始终对应新的行顺序。
if (defaultRow == row)
{
defaultRow = targetRow;
}
else if (defaultRow == targetRow)
{
defaultRow = row;
}
refreshWorkpieceRows(defaultRow);
ui->tableWidget_workpieceSizes->selectRow(targetRow);
2026-08-04 14:23:07 +08:00
}
void DialogAlgoarg::loadWorkpieceSizes(const VrRotorCoreParam& rotorParam)
2026-08-04 14:23:07 +08:00
{
ui->tableWidget_workpieceSizes->setRowCount(0);
int defaultRow = -1;
for (const auto& size : rotorParam.workpieceSizes)
{
if (size.index == rotorParam.defaultWorkpieceIndex)
{
defaultRow = ui->tableWidget_workpieceSizes->rowCount();
}
addWorkpieceSizeRow(size);
}
refreshWorkpieceRows(defaultRow);
if (!rotorParam.isWorkpieceListValid())
{
StyledMessageBox::warning(this, "工件尺寸", "尺寸列表存在无效参数,请检查尺寸和默认工件后保存。");
2026-08-04 14:23:07 +08:00
}
}
2026-08-04 14:23:07 +08:00
bool DialogAlgoarg::saveWorkpieceSizes(VrRotorCoreParam& rotorParam)
{
VrRotorCoreParam updated = rotorParam;
updated.workpieceSizes.clear();
updated.defaultWorkpieceIndex = ui->comboBox_defaultWorkpiece->currentData().toInt();
for (int row = 0; row < ui->tableWidget_workpieceSizes->rowCount(); ++row)
{
VrRotorWorkpieceSize size;
size.index = row + 1;
bool radiusOk = false;
bool heightOk = false;
size.radius = ui->tableWidget_workpieceSizes->item(row, 1)->text().toDouble(&radiusOk);
size.height = ui->tableWidget_workpieceSizes->item(row, 2)->text().toDouble(&heightOk);
if (!radiusOk || !heightOk || !std::isfinite(size.radius) || !std::isfinite(size.height) ||
size.radius <= 0.0 || size.height <= 0.0 || size.radius > 1e6 || size.height > 1e6)
{
StyledMessageBox::warning(this, "工件尺寸", QString("第 %1 项半径和高度必须为大于 0 且不超过 1000000 的数值。").arg(row + 1));
return false;
}
updated.workpieceSizes.push_back(size);
}
VrRotorWorkpieceSize defaultSize;
if (!updated.getWorkpieceSize(0, defaultSize))
{
StyledMessageBox::warning(this, "工件尺寸", "工件序号不能重复,默认序号必须在列表中,半径和高度必须大于 0。");
2026-08-04 14:23:07 +08:00
return false;
}
// 同步旧字段便于配置兼容;检测始终按列表选择。
updated.radius = defaultSize.radius;
updated.height = defaultSize.height;
rotorParam = updated;
return true;
}
2026-08-04 14:23:07 +08:00
bool DialogAlgoarg::SaveModelDetectionParamsFromUI(VrAlgorithmParams& algorithmParams)
{
if (!ui || !ui->tableWidget_modelParams || ui->tableWidget_modelParams->rowCount() <= 0) {
return false;
}
algorithmParams.modelParams.clear();
algorithmParams.modelGraySelection.targetGray = ui->doubleSpinBox_targetGray->value();
algorithmParams.modelGraySelection.maxGrayDeviation = ui->doubleSpinBox_maxGrayDeviation->value();
VrModelDetectionRegion& region = algorithmParams.modelDetectionRegion;
region.enabled = ui->groupBox_modelDetectionRegion->isChecked();
region.left = ui->spinBox_modelRegionLeft->value();
region.top = ui->spinBox_modelRegionTop->value();
region.right = ui->spinBox_modelRegionRight->value();
region.bottom = ui->spinBox_modelRegionBottom->value();
if (!region.isValid())
{
StyledMessageBox::warning(this, "2D识别区域", "右边界必须大于左边界,下边界必须大于上边界。");
return false;
}
if (!algorithmParams.modelGraySelection.isValid())
{
return false;
}
for (int row = 0; row < ui->tableWidget_modelParams->rowCount(); ++row)
{
if (!ui->tableWidget_modelParams->item(row, 1) ||
!ui->tableWidget_modelParams->item(row, 2) ||
!ui->tableWidget_modelParams->item(row, 3))
{
return false;
}
VrWorkpieceModelParam model;
bool exposureOk = false;
bool gainOk = false;
bool scoreOk = false;
model.exposure = ui->tableWidget_modelParams->item(row, 1)->text().toUInt(&exposureOk);
model.gain = ui->tableWidget_modelParams->item(row, 2)->text().toUInt(&gainOk);
model.minimumScore = ui->tableWidget_modelParams->item(row, 3)->text().toDouble(&scoreOk);
if (!exposureOk || !gainOk || !scoreOk || !model.isValid())
{
return false;
}
algorithmParams.modelParams.push_back(model);
}
// 保留公共配置结构对旧项目调用方的兼容镜像;转子检测始终使用上面的列表。
algorithmParams.modelParam.exposure = algorithmParams.modelParams.front().exposure;
algorithmParams.modelParam.gain = algorithmParams.modelParams.front().gain;
algorithmParams.modelParam.minimumScore = algorithmParams.modelParams.front().minimumScore;
2026-08-04 14:23:07 +08:00
return true;
}
void DialogAlgoarg::initModelParamsTab()
{
if (ui && ui->tableWidget_modelParams)
{
auto* header = ui->tableWidget_modelParams->horizontalHeader();
for (int column = 0; column < ui->tableWidget_modelParams->columnCount(); ++column)
{
header->setSectionResizeMode(column, QHeaderView::Stretch);
}
ui->tableWidget_modelParams->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
connect(ui->btn_modelParamAdd, &QPushButton::clicked, this, [this]()
{
if (ui->tableWidget_modelParams->rowCount() < 65535)
{
addModelParamRow(VrWorkpieceModelParam());
refreshModelParamRows();
ui->tableWidget_modelParams->selectRow(ui->tableWidget_modelParams->rowCount() - 1);
}
});
}
void DialogAlgoarg::addModelParamRow(const VrWorkpieceModelParam& modelParam)
{
const int row = ui->tableWidget_modelParams->rowCount();
ui->tableWidget_modelParams->insertRow(row);
const QStringList values = {QString::number(row + 1), QString::number(modelParam.exposure),
QString::number(modelParam.gain), QString::number(modelParam.minimumScore, 'g', 15)};
for (int column = 0; column < values.size(); ++column)
{
auto* item = new QTableWidgetItem(values[column]);
item->setForeground(QColor(221, 225, 233));
if (column == 0) item->setFlags(item->flags() & ~Qt::ItemIsEditable);
ui->tableWidget_modelParams->setItem(row, column, item);
}
auto* actions = new WorkpieceSizeActions(ui->tableWidget_modelParams);
ui->tableWidget_modelParams->setCellWidget(row, 4, actions);
const QPersistentModelIndex rowIndex(ui->tableWidget_modelParams->model()->index(row, 0));
connect(actions, &WorkpieceSizeActions::moveRequested, this, [this, rowIndex](int offset)
{ if (rowIndex.isValid()) moveModelParamRow(rowIndex.row(), offset); });
connect(actions, &WorkpieceSizeActions::removeRequested, this, [this, rowIndex]()
{ if (rowIndex.isValid()) removeModelParamRow(rowIndex.row()); });
}
void DialogAlgoarg::refreshModelParamRows()
{
for (int row = 0; row < ui->tableWidget_modelParams->rowCount(); ++row)
{
ui->tableWidget_modelParams->item(row, 0)->setText(QString::number(row + 1));
}
updateModelParamActions();
}
void DialogAlgoarg::updateModelParamActions()
{
const int count = ui->tableWidget_modelParams->rowCount();
for (int row = 0; row < count; ++row)
{
if (auto* actions = qobject_cast<WorkpieceSizeActions*>(ui->tableWidget_modelParams->cellWidget(row, 4)))
actions->setRowPosition(row, count);
}
}
void DialogAlgoarg::moveModelParamRow(int row, int offset)
{
const int target = row + offset;
if (row < 0 || target < 0 || target >= ui->tableWidget_modelParams->rowCount()) return;
for (int column = 1; column <= 3; ++column)
{
auto* source = ui->tableWidget_modelParams->takeItem(row, column);
auto* destination = ui->tableWidget_modelParams->takeItem(target, column);
ui->tableWidget_modelParams->setItem(row, column, destination);
ui->tableWidget_modelParams->setItem(target, column, source);
}
refreshModelParamRows();
ui->tableWidget_modelParams->selectRow(target);
}
void DialogAlgoarg::removeModelParamRow(int row)
{
if (ui->tableWidget_modelParams->rowCount() <= 1) return;
ui->tableWidget_modelParams->removeRow(row);
refreshModelParamRows();
}
2026-08-04 14:23:07 +08:00
void DialogAlgoarg::on_btn_camer_ok_clicked()
{
if (SaveConfigFromUI()) {
StyledMessageBox::information(this, "成功", "配置保存成功!");
accept();
} else {
StyledMessageBox::warning(this, "失败", "配置保存失败,请检查文件权限或参数输入!");
}
}
void DialogAlgoarg::on_btn_camer_cancel_clicked()
{
reject();
}
2026-09-19 09:44:46 +08:00
void DialogAlgoarg::onCalibMatrixLoaded(int cameraIndex, const double* matrix)
2026-08-04 14:23:07 +08:00
{
2026-09-19 09:44:46 +08:00
Q_UNUSED(cameraIndex);
Q_UNUSED(matrix);
2026-08-04 14:23:07 +08:00
StyledMessageBox::information(this, "提示", "已从文件导入标定矩阵");
}
2026-09-19 09:44:46 +08:00
void DialogAlgoarg::onSaveCalibRequested(int cameraIndex, const double* matrix)
2026-08-04 14:23:07 +08:00
{
2026-09-19 09:44:46 +08:00
Q_UNUSED(cameraIndex);
Q_UNUSED(matrix);
2026-08-04 14:23:07 +08:00
2026-09-19 09:44:46 +08:00
if (SaveConfigFromUI())
{
StyledMessageBox::information(this, "成功", "手眼标定参数已保存!");
2026-08-04 14:23:07 +08:00
}
2026-09-19 09:44:46 +08:00
else
{
StyledMessageBox::warning(this, "失败", "保存手眼标定参数失败!");
2026-08-04 14:23:07 +08:00
}
}
2026-09-19 09:44:46 +08:00
void DialogAlgoarg::LoadHandEyeCalibConfig()
2026-08-04 14:23:07 +08:00
{
2026-09-19 09:44:46 +08:00
if (!m_pConfigManager || !m_handEyeCalibWidget)
{
return;
2026-08-04 14:23:07 +08:00
}
2026-09-19 09:44:46 +08:00
const ConfigResult configData = m_pConfigManager->GetConfigResult();
const int count = std::max(1, static_cast<int>(configData.cameraList.size()));
for (int cameraIndex = 1; cameraIndex <= count; ++cameraIndex)
2026-09-19 09:44:46 +08:00
{
const VrHandEyeCalibMatrix defaultMatrix;
const VrHandEyeCalibMatrix* calibMatrix = cameraIndex == 1 ? &configData.handEyeCalibMatrix : &defaultMatrix;
for (const auto& item : configData.handEyeCalibMatricesByCamera)
{
if (item.first == cameraIndex)
{
calibMatrix = &item.second;
}
}
m_handEyeCalibWidget->setCalibData(cameraIndex, calibMatrix->matrix,
!isIdentityMatrix(calibMatrix->matrix));
2026-08-04 14:23:07 +08:00
}
}
void DialogAlgoarg::LoadPlcRobotServerConfigToUI(const VrPlcRobotServerConfig& config)
{
if (!m_networkConfigWidget) return;
NetworkConfigData data;
data.poseOutputOrder = config.poseOutputOrder;
data.byteOrder = config.byteOrder;
data.dirVectorInvert = config.dirVectorInvert;
data.addrPhotoRequest = config.registerConfig.addrTrigger;
data.addrDataComplete = config.registerConfig.addrWorkStatus;
data.addrCoordDataStart = config.registerConfig.addrCoordDataStart;
data.tcpServerPort = config.robotServerPort;
m_networkConfigWidget->setConfig(data);
}
bool DialogAlgoarg::SavePlcRobotServerConfigFromUI(VrPlcRobotServerConfig& config)
{
if (!m_networkConfigWidget) return false;
const NetworkConfigData data = m_networkConfigWidget->getConfig();
if (data.tcpServerPort <= 0 || data.tcpServerPort > 65535 ||
data.addrPhotoRequest < 0 ||
data.addrDataComplete < 0 ||
data.addrCoordDataStart < 0) {
return false;
}
config.robotServerPort = data.tcpServerPort;
config.registerConfig.addrTrigger = data.addrPhotoRequest;
config.registerConfig.addrWorkStatus = data.addrDataComplete;
config.registerConfig.addrCoordDataStart = data.addrCoordDataStart;
config.poseOutputOrder = data.poseOutputOrder;
config.dirVectorInvert = data.dirVectorInvert;
config.byteOrder = data.byteOrder;
return true;
}