GrabBag/App/WeldSeam/WeldSeamApp/dialogalgoarg.cpp

376 lines
15 KiB
C++

#include "dialogalgoarg.h"
#include "ui_dialogalgoarg.h"
#include "PathManager.h"
#include "StyledMessageBox.h"
#include "HandEyeCalibWidget.h"
#include "ToolExtrinsicWidget.h"
#include "WeldSeamPresenter.h"
#include <QDoubleValidator>
#include <QIntValidator>
#include <QLineEdit>
#include <QMessageBox>
#include <QComboBox>
#include <cmath>
#include <cstring>
namespace {
bool IsIdentityMatrix(const double matrix[16])
{
for (int i = 0; i < 16; ++i) {
const double expected = (i / 4 == i % 4) ? 1.0 : 0.0;
if (std::fabs(matrix[i] - expected) > 1e-9) return false;
}
return true;
}
} // namespace
DialogAlgoArg::DialogAlgoArg(QWidget* parent)
: QDialog(parent)
, ui(new Ui::DialogAlgoArg)
{
ui->setupUi(this);
setWindowTitle(QStringLiteral("焊缝定位 - 算法参数设置"));
ui->comboDetectFlawType->addItem(QStringLiteral("未知/全部"), 0);
ui->comboDetectFlawType->addItem(QStringLiteral("凹陷"), 1);
ui->comboDetectFlawType->addItem(QStringLiteral("凸起"), 2);
ui->comboDetectFlawType->addItem(QStringLiteral("凹陷+凸起"), 3);
InitNumericEditors();
InitCommunicationWidgets();
}
DialogAlgoArg::~DialogAlgoArg()
{
delete ui;
}
void DialogAlgoArg::SetPresenter(WeldSeamPresenter* presenter)
{
m_presenter = presenter;
Load();
}
void DialogAlgoArg::InitNumericEditors()
{
auto setupDouble = [](QLineEdit* edit, double minimum, double maximum) {
auto* validator = new QDoubleValidator(minimum, maximum, 6, edit);
validator->setNotation(QDoubleValidator::StandardNotation);
edit->setValidator(validator);
};
setupDouble(ui->editValleyChkWinWidth, 0.0, 10000.0);
setupDouble(ui->editValleyMaxWidth, 0.0, 10000.0);
setupDouble(ui->editValleyMinHeight, 0.0, 10000.0);
setupDouble(ui->editYDeviationMax, 0.0, 10000.0);
setupDouble(ui->editMaxSkipDistance, 0.0, 100000.0);
setupDouble(ui->editZDeviationMax, 0.0, 10000.0);
setupDouble(ui->editMinLTypeTreeLen, 0.0, 100000.0);
setupDouble(ui->editMinVTypeTreeLen, 0.0, 100000.0);
ui->editMaxLineSkipNum->setValidator(new QIntValidator(0, 100000, ui->editMaxLineSkipNum));
ui->editWeldRefPoints->setValidator(new QIntValidator(0, 1000, ui->editWeldRefPoints));
ui->editTcpPort->setValidator(new QIntValidator(1, 65535, ui->editTcpPort));
}
void DialogAlgoArg::InitCommunicationWidgets()
{
if (!m_toolExtrinsicWidget && ui->verticalLayout_toolExtrinsicHost) {
m_toolExtrinsicWidget = new ToolExtrinsicWidget(this);
m_toolExtrinsicWidget->setTargetOffsetVisible(true);
m_toolExtrinsicWidget->setAxialAngleRangeVisible(true);
ui->verticalLayout_toolExtrinsicHost->addWidget(m_toolExtrinsicWidget);
}
if (!m_handEyeCalibWidget && ui->verticalLayout_handEyeCalibHost) {
m_handEyeCalibWidget = new HandEyeCalibWidget(this);
m_handEyeCalibWidget->setMatrixEditable(true);
m_handEyeCalibWidget->setExtrinsicControlsVisible(false);
m_handEyeCalibWidget->setTargetOffsetVisible(false);
m_handEyeCalibWidget->setApproachOffsetVisible(false);
m_handEyeCalibWidget->setDefaultFilePath(
PathManager::GetInstance().GetAppConfigDirectory());
ui->verticalLayout_handEyeCalibHost->addWidget(m_handEyeCalibWidget);
connect(m_handEyeCalibWidget, &HandEyeCalibWidget::calibMatrixLoaded,
this, &DialogAlgoArg::onCalibMatrixLoaded);
connect(m_handEyeCalibWidget, &HandEyeCalibWidget::saveCalibRequested,
this, &DialogAlgoArg::onSaveCalibRequested);
}
}
void DialogAlgoArg::SetDoubleText(QLineEdit* edit, double value)
{
if (edit) edit->setText(QString::number(value, 'g', 12));
}
void DialogAlgoArg::SetIntText(QLineEdit* edit, int value)
{
if (edit) edit->setText(QString::number(value));
}
bool DialogAlgoArg::TryGetDouble(QLineEdit* edit, const QString& label, double& value)
{
bool ok = false;
value = edit->text().trimmed().toDouble(&ok);
if (ok && edit->hasAcceptableInput()) return true;
StyledMessageBox::warning(this, QStringLiteral("参数错误"),
QStringLiteral("“%1”输入的数值无效或超出范围。").arg(label));
edit->setFocus();
edit->selectAll();
return false;
}
bool DialogAlgoArg::TryGetInt(QLineEdit* edit, const QString& label, int& value)
{
bool ok = false;
value = edit->text().trimmed().toInt(&ok);
if (ok && edit->hasAcceptableInput()) return true;
StyledMessageBox::warning(this, QStringLiteral("参数错误"),
QStringLiteral("“%1”输入的整数无效或超出范围。").arg(label));
edit->setFocus();
edit->selectAll();
return false;
}
void DialogAlgoArg::Load()
{
if (!m_presenter || !m_presenter->GetConfigManager()) return;
const ConfigResult config = m_presenter->GetConfigManager()->GetConfigResult();
const auto& p = config.algorithmParams;
SetDoubleText(ui->editValleyChkWinWidth, p.valleyParam.chkWinWidth);
SetDoubleText(ui->editValleyMaxWidth, p.valleyParam.maxWidth);
SetDoubleText(ui->editValleyMinHeight, p.valleyParam.minHeight);
SetIntText(ui->editMaxLineSkipNum, p.treeGrowParam.maxLineSkipNum);
SetDoubleText(ui->editYDeviationMax, p.treeGrowParam.yDeviationMax);
SetDoubleText(ui->editMaxSkipDistance, p.treeGrowParam.maxSkipDistance);
SetDoubleText(ui->editZDeviationMax, p.treeGrowParam.zDeviationMax);
SetDoubleText(ui->editMinLTypeTreeLen, p.treeGrowParam.minLTypeTreeLen);
SetDoubleText(ui->editMinVTypeTreeLen, p.treeGrowParam.minVTypeTreeLen);
SetIntText(ui->editWeldRefPoints, p.weldSeamParam.weldRefPoints);
const int flawType = static_cast<int>(p.weldSeamParam.detectFlawType);
const int flawTypeIndex = ui->comboDetectFlawType->findData(flawType);
ui->comboDetectFlawType->setCurrentIndex(flawTypeIndex >= 0 ? flawTypeIndex : 0);
SetIntText(ui->editTcpPort, config.tcpPort);
LoadCommunicationConfig();
}
void DialogAlgoArg::LoadCommunicationConfig()
{
if (!m_presenter || !m_presenter->GetConfigManager() ||
!m_toolExtrinsicWidget || !m_handEyeCalibWidget) return;
const ConfigResult config = m_presenter->GetConfigManager()->GetConfigResult();
QVector<ToolExtrinsicCameraInfo> toolCameras;
QVector<HandEyeCalibCameraInfo> handEyeCameras;
if (config.cameraList.empty()) {
toolCameras.append({1, QStringLiteral("相机 1")});
handEyeCameras.append({1, QStringLiteral("相机 1")});
} else {
for (size_t i = 0; i < config.cameraList.size(); ++i) {
const int cameraIndex = static_cast<int>(i + 1);
const QString name = QString::fromStdString(config.cameraList[i].name);
toolCameras.append({cameraIndex, name});
handEyeCameras.append({cameraIndex, name});
}
}
m_toolExtrinsicWidget->setCameraList(toolCameras);
m_handEyeCalibWidget->setCameraList(handEyeCameras);
for (const auto& camera : handEyeCameras) {
const VrHandEyeCalibMatrix* matrix = config.FindHandEyeMatrix(camera.cameraIndex);
VrHandEyeCalibMatrix defaults;
defaults.cameraIndex = camera.cameraIndex;
if (!matrix) matrix = &defaults;
m_toolExtrinsicWidget->setData(
camera.cameraIndex, matrix->eulerOrder,
matrix->rotX, matrix->rotY, matrix->rotZ,
matrix->offsetX, matrix->offsetY, matrix->offsetZ,
matrix->axialAngleMin, matrix->axialAngleMax);
m_handEyeCalibWidget->setCalibData(
camera.cameraIndex, matrix->matrix, !IsIdentityMatrix(matrix->matrix));
m_handEyeCalibWidget->setCalibType(camera.cameraIndex, matrix->calibType);
}
}
bool DialogAlgoArg::Save()
{
if (!m_presenter || !m_presenter->GetConfigManager()) return false;
if (m_presenter->GetScanState() != WeldSeamPresenter::ScanState::Ready) {
StyledMessageBox::warning(this, QStringLiteral("无法保存"),
QStringLiteral("扫描或计算期间不能修改参数。"));
return false;
}
SystemConfig config = m_presenter->GetConfigManager()->GetConfig();
auto& p = config.configResult.algorithmParams;
if (!TryGetDouble(ui->editValleyChkWinWidth, QStringLiteral("检测窗口宽度"),
p.valleyParam.chkWinWidth)) return false;
if (!TryGetDouble(ui->editValleyMaxWidth, QStringLiteral("V 型缺陷最大宽度"),
p.valleyParam.maxWidth)) return false;
if (!TryGetDouble(ui->editValleyMinHeight, QStringLiteral("V 型缺陷最小高度"),
p.valleyParam.minHeight)) return false;
if (!TryGetInt(ui->editMaxLineSkipNum, QStringLiteral("最大跳过扫描线数"),
p.treeGrowParam.maxLineSkipNum)) return false;
if (!TryGetDouble(ui->editYDeviationMax, QStringLiteral("Y 最大偏差"),
p.treeGrowParam.yDeviationMax)) return false;
if (!TryGetDouble(ui->editMaxSkipDistance, QStringLiteral("最大跳过距离"),
p.treeGrowParam.maxSkipDistance)) return false;
if (!TryGetDouble(ui->editZDeviationMax, QStringLiteral("Z 最大偏差"),
p.treeGrowParam.zDeviationMax)) return false;
if (!TryGetDouble(ui->editMinLTypeTreeLen, QStringLiteral("L 型树最小长度"),
p.treeGrowParam.minLTypeTreeLen)) return false;
if (!TryGetDouble(ui->editMinVTypeTreeLen, QStringLiteral("V 型树最小长度"),
p.treeGrowParam.minVTypeTreeLen)) return false;
if (!TryGetInt(ui->editWeldRefPoints, QStringLiteral("焊缝中间参考点数"),
p.weldSeamParam.weldRefPoints)) return false;
const int flawType = ui->comboDetectFlawType->currentData().toInt();
if (flawType < static_cast<int>(keWD_FlawType_Uknown) ||
flawType > static_cast<int>(keSG_FlawType_Dep_Prot)) {
StyledMessageBox::warning(this, QStringLiteral("参数错误"),
QStringLiteral("焊缝类型无效。"));
return false;
}
p.weldSeamParam.detectFlawType = static_cast<EWD_FlawType>(flawType);
int tcpPort = 0;
if (!TryGetInt(ui->editTcpPort, QStringLiteral("TCP 服务端口"), tcpPort)) return false;
config.configResult.tcpPort = static_cast<uint16_t>(tcpPort);
if (!SaveCommunicationConfig(config)) return false;
auto* manager = m_presenter->GetConfigManager();
if (!manager->UpdateFullConfig(config) ||
!manager->SaveConfigToFile(PathManager::GetInstance().GetConfigFilePath().toStdString())) {
StyledMessageBox::warning(this, QStringLiteral("保存失败"),
QStringLiteral("无法写入配置文件。"));
return false;
}
m_presenter->OnConfigChanged(config.configResult);
return true;
}
bool DialogAlgoArg::SaveCommunicationConfig(SystemConfig& config)
{
if (!m_toolExtrinsicWidget || !m_handEyeCalibWidget) return true;
if (!m_toolExtrinsicWidget->hasAcceptableInput()) {
StyledMessageBox::warning(this, QStringLiteral("参数错误"),
QStringLiteral("工具外参输入无效,请检查。"));
return false;
}
ConfigResult& result = config.configResult;
const int cameraCount = result.cameraList.empty()
? 1 : static_cast<int>(result.cameraList.size());
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex) {
VrHandEyeCalibMatrix& matrix = result.EnsureHandEyeMatrix(cameraIndex);
matrix.cameraIndex = cameraIndex;
double values[16];
bool isCalibrated = false;
if (m_handEyeCalibWidget->getCalibData(cameraIndex, values, isCalibrated) &&
isCalibrated) {
std::memcpy(matrix.matrix, values, sizeof(values));
}
int calibType = matrix.calibType;
if (m_handEyeCalibWidget->getCalibType(cameraIndex, calibType)) {
matrix.calibType = calibType;
}
int eulerOrder = matrix.eulerOrder;
double rotX = matrix.rotX;
double rotY = matrix.rotY;
double rotZ = matrix.rotZ;
double offsetX = matrix.offsetX;
double offsetY = matrix.offsetY;
double offsetZ = matrix.offsetZ;
double axialAngleMin = matrix.axialAngleMin;
double axialAngleMax = matrix.axialAngleMax;
if (m_toolExtrinsicWidget->getData(
cameraIndex, eulerOrder,
rotX, rotY, rotZ,
offsetX, offsetY, offsetZ,
axialAngleMin, axialAngleMax)) {
matrix.eulerOrder = eulerOrder;
matrix.rotX = rotX;
matrix.rotY = rotY;
matrix.rotZ = rotZ;
matrix.offsetX = offsetX;
matrix.offsetY = offsetY;
matrix.offsetZ = offsetZ;
matrix.axialAngleMin = axialAngleMin;
matrix.axialAngleMax = axialAngleMax;
}
}
return true;
}
void DialogAlgoArg::ResetDefaults()
{
SetDoubleText(ui->editValleyChkWinWidth, 2.5);
SetDoubleText(ui->editValleyMaxWidth, 20.0);
SetDoubleText(ui->editValleyMinHeight, 1.0);
SetIntText(ui->editMaxLineSkipNum, 50);
SetDoubleText(ui->editYDeviationMax, 1.0);
SetDoubleText(ui->editMaxSkipDistance, 30.0);
SetDoubleText(ui->editZDeviationMax, 5.0);
SetDoubleText(ui->editMinLTypeTreeLen, 50.0);
SetDoubleText(ui->editMinVTypeTreeLen, 50.0);
SetIntText(ui->editWeldRefPoints, 3);
SetIntText(ui->editTcpPort, 7800);
}
void DialogAlgoArg::on_btnOK_clicked()
{
if (Save()) accept();
}
void DialogAlgoArg::on_btnCancel_clicked()
{
reject();
}
void DialogAlgoArg::on_btnApply_clicked()
{
if (Save()) {
StyledMessageBox::information(this, QStringLiteral("提示"),
QStringLiteral("参数已应用。"));
}
}
void DialogAlgoArg::on_btnReset_clicked()
{
const auto result = StyledMessageBox::question(
this, QStringLiteral("确认重置"), QStringLiteral("确定要恢复算法测试默认参数吗?"),
QMessageBox::Yes | QMessageBox::No);
if (result == QMessageBox::Yes) ResetDefaults();
}
void DialogAlgoArg::onCalibMatrixLoaded(int cameraIndex, const double* matrix)
{
Q_UNUSED(cameraIndex);
Q_UNUSED(matrix);
StyledMessageBox::information(this, QStringLiteral("提示"),
QStringLiteral("已从文件导入手眼标定矩阵。"));
}
void DialogAlgoArg::onSaveCalibRequested(int cameraIndex, const double* matrix)
{
Q_UNUSED(cameraIndex);
Q_UNUSED(matrix);
if (Save()) {
StyledMessageBox::information(this, QStringLiteral("成功"),
QStringLiteral("手眼标定参数已保存。"));
}
}