887 lines
32 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 "workpiecesizeactions.h"
#include <QPushButton>
#include <QDoubleValidator>
#include <QGroupBox>
#include <QtMath>
#include <QTableWidget>
#include <QHeaderView>
#include <QColor>
#include <QComboBox>
#include <QPersistentModelIndex>
#include <algorithm>
#include <cmath>
#include "HandEyeCalibWidget.h"
#include "PathManager.h"
#include "StyledMessageBox.h"
#include "ToolExtrinsicWidget.h"
#include "NetworkConfigWidget.h"
#include "PointCloudFilterWidget.h"
#include "workpiecesizeactions.h"
#include "VrLog.h"
namespace
{
const int kHandEyeConfigIndex = 1;
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;
}
}
DialogAlgoarg::DialogAlgoarg(ConfigManager* configManager, QWidget *parent)
: QDialog(parent)
, ui(new Ui::DialogAlgoarg)
, m_pConfigManager(configManager)
, m_handEyeCalibWidget(nullptr)
, m_toolExtrinsicWidget(nullptr)
{
try {
ui->setupUi(this);
m_pointCloudFilterWidget = ui->pointCloudFilterWidget;
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));
InitHandEyeCalibTab();
InitNetworkConfigTab();
InitToolExtrinsicTab();
initWorkpieceSizesTab();
initModelParamsTab();
// 隐藏标题栏
// 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);
// 检查配置文件路径是否有效
if (m_configFilePath.isEmpty()) {
LOG_WARNING("Config file path is empty\n");
}
const VrToolParam& toolParam = configData.algorithmParams.toolParam;
LoadModelDetectionParamsToUI(configData.algorithmParams);
LoadBinDetectionParamsToUI(configData.algorithmParams.binParam);
LoadRotorCoreParamsToUI(configData.algorithmParams.rotorCoreParam);
if (m_toolExtrinsicWidget) {
m_toolExtrinsicWidget->setData(
toolParam.eulerOrder,
toolParam.rotX, toolParam.rotY, toolParam.rotZ,
toolParam.offsetX, toolParam.offsetY, toolParam.offsetZ);
}
// 加载网络配置(JSON TCP 端口 + Modbus 寄存器地址)
LoadPlcRobotServerConfigToUI(configData.plcRobotServerConfig);
LoadHandEyeCalibConfig();
} 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);
LoadBinDetectionParamsToUI(configData.algorithmParams.binParam);
LoadRotorCoreParamsToUI(configData.algorithmParams.rotorCoreParam);
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);
LoadBinDetectionParamsToUI(configData.algorithmParams.binParam);
LoadRotorCoreParamsToUI(configData.algorithmParams.rotorCoreParam);
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)
{
return;
}
m_toolExtrinsicWidget = ui->toolExtrinsicWidget;
m_toolExtrinsicWidget->setCameraSelectorVisible(false);
m_toolExtrinsicWidget->setTargetOffsetVisible(true);
m_toolExtrinsicWidget->setAxialAngleRangeVisible(false);
}
void DialogAlgoarg::InitHandEyeCalibTab()
{
if (!ui || m_handEyeCalibWidget)
{
return;
}
m_handEyeCalibWidget = ui->handEyeCalibWidget;
m_handEyeCalibWidget->setMatrixEditable(true);
m_handEyeCalibWidget->setDefaultCalibType(0);
m_handEyeCalibWidget->setDefaultFilePath(
PathManager::GetInstance().GetAppConfigDirectory());
QVector<HandEyeCalibCameraInfo> cameraList;
HandEyeCalibCameraInfo cameraInfo;
cameraInfo.cameraIndex = kHandEyeConfigIndex;
cameraInfo.displayName = QStringLiteral("手眼标定");
cameraList.append(cameraInfo);
m_handEyeCalibWidget->setCameraList(cameraList);
m_handEyeCalibWidget->setCalibType(kHandEyeConfigIndex, 0);
connect(m_handEyeCalibWidget, &HandEyeCalibWidget::calibMatrixLoaded,
this, &DialogAlgoarg::onCalibMatrixLoaded);
connect(m_handEyeCalibWidget, &HandEyeCalibWidget::saveCalibRequested,
this, &DialogAlgoarg::onSaveCalibRequested);
}
void DialogAlgoarg::InitNetworkConfigTab()
{
if (!ui || m_networkConfigWidget)
{
return;
}
m_networkConfigWidget = ui->networkConfigWidget;
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;
}
bool DialogAlgoarg::SaveConfigFromUI()
{
if (!m_pConfigManager) {
return false;
}
try {
// 获取当前配置
SystemConfig systemConfig = m_pConfigManager->GetConfig();
if (!savePointCloudFilterConfig(systemConfig.configResult))
{
return false;
}
VrToolParam& toolParam = systemConfig.configResult.algorithmParams.toolParam;
if (!SaveModelDetectionParamsFromUI(systemConfig.configResult.algorithmParams)) {
StyledMessageBox::warning(this, "错误", "模型检测参数输入有误,请检查!");
return false;
}
if (!SaveBinDetectionParamsFromUI(systemConfig.configResult.algorithmParams.binParam)) {
StyledMessageBox::warning(this, "错误", "料框检测参数输入有误,请检查!");
return false;
}
if (!SaveRotorCoreParamsFromUI(systemConfig.configResult.algorithmParams.rotorCoreParam)) {
StyledMessageBox::warning(this, "错误", "转子钢芯算法参数输入有误,请检查!");
return false;
}
if (!m_toolExtrinsicWidget || !m_toolExtrinsicWidget->hasAcceptableInput()) {
StyledMessageBox::warning(this, "错误", "工具参数输入有误,请检查!");
return false;
}
m_toolExtrinsicWidget->getData(
toolParam.eulerOrder,
toolParam.rotX, toolParam.rotY, toolParam.rotZ,
toolParam.offsetX, toolParam.offsetY, toolParam.offsetZ);
// 保存网络配置(JSON TCP 端口 + Modbus 寄存器地址)
if (!SavePlcRobotServerConfigFromUI(systemConfig.configResult.plcRobotServerConfig)) {
StyledMessageBox::warning(this, "错误", "网络配置输入有误,请检查!");
return false;
}
// 保存手眼标定矩阵到配置
if (!SaveCalibMatrixToConfig(systemConfig.configResult.handEyeCalibMatrix)) {
StyledMessageBox::warning(this, "错误", "手眼标定矩阵输入有误,请检查!");
return false;
}
// 更新并保存配置到文件
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)
{
if (!ui || !ui->tableWidget_modelParams) {
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();
}
void DialogAlgoarg::LoadBinDetectionParamsToUI(const VrBinParam& binParam)
{
Q_UNUSED(binParam);
}
bool DialogAlgoarg::SaveBinDetectionParamsFromUI(VrBinParam& binParam)
{
Q_UNUSED(binParam);
return true;
}
void DialogAlgoarg::LoadRotorCoreParamsToUI(const VrRotorCoreParam& rotorParam)
{
loadWorkpieceSizes(rotorParam);
if (!ui || !ui->lineEdit_spacerLength || !ui->lineEdit_spacerWidth ||
!ui->lineEdit_layerRatio || !ui->lineEdit_distToSideRatio ||
!ui->lineEdit_groundRemoveOffset) {
return;
}
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);
}
bool DialogAlgoarg::SaveRotorCoreParamsFromUI(VrRotorCoreParam& rotorParam)
{
if (!saveWorkpieceSizes(rotorParam))
{
return false;
}
if (!ui || !ui->lineEdit_spacerLength || !ui->lineEdit_spacerWidth ||
!ui->lineEdit_layerRatio || !ui->lineEdit_distToSideRatio ||
!ui->lineEdit_groundRemoveOffset) {
return false;
}
bool ok = false;
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;
return true;
}
void DialogAlgoarg::initWorkpieceSizesTab()
{
// 页面和编辑触发方式定义在 .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;
}
ui->tableWidget_workpieceSizes->removeRow(row);
refreshWorkpieceRows(defaultRow);
ui->tableWidget_workpieceSizes->selectRow(std::min(row, ui->tableWidget_workpieceSizes->rowCount() - 1));
}
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)));
}
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());
}
});
}
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();
}
void DialogAlgoarg::updateWorkpieceActions()
{
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())
{
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);
}
void DialogAlgoarg::loadWorkpieceSizes(const VrRotorCoreParam& rotorParam)
{
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, "工件尺寸", "尺寸列表存在无效参数,请检查尺寸和默认工件后保存。");
}
}
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。");
return false;
}
// 同步旧字段便于配置兼容;检测始终按列表选择。
updated.radius = defaultSize.radius;
updated.height = defaultSize.height;
rotorParam = updated;
return true;
}
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;
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();
}
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::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::LoadHandEyeCalibConfig()
{
if (!m_pConfigManager || !m_handEyeCalibWidget)
{
return;
}
const ConfigResult configData = m_pConfigManager->GetConfigResult();
const VrHandEyeCalibMatrix& calibMatrix = configData.handEyeCalibMatrix;
m_handEyeCalibWidget->setCalibData(
kHandEyeConfigIndex,
calibMatrix.matrix,
!isIdentityMatrix(calibMatrix.matrix));
}
bool DialogAlgoarg::SaveCalibMatrixToConfig(VrHandEyeCalibMatrix& calibMatrix)
{
if (!m_handEyeCalibWidget)
{
return false;
}
bool isCalibrated = false;
return m_handEyeCalibWidget->getCalibData(
kHandEyeConfigIndex, calibMatrix.matrix, isCalibrated);
}
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;
}