458 lines
16 KiB
C++
Raw Normal View History

2026-03-11 23:40:06 +08:00
#include "dialogalgoarg.h"
#include "ui_dialogalgoarg.h"
#include "HoleDetectionPresenter.h"
#include "HandEyeCalibWidget.h"
#include "NetworkConfigWidget.h"
2026-03-11 23:40:06 +08:00
#include "PathManager.h"
#include "StyledMessageBox.h"
#include "VrLog.h"
#include <cstring>
#include <cmath>
2026-03-11 23:40:06 +08:00
DialogAlgoarg::DialogAlgoarg(ConfigManager* configManager, HoleDetectionPresenter* presenter, QWidget *parent)
2026-03-11 23:40:06 +08:00
: QDialog(parent)
, ui(new Ui::DialogAlgoarg)
, m_pConfigManager(configManager)
, m_presenter(presenter)
, m_handEyeCalibWidget(nullptr)
, m_networkConfigWidget(nullptr)
2026-03-11 23:40:06 +08:00
{
try {
ui->setupUi(this);
// 获取配置文件路径
m_configFilePath = PathManager::GetInstance().GetConfigFilePath();
if (m_configFilePath.isEmpty()) {
StyledMessageBox::critical(this, "错误", "无法获取配置文件路径!");
return;
}
// 初始化排序模式下拉框
InitSortModeComboBox();
2026-03-11 23:40:06 +08:00
// 初始化姿态输出顺序下拉框HoleDetection 特有)
2026-03-11 23:40:06 +08:00
InitPoseOutputOrderComboBox();
// 初始化手眼标定 tab使用共享 HandEyeCalibWidget
InitHandEyeCalibTab();
2026-03-11 23:40:06 +08:00
// 初始化网络配置 tab使用共享 NetworkConfigWidget
InitNetworkConfigTab();
2026-03-11 23:40:06 +08:00
// 从配置文件加载数据到界面
LoadConfigToUI();
} catch (const std::exception& e) {
StyledMessageBox::critical(this, "初始化错误", QString("对话框初始化失败: %1").arg(e.what()));
} catch (...) {
StyledMessageBox::critical(this, "初始化错误", "对话框初始化失败!(未知错误)");
}
}
DialogAlgoarg::~DialogAlgoarg()
{
delete ui;
}
// ========== 手眼标定相关实现 ==========
void DialogAlgoarg::InitHandEyeCalibTab()
{
if (!m_presenter) return;
m_handEyeCalibWidget = new HandEyeCalibWidget(this);
m_handEyeCalibWidget->setMatrixEditable(true);
ui->verticalLayout_handEyeCalibHost->addWidget(m_handEyeCalibWidget);
m_handEyeCalibWidget->setDefaultFilePath(
PathManager::GetInstance().GetAppConfigDirectory());
// 获取相机列表
const auto& cameraList = m_presenter->GetCameraList();
QVector<HandEyeCalibCameraInfo> calibCameraList;
if (cameraList.empty()) {
HandEyeCalibCameraInfo defaultCam;
defaultCam.cameraIndex = 1;
defaultCam.displayName = QString::fromUtf8("相机 1");
calibCameraList.append(defaultCam);
} else {
for (size_t i = 0; i < cameraList.size(); ++i) {
HandEyeCalibCameraInfo info;
info.cameraIndex = static_cast<int>(i + 1);
info.displayName = QString::fromStdString(cameraList[i].first);
calibCameraList.append(info);
}
}
m_handEyeCalibWidget->setCameraList(calibCameraList);
// 加载各相机的标定矩阵
for (const auto& camInfo : calibCameraList) {
CalibMatrix calibMatrix = m_presenter->GetClibMatrix(camInfo.cameraIndex - 1);
bool hasCalibData = false;
for (int i = 0; i < 16; ++i) {
double identity = (i / 4 == i % 4) ? 1.0 : 0.0;
if (qAbs(calibMatrix.clibMatrix[i] - identity) > 1e-9) {
hasCalibData = true;
break;
}
}
m_handEyeCalibWidget->setCalibData(camInfo.cameraIndex, calibMatrix.clibMatrix, hasCalibData);
}
connect(m_handEyeCalibWidget, &HandEyeCalibWidget::calibMatrixLoaded,
this, &DialogAlgoarg::onCalibMatrixLoaded);
connect(m_handEyeCalibWidget, &HandEyeCalibWidget::saveCalibRequested,
this, &DialogAlgoarg::onSaveCalibRequested);
}
void DialogAlgoarg::onCalibMatrixLoaded(int cameraIndex, const double* matrix)
{
Q_UNUSED(cameraIndex);
Q_UNUSED(matrix);
StyledMessageBox::information(this, "提示", "已从文件导入标定矩阵");
}
void DialogAlgoarg::onSaveCalibRequested(int cameraIndex, const double* matrix)
{
Q_UNUSED(cameraIndex);
Q_UNUSED(matrix);
if (SaveConfigFromUI()) {
StyledMessageBox::information(this, "成功", "手眼标定参数已保存!");
} else {
StyledMessageBox::warning(this, "失败", "保存手眼标定参数失败!");
}
}
// ========== 网络配置相关实现 ==========
void DialogAlgoarg::InitNetworkConfigTab()
{
if (!m_pConfigManager) return;
// 创建网络配置控件不显示PLC配置显示TCP端口配置
m_networkConfigWidget = new NetworkConfigWidget(false, true, this);
ui->verticalLayout_networkConfigHost->addWidget(m_networkConfigWidget);
}
void DialogAlgoarg::LoadNetworkConfigToUI()
{
if (!m_pConfigManager || !m_networkConfigWidget) return;
ConfigResult configResult = m_pConfigManager->GetConfigResult();
const VrPlcRobotServerConfig& plcConfig = configResult.plcRobotServerConfig;
NetworkConfigData netConfig;
netConfig.tcpServerPort = plcConfig.tcpServerPort;
netConfig.dirVectorInvert = plcConfig.dirVectorInvert;
// eulerOrder: 从第一个手眼标定矩阵的旋转顺序获取
if (!configResult.handEyeCalibMatrixList.empty()) {
netConfig.eulerOrder = configResult.handEyeCalibMatrixList[0].eulerOrder;
}
m_networkConfigWidget->setConfig(netConfig);
// 加载姿态输出顺序
int poseOrderIndex = ui->comboBox_poseOutputOrder->findData(plcConfig.poseOutputOrder);
if (poseOrderIndex >= 0) {
ui->comboBox_poseOutputOrder->setCurrentIndex(poseOrderIndex);
}
}
// ========== 配置加载与保存 ==========
2026-03-11 23:40:06 +08:00
void DialogAlgoarg::LoadConfigToUI()
{
if (!m_pConfigManager) {
StyledMessageBox::critical(this, "错误", "配置对象未初始化!");
return;
}
try {
ConfigResult configData = m_pConfigManager->GetConfigResult();
const VrAlgorithmParams& algoParams = configData.algorithmParams;
// 加载各个参数组
LoadDetectionParamToUI(algoParams.detectionParam);
LoadFilterParamToUI(algoParams.filterParam);
LoadSortModeToUI(algoParams.sortMode);
// 加载网络配置
LoadNetworkConfigToUI();
2026-03-11 23:40:06 +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;
const VrAlgorithmParams& algoParams = configData.algorithmParams;
LoadDetectionParamToUI(algoParams.detectionParam);
LoadFilterParamToUI(algoParams.filterParam);
LoadSortModeToUI(algoParams.sortMode);
} catch (...) {
LOG_ERROR("Unknown exception in LoadConfigToUI\n");
StyledMessageBox::warning(this, "警告", "加载配置文件失败(未知错误),将使用默认参数显示");
ConfigResult configData;
const VrAlgorithmParams& algoParams = configData.algorithmParams;
LoadDetectionParamToUI(algoParams.detectionParam);
LoadFilterParamToUI(algoParams.filterParam);
LoadSortModeToUI(algoParams.sortMode);
}
}
bool DialogAlgoarg::SaveConfigFromUI()
{
if (!m_pConfigManager) {
return false;
}
try {
SystemConfig systemConfig = m_pConfigManager->GetConfig();
VrAlgorithmParams& algoParams = systemConfig.configResult.algorithmParams;
// 保存检测参数
if (!SaveDetectionParamFromUI(algoParams.detectionParam)) {
StyledMessageBox::warning(this, "错误", "检测参数输入有误,请检查!");
return false;
}
// 保存过滤参数
if (!SaveFilterParamFromUI(algoParams.filterParam)) {
StyledMessageBox::warning(this, "错误", "过滤参数输入有误,请检查!");
return false;
}
// 保存排序模式
if (!SaveSortModeFromUI(algoParams.sortMode)) {
StyledMessageBox::warning(this, "错误", "排序模式设置有误,请检查!");
return false;
}
// 保存手眼标定矩阵(使用共享控件)
if (m_handEyeCalibWidget && m_presenter) {
const auto& oldMatrixList = systemConfig.configResult.handEyeCalibMatrixList;
std::vector<VrHandEyeCalibMatrix> newMatrixList;
const auto& cameraList = m_presenter->GetCameraList();
int cameraCount = std::max(1, static_cast<int>(cameraList.size()));
for (int i = 0; i < cameraCount; ++i) {
int camIdx = i + 1;
// 先从已有配置中获取该相机的矩阵作为基础
VrHandEyeCalibMatrix calibMatrix;
calibMatrix.cameraIndex = camIdx;
for (const auto& old : oldMatrixList) {
if (old.cameraIndex == camIdx) {
calibMatrix = old;
break;
}
}
// 如果控件中有更新的数据则覆盖
bool isCalibrated = false;
double matrix[16];
if (m_handEyeCalibWidget->getCalibData(camIdx, matrix, isCalibrated) && isCalibrated) {
memcpy(calibMatrix.matrix, matrix, sizeof(double) * 16);
}
newMatrixList.push_back(calibMatrix);
}
systemConfig.configResult.handEyeCalibMatrixList = newMatrixList;
2026-03-11 23:40:06 +08:00
}
// 保存网络配置(使用共享控件)
if (m_networkConfigWidget) {
NetworkConfigData netConfig = m_networkConfigWidget->getConfig();
systemConfig.configResult.plcRobotServerConfig.tcpServerPort = netConfig.tcpServerPort;
systemConfig.configResult.plcRobotServerConfig.dirVectorInvert = netConfig.dirVectorInvert;
// 保存 eulerOrder 到所有手眼标定矩阵
for (auto& calibMatrix : systemConfig.configResult.handEyeCalibMatrixList) {
calibMatrix.eulerOrder = netConfig.eulerOrder;
}
2026-03-11 23:40:06 +08:00
}
// 保存姿态输出顺序HoleDetection 特有)
systemConfig.configResult.plcRobotServerConfig.poseOutputOrder =
ui->comboBox_poseOutputOrder->currentData().toInt();
2026-03-11 23:40:06 +08:00
// 更新并保存配置到文件
if (!m_pConfigManager->UpdateFullConfig(systemConfig)) {
return false;
}
if (!m_pConfigManager->SaveConfigToFile(m_configFilePath.toStdString())) {
return false;
}
// 通知 Presenter 重新加载配置
if (m_presenter) {
m_presenter->OnConfigChanged(systemConfig.configResult);
}
return true;
2026-03-11 23:40:06 +08:00
} catch (const std::exception& e) {
LOG_ERROR("Exception in SaveConfigFromUI: %s\n", e.what());
2026-03-11 23:40:06 +08:00
return false;
}
}
// ========== 参数加载到 UI ==========
void DialogAlgoarg::LoadDetectionParamToUI(const VrHoleDetectionParam& param)
{
if (!ui) return;
ui->lineEdit_neighborCount->setText(QString::number(param.neighborCount));
ui->lineEdit_angleThresholdPos->setText(QString::number(param.angleThresholdPos));
ui->lineEdit_angleThresholdNeg->setText(QString::number(param.angleThresholdNeg));
ui->lineEdit_minPitDepth->setText(QString::number(param.minPitDepth));
ui->lineEdit_minRadius->setText(QString::number(param.minRadius));
ui->lineEdit_maxRadius->setText(QString::number(param.maxRadius));
ui->lineEdit_expansionSize1->setText(QString::number(param.expansionSize1));
ui->lineEdit_expansionSize2->setText(QString::number(param.expansionSize2));
ui->lineEdit_minVTransitionPoints->setText(QString::number(param.minVTransitionPoints));
}
void DialogAlgoarg::LoadFilterParamToUI(const VrHoleFilterParam& param)
{
if (!ui) return;
ui->lineEdit_maxEccentricity->setText(QString::number(param.maxEccentricity));
ui->lineEdit_minAngularCoverage->setText(QString::number(param.minAngularCoverage));
ui->lineEdit_maxRadiusFitRatio->setText(QString::number(param.maxRadiusFitRatio));
ui->lineEdit_minQualityScore->setText(QString::number(param.minQualityScore));
ui->lineEdit_maxPlaneResidual->setText(QString::number(param.maxPlaneResidual));
ui->lineEdit_maxAngularGap->setText(QString::number(param.maxAngularGap));
ui->lineEdit_minInlierRatio->setText(QString::number(param.minInlierRatio));
}
void DialogAlgoarg::LoadSortModeToUI(int sortMode)
{
if (!ui) return;
int index = ui->comboBox_sortMode->findData(sortMode);
if (index >= 0) {
ui->comboBox_sortMode->setCurrentIndex(index);
}
}
// ========== 从 UI 保存参数 ==========
2026-03-11 23:40:06 +08:00
bool DialogAlgoarg::SaveDetectionParamFromUI(VrHoleDetectionParam& param)
{
bool ok = true;
param.neighborCount = ui->lineEdit_neighborCount->text().toInt(&ok);
if (!ok) return false;
param.angleThresholdPos = ui->lineEdit_angleThresholdPos->text().toDouble(&ok);
if (!ok) return false;
param.angleThresholdNeg = ui->lineEdit_angleThresholdNeg->text().toDouble(&ok);
if (!ok) return false;
param.minPitDepth = ui->lineEdit_minPitDepth->text().toDouble(&ok);
if (!ok) return false;
param.minRadius = ui->lineEdit_minRadius->text().toDouble(&ok);
if (!ok) return false;
param.maxRadius = ui->lineEdit_maxRadius->text().toDouble(&ok);
if (!ok) return false;
param.expansionSize1 = ui->lineEdit_expansionSize1->text().toInt(&ok);
if (!ok) return false;
param.expansionSize2 = ui->lineEdit_expansionSize2->text().toInt(&ok);
if (!ok) return false;
param.minVTransitionPoints = ui->lineEdit_minVTransitionPoints->text().toInt(&ok);
if (!ok) return false;
return true;
}
bool DialogAlgoarg::SaveFilterParamFromUI(VrHoleFilterParam& param)
{
bool ok = true;
param.maxEccentricity = ui->lineEdit_maxEccentricity->text().toDouble(&ok);
if (!ok) return false;
param.minAngularCoverage = ui->lineEdit_minAngularCoverage->text().toDouble(&ok);
if (!ok) return false;
param.maxRadiusFitRatio = ui->lineEdit_maxRadiusFitRatio->text().toDouble(&ok);
if (!ok) return false;
param.minQualityScore = ui->lineEdit_minQualityScore->text().toDouble(&ok);
if (!ok) return false;
param.maxPlaneResidual = ui->lineEdit_maxPlaneResidual->text().toDouble(&ok);
if (!ok) return false;
param.maxAngularGap = ui->lineEdit_maxAngularGap->text().toDouble(&ok);
if (!ok) return false;
param.minInlierRatio = ui->lineEdit_minInlierRatio->text().toDouble(&ok);
if (!ok) return false;
return true;
}
bool DialogAlgoarg::SaveSortModeFromUI(int& sortMode)
{
if (!ui) return false;
sortMode = ui->comboBox_sortMode->currentData().toInt();
return true;
}
// ========== 按钮事件 ==========
2026-03-11 23:40:06 +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-03-11 23:40:06 +08:00
void DialogAlgoarg::InitPoseOutputOrderComboBox()
{
ui->comboBox_poseOutputOrder->addItem("RPY (Roll, Pitch, Yaw)", POSE_ORDER_RPY);
2026-03-11 23:40:06 +08:00
ui->comboBox_poseOutputOrder->addItem("RYP (Roll, Yaw, Pitch)", POSE_ORDER_RYP);
ui->comboBox_poseOutputOrder->addItem("PRY (Pitch, Roll, Yaw)", POSE_ORDER_PRY);
ui->comboBox_poseOutputOrder->addItem("PYR (Pitch, Yaw, Roll)", POSE_ORDER_PYR);
ui->comboBox_poseOutputOrder->addItem("YRP (Yaw, Roll, Pitch)", POSE_ORDER_YRP);
ui->comboBox_poseOutputOrder->addItem("YPR (Yaw, Pitch, Roll)", POSE_ORDER_YPR);
ui->comboBox_poseOutputOrder->setCurrentIndex(0);
}
void DialogAlgoarg::InitSortModeComboBox()
{
ui->comboBox_sortMode->addItem("不排序", HOLE_SORT_NONE);
ui->comboBox_sortMode->addItem("按半径排序(最大优先)", HOLE_SORT_BY_RADIUS);
ui->comboBox_sortMode->addItem("按深度排序(最深优先)", HOLE_SORT_BY_DEPTH);
ui->comboBox_sortMode->addItem("按质量分数排序(最高优先)", HOLE_SORT_BY_QUALITY);
ui->comboBox_sortMode->setCurrentIndex(0);
}