466 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "dialogalgoarg.h"
#include "ui_dialogalgoarg.h"
#include "HoleDetectionPresenter.h"
#include "HandEyeCalibWidget.h"
#include "NetworkConfigWidget.h"
#include "PathManager.h"
#include "StyledMessageBox.h"
#include "VrLog.h"
#include <cstring>
#include <cmath>
DialogAlgoarg::DialogAlgoarg(ConfigManager* configManager, HoleDetectionPresenter* presenter, QWidget *parent)
: QDialog(parent)
, ui(new Ui::DialogAlgoarg)
, m_pConfigManager(configManager)
, m_presenter(presenter)
, m_handEyeCalibWidget(nullptr)
, m_networkConfigWidget(nullptr)
{
try {
ui->setupUi(this);
// 获取配置文件路径
m_configFilePath = PathManager::GetInstance().GetConfigFilePath();
if (m_configFilePath.isEmpty()) {
StyledMessageBox::critical(this, "错误", "无法获取配置文件路径!");
return;
}
// 初始化排序模式下拉框
InitSortModeComboBox();
// 初始化姿态输出顺序下拉框HoleDetection 特有)
InitPoseOutputOrderComboBox();
// 初始化手眼标定 tab使用共享 HandEyeCalibWidget
InitHandEyeCalibTab();
// 初始化网络配置 tab使用共享 NetworkConfigWidget
InitNetworkConfigTab();
// 从配置文件加载数据到界面
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);
}
}
// ========== 配置加载与保存 ==========
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();
} 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;
}
// 保存网络配置(使用共享控件)
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;
}
}
// 保存姿态输出顺序HoleDetection 特有)
systemConfig.configResult.plcRobotServerConfig.poseOutputOrder =
ui->comboBox_poseOutputOrder->currentData().toInt();
// 更新并保存配置到文件
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;
} catch (const std::exception& e) {
LOG_ERROR("Exception in SaveConfigFromUI: %s\n", e.what());
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));
ui->lineEdit_edgeBoundaryFilterDist->setText(QString::number(param.edgeBoundaryFilterDist));
}
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));
ui->lineEdit_minHoleDepth->setText(QString::number(param.minHoleDepth));
}
void DialogAlgoarg::LoadSortModeToUI(int sortMode)
{
if (!ui) return;
int index = ui->comboBox_sortMode->findData(sortMode);
if (index >= 0) {
ui->comboBox_sortMode->setCurrentIndex(index);
}
}
// ========== 从 UI 保存参数 ==========
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;
param.edgeBoundaryFilterDist = ui->lineEdit_edgeBoundaryFilterDist->text().toDouble(&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;
param.minHoleDepth = ui->lineEdit_minHoleDepth->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;
}
// ========== 按钮事件 ==========
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();
}
// ========== 下拉框初始化 ==========
void DialogAlgoarg::InitPoseOutputOrderComboBox()
{
ui->comboBox_poseOutputOrder->addItem("RPY (Roll, Pitch, Yaw)", POSE_ORDER_RPY);
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);
}