#include "dialogalgoarg.h" #include "ui_dialogalgoarg.h" #include #include #include #include #include #include #include #include #include #include #include "PathManager.h" #include "StyledMessageBox.h" #include "HandEyeCalibWidget.h" #include "VrLog.h" DialogAlgoarg::DialogAlgoarg(ConfigManager* configManager, QWidget *parent) : QDialog(parent) , ui(new Ui::DialogAlgoarg) , m_pConfigManager(configManager) , m_handEyeCalibWidget(nullptr) { try { ui->setupUi(this); // 获取配置文件路径 m_configFilePath = PathManager::GetInstance().GetConfigFilePath(); // 检查配置文件路径是否有效 if (m_configFilePath.isEmpty()) { StyledMessageBox::critical(this, "错误", "无法获取配置文件路径!"); return; } // 初始化旋转顺序下拉框 InitEulerOrderComboBox(); // 初始化姿态输出顺序下拉框 InitPoseOutputOrderComboBox(); // 初始化方向向量反向下拉框 InitDirVectorInvertComboBox(); // 初始化数据字节序下拉框 InitByteOrderComboBox(); // 从配置文件加载数据到界面 LoadConfigToUI(); // 初始化手眼标定 tab(在 LoadConfigToUI 之后,因为需要配置数据) InitHandEyeCalibTab(); } 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(); // 检查配置文件路径是否有效 if (m_configFilePath.isEmpty()) { LOG_WARNING("Config file path is empty\n"); } // 加载算法参数到UI const VrAlgorithmParams& algoParams = configData.algorithmParams; // 加载各个参数组 LoadWorkpieceHoleParamToUI(algoParams.workpieceHoleParam); LoadLineSegParamToUI(algoParams.lineSegParam); LoadFilterParamToUI(algoParams.filterParam); LoadGrowParamToUI(algoParams.growParam); // 加载网络配置(PLC和机械臂服务端) LoadPlcRobotServerConfigToUI(configData.plcRobotServerConfig); // 加载旋转顺序 int eulerOrder = configData.handEyeCalibMatrix.eulerOrder; int index = ui->comboBox_eulerOrder->findData(eulerOrder); if (index >= 0) { ui->comboBox_eulerOrder->setCurrentIndex(index); } } 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; LoadWorkpieceHoleParamToUI(algoParams.workpieceHoleParam); LoadLineSegParamToUI(algoParams.lineSegParam); LoadFilterParamToUI(algoParams.filterParam); LoadGrowParamToUI(algoParams.growParam); } catch (...) { LOG_ERROR("Unknown exception in LoadConfigToUI\n"); StyledMessageBox::warning(this, "警告", "加载配置文件失败(未知错误),将使用默认参数显示"); // 发生未知异常时使用默认参数 ConfigResult configData; const VrAlgorithmParams& algoParams = configData.algorithmParams; LoadWorkpieceHoleParamToUI(algoParams.workpieceHoleParam); LoadLineSegParamToUI(algoParams.lineSegParam); LoadFilterParamToUI(algoParams.filterParam); LoadGrowParamToUI(algoParams.growParam); } } void DialogAlgoarg::LoadWorkpieceHoleParamToUI(const VrWorkpieceHoleParam& param) { if (!ui) return; ui->lineEdit_workpieceType->setText(QString::number(param.workpieceType)); ui->lineEdit_holeDiameter->setText(QString::number(param.holeDiameter)); ui->lineEdit_holeDist_L->setText(QString::number(param.holeDist_L)); ui->lineEdit_holeDist_W->setText(QString::number(param.holeDist_W)); } void DialogAlgoarg::LoadLineSegParamToUI(const VrLineSegParam& param) { if (!ui) return; ui->lineEdit_distScale->setText(QString::number(param.distScale)); ui->lineEdit_segGapTh_y->setText(QString::number(param.segGapTh_y)); ui->lineEdit_segGapTh_z->setText(QString::number(param.segGapTh_z)); } void DialogAlgoarg::LoadFilterParamToUI(const VrOutlierFilterParam& param) { if (!ui) return; ui->lineEdit_continuityTh->setText(QString::number(param.continuityTh)); ui->lineEdit_outlierTh->setText(QString::number(param.outlierTh)); } void DialogAlgoarg::LoadGrowParamToUI(const VrTreeGrowParam& param) { if (!ui) return; ui->lineEdit_maxLineSkipNum->setText(QString::number(param.maxLineSkipNum)); ui->lineEdit_yDeviation_max->setText(QString::number(param.yDeviation_max)); ui->lineEdit_maxSkipDistance->setText(QString::number(param.maxSkipDistance)); ui->lineEdit_zDeviation_max->setText(QString::number(param.zDeviation_max)); ui->lineEdit_minLTypeTreeLen->setText(QString::number(param.minLTypeTreeLen)); ui->lineEdit_minVTypeTreeLen->setText(QString::number(param.minVTypeTreeLen)); } bool DialogAlgoarg::SaveConfigFromUI() { if (!m_pConfigManager) { return false; } try { // 获取当前配置 SystemConfig systemConfig = m_pConfigManager->GetConfig(); VrAlgorithmParams& algoParams = systemConfig.configResult.algorithmParams; // 保存各个参数组 if (!SaveWorkpieceHoleParamFromUI(algoParams.workpieceHoleParam)) { StyledMessageBox::warning(this, "错误", "工件孔参数输入有误,请检查!"); return false; } if (!SaveLineSegParamFromUI(algoParams.lineSegParam)) { StyledMessageBox::warning(this, "错误", "线段分割参数输入有误,请检查!"); return false; } if (!SaveFilterParamFromUI(algoParams.filterParam)) { StyledMessageBox::warning(this, "错误", "滤波参数输入有误,请检查!"); return false; } if (!SaveGrowParamFromUI(algoParams.growParam)) { StyledMessageBox::warning(this, "错误", "生长参数输入有误,请检查!"); return false; } // 保存网络配置(PLC和机械臂服务端) if (!SavePlcRobotServerConfigFromUI(systemConfig.configResult.plcRobotServerConfig)) { StyledMessageBox::warning(this, "错误", "网络配置输入有误,请检查!"); return false; } // 保存手眼标定矩阵:从共享控件读取 VrHandEyeCalibMatrix& calibMatrix = systemConfig.configResult.handEyeCalibMatrix; if (m_handEyeCalibWidget) { // 尝试从控件获取当前相机的矩阵数据 int camIdx = m_handEyeCalibWidget->currentCameraIndex(); if (camIdx >= 0) { bool isCalibrated = false; double matrix[16]; if (m_handEyeCalibWidget->getCalibData(camIdx, matrix, isCalibrated) && isCalibrated) { memcpy(calibMatrix.matrix, matrix, sizeof(double) * 16); } } } // euler order 仍从 comboBox 读取 calibMatrix.eulerOrder = ui->comboBox_eulerOrder->currentData().toInt(); // 更新并保存配置到文件 if (!m_pConfigManager->UpdateFullConfig(systemConfig)) { return false; } return m_pConfigManager->SaveConfigToFile(m_configFilePath.toStdString()); } catch (const std::exception& e) { return false; } } bool DialogAlgoarg::SaveWorkpieceHoleParamFromUI(VrWorkpieceHoleParam& param) { bool ok = true; param.workpieceType = ui->lineEdit_workpieceType->text().toInt(&ok); if (!ok) return false; param.holeDiameter = ui->lineEdit_holeDiameter->text().toDouble(&ok); if (!ok) return false; param.holeDist_L = ui->lineEdit_holeDist_L->text().toDouble(&ok); if (!ok) return false; param.holeDist_W = ui->lineEdit_holeDist_W->text().toDouble(&ok); if (!ok) return false; return true; } bool DialogAlgoarg::SaveLineSegParamFromUI(VrLineSegParam& param) { bool ok = true; param.distScale = ui->lineEdit_distScale->text().toDouble(&ok); if (!ok) return false; param.segGapTh_y = ui->lineEdit_segGapTh_y->text().toDouble(&ok); if (!ok) return false; param.segGapTh_z = ui->lineEdit_segGapTh_z->text().toDouble(&ok); if (!ok) return false; return true; } bool DialogAlgoarg::SaveFilterParamFromUI(VrOutlierFilterParam& param) { bool ok = true; param.continuityTh = ui->lineEdit_continuityTh->text().toDouble(&ok); if (!ok) return false; param.outlierTh = ui->lineEdit_outlierTh->text().toDouble(&ok); if (!ok) return false; return true; } bool DialogAlgoarg::SaveGrowParamFromUI(VrTreeGrowParam& param) { bool ok = true; param.maxLineSkipNum = ui->lineEdit_maxLineSkipNum->text().toInt(&ok); if (!ok) return false; param.yDeviation_max = ui->lineEdit_yDeviation_max->text().toDouble(&ok); if (!ok) return false; param.maxSkipDistance = ui->lineEdit_maxSkipDistance->text().toDouble(&ok); if (!ok) return false; param.zDeviation_max = ui->lineEdit_zDeviation_max->text().toDouble(&ok); if (!ok) return false; param.minLTypeTreeLen = ui->lineEdit_minLTypeTreeLen->text().toDouble(&ok); if (!ok) return false; param.minVTypeTreeLen = ui->lineEdit_minVTypeTreeLen->text().toDouble(&ok); if (!ok) return false; 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::InitHandEyeCalibTab() { if (!ui || !m_pConfigManager) return; // 创建共享控件并嵌入 tab 的 layout m_handEyeCalibWidget = new HandEyeCalibWidget(this); m_handEyeCalibWidget->setMatrixEditable(true); // WorkpieceHoleApp 矩阵可编辑 ui->verticalLayout_handEyeCalibHost->addWidget(m_handEyeCalibWidget); // 设置默认文件路径 m_handEyeCalibWidget->setDefaultFilePath( PathManager::GetInstance().GetAppConfigDirectory()); // 从配置中获取相机列表填充控件 ConfigResult configData = m_pConfigManager->GetConfigResult(); QVector cameraList; if (configData.cameraList.empty()) { // 如果没有配置相机列表,提供默认项 HandEyeCalibCameraInfo defaultCam; defaultCam.cameraIndex = 1; defaultCam.displayName = QString::fromUtf8("相机 1"); cameraList.append(defaultCam); } else { for (size_t i = 0; i < configData.cameraList.size(); ++i) { const DeviceInfo& camera = configData.cameraList[i]; HandEyeCalibCameraInfo info; info.cameraIndex = static_cast(i + 1); // 1-based info.displayName = QString::fromStdString(camera.name); cameraList.append(info); } } m_handEyeCalibWidget->setCameraList(cameraList); // 加载已有的手眼标定矩阵数据到控件 const VrHandEyeCalibMatrix& calibMatrix = configData.handEyeCalibMatrix; // WorkpieceHoleApp 只有一个矩阵,默认关联到第一个相机 int defaultCamIdx = cameraList.isEmpty() ? 1 : cameraList.first().cameraIndex; // 检查矩阵是否不是单位矩阵(即已标定) bool hasCalibData = false; for (int i = 0; i < 16; ++i) { double identity = (i / 4 == i % 4) ? 1.0 : 0.0; if (qAbs(calibMatrix.matrix[i] - identity) > 1e-9) { hasCalibData = true; break; } } m_handEyeCalibWidget->setCalibData(defaultCamIdx, calibMatrix.matrix, 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::InitEulerOrderComboBox() { // 添加外旋旋转顺序选项(工业机械臂常用) ui->comboBox_eulerOrder->addItem("RZ-RY-RX (外旋ZYX)", 11); // ABB、KUKA、发那科等常用 ui->comboBox_eulerOrder->addItem("RX-RY-RZ (外旋XYZ)", 10); ui->comboBox_eulerOrder->addItem("RZ-RX-RY (外旋ZXY)", 12); ui->comboBox_eulerOrder->addItem("RY-RX-RZ (外旋YXZ)", 13); ui->comboBox_eulerOrder->addItem("RY-RZ-RX (外旋YZX)", 14); ui->comboBox_eulerOrder->addItem("RX-RZ-RY (外旋XZY)", 15); // 默认选择外旋 ZYX ui->comboBox_eulerOrder->setCurrentIndex(0); } 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); // 默认选择 RPY ui->comboBox_poseOutputOrder->setCurrentIndex(0); } void DialogAlgoarg::InitDirVectorInvertComboBox() { // 添加方向向量反向选项 ui->comboBox_dirVectorInvert->addItem("不反向", DIR_INVERT_NONE); ui->comboBox_dirVectorInvert->addItem("XY反向", DIR_INVERT_XY); ui->comboBox_dirVectorInvert->addItem("XZ反向", DIR_INVERT_XZ); ui->comboBox_dirVectorInvert->addItem("YZ反向(默认)", DIR_INVERT_YZ); // 默认选择 YZ反向(兼容原有行为) ui->comboBox_dirVectorInvert->setCurrentIndex(3); } void DialogAlgoarg::InitByteOrderComboBox() { // 添加数据字节序选项 ui->comboBox_byteOrder->addItem("大端序 (ABCD)", BYTE_ORDER_BIG_ENDIAN); ui->comboBox_byteOrder->addItem("小端序 (DCBA)", BYTE_ORDER_LITTLE_ENDIAN); // 默认选择大端序 ui->comboBox_byteOrder->setCurrentIndex(0); } void DialogAlgoarg::LoadPlcRobotServerConfigToUI(const VrPlcRobotServerConfig& config) { if (!ui) return; // 加载PLC服务端配置 ui->lineEdit_plcServerIp->setText(QString::fromStdString(config.plcServerIp)); ui->lineEdit_plcServerPort->setText(QString::number(config.plcServerPort)); // 加载PLC寄存器地址配置 ui->lineEdit_addrPhotoRequest->setText(QString::number(config.registerConfig.addrPhotoRequest)); ui->lineEdit_addrDataComplete->setText(QString::number(config.registerConfig.addrDataComplete)); ui->lineEdit_addrCoordDataStart->setText(QString::number(config.registerConfig.addrCoordDataStart)); // 加载姿态输出顺序 int poseOrderIndex = ui->comboBox_poseOutputOrder->findData(config.poseOutputOrder); if (poseOrderIndex >= 0) { ui->comboBox_poseOutputOrder->setCurrentIndex(poseOrderIndex); } // 加载方向向量反向配置 int dirInvertIndex = ui->comboBox_dirVectorInvert->findData(config.dirVectorInvert); if (dirInvertIndex >= 0) { ui->comboBox_dirVectorInvert->setCurrentIndex(dirInvertIndex); } // 加载数据字节序配置 int byteOrderIndex = ui->comboBox_byteOrder->findData(config.byteOrder); if (byteOrderIndex >= 0) { ui->comboBox_byteOrder->setCurrentIndex(byteOrderIndex); } } bool DialogAlgoarg::SavePlcRobotServerConfigFromUI(VrPlcRobotServerConfig& config) { if (!ui) return false; // 获取PLC服务端IP QString plcIp = ui->lineEdit_plcServerIp->text().trimmed(); if (plcIp.isEmpty()) { return false; } config.plcServerIp = plcIp.toStdString(); // 获取PLC服务端端口 bool ok = false; config.plcServerPort = ui->lineEdit_plcServerPort->text().toInt(&ok); if (!ok || config.plcServerPort <= 0 || config.plcServerPort > 65535) { return false; } // 获取PLC寄存器地址配置 config.registerConfig.addrPhotoRequest = ui->lineEdit_addrPhotoRequest->text().toInt(&ok); if (!ok || config.registerConfig.addrPhotoRequest < 0) { return false; } config.registerConfig.addrDataComplete = ui->lineEdit_addrDataComplete->text().toInt(&ok); if (!ok || config.registerConfig.addrDataComplete < 0) { return false; } config.registerConfig.addrCoordDataStart = ui->lineEdit_addrCoordDataStart->text().toInt(&ok); if (!ok || config.registerConfig.addrCoordDataStart < 0) { return false; } // 获取姿态输出顺序 config.poseOutputOrder = ui->comboBox_poseOutputOrder->currentData().toInt(); // 获取方向向量反向配置 config.dirVectorInvert = ui->comboBox_dirVectorInvert->currentData().toInt(); // 获取数据字节序配置 config.byteOrder = ui->comboBox_byteOrder->currentData().toInt(); return true; }