2026-07-11 16:02:57 +08:00

602 lines
18 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 "mainwindow.h"
#include "ui_mainwindow.h"
#include <QApplication>
#include <QBrush>
#include <QDateTime>
#include <QDir>
#include <QFileDialog>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QIcon>
#include <QListWidgetItem>
#include <QMetaObject>
#include <QPixmap>
#include <QPushButton>
#include <QStandardPaths>
#include <QVBoxLayout>
#include <algorithm>
#include <thread>
#include "IVrUtils.h"
#include "PathManager.h"
#include "SingleTargetResultWidget.h"
#include "StyledMessageBox.h"
#include "Version.h"
#include "VrDateUtils.h"
#include "VrLog.h"
namespace {
bool IsExceptionGuideState(int guideStateCode)
{
return guideStateCode >= 10 && guideStateCode <= 24;
}
QString FormatNumber(double value, const QString& unit, int precision = 2)
{
return QStringLiteral("%1%2").arg(value, 0, 'f', precision).arg(unit);
}
} // namespace
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowTitle(QStringLiteral("停车引导"));
if (ui->label) {
ui->label->setText(QStringLiteral("停车引导"));
}
#ifdef _WIN32
setWindowIcon(QIcon(":/common/resource/logo.ico"));
#else
setWindowIcon(QIcon(":/common/resource/logo.png"));
#endif
QFont statusFont = statusBar()->font();
statusFont.setPointSize(12);
statusBar()->setFont(statusFont);
statusBar()->setStyleSheet("QStatusBar { color: rgb(239, 241, 245); padding: 20px; }");
const QString versionWithBuildTime = QString("%1_%2%3%4%5%6%7")
.arg(GetParkingSpaceGuideFullVersion())
.arg(YEAR)
.arg(MONTH, 2, 10, QChar('0'))
.arg(DAY, 2, 10, QChar('0'))
.arg(HOUR, 2, 10, QChar('0'))
.arg(MINUTE, 2, 10, QChar('0'))
.arg(SECOND, 2, 10, QChar('0'));
m_versionLabel = new QLabel(versionWithBuildTime);
m_versionLabel->setStyleSheet("color: rgb(239, 241, 245); font-size: 20px; margin-right: 16px;");
m_versionLabel->setCursor(Qt::PointingHandCursor);
m_versionLabel->installEventFilter(this);
statusBar()->addPermanentWidget(m_versionLabel);
setWindowFlags(Qt::FramelessWindowHint);
showMaximized();
ui->label_work->setVisible(false);
ui->detect_image->setScene(new QGraphicsScene(this));
m_logHelper = new DetectLogHelper(ui->detect_log, this);
qRegisterMetaType<DetectionResult>("DetectionResult");
qRegisterMetaType<WorkStatus>("WorkStatus");
qRegisterMetaType<ParkingSpaceGuidePosition>("ParkingSpaceGuidePosition");
connect(this, &MainWindow::workStatusUpdateRequested,
this, &MainWindow::updateWorkStatusLabel);
connect(this, &MainWindow::detectionResultUpdateRequested,
this, &MainWindow::updateDetectionResultDisplay);
setupContextMenu();
updateStatusLog(tr("设备初始化开始"));
Init();
}
MainWindow::~MainWindow()
{
if (m_presenter) {
m_presenter->SetStatusCallback(static_cast<IYParkingSpaceGuideStatus*>(nullptr));
m_presenter->DeinitApp();
delete m_presenter;
m_presenter = nullptr;
}
delete ui;
}
bool MainWindow::eventFilter(QObject* obj, QEvent* event)
{
if (obj == m_versionLabel && event->type() == QEvent::MouseButtonPress) {
const QString algoVersion = m_presenter ? m_presenter->GetAlgoVersion() : QString();
AboutDialog dlg(PARKINGSPACEGUIDE_APP_NAME, m_versionLabel->text(), algoVersion, this);
dlg.exec();
return true;
}
return QMainWindow::eventFilter(obj, event);
}
void MainWindow::updateStatusLog(const QString& message)
{
if (m_logHelper) {
m_logHelper->appendLog(message);
}
}
void MainWindow::clearDetectionLog()
{
if (m_logHelper) {
m_logHelper->clearLog();
}
}
void MainWindow::Init()
{
m_presenter = new ParkingSpaceGuidePresenter();
m_presenter->SetStatusCallback<IYParkingSpaceGuideStatus>(this);
m_deviceStatusWidget = new DeviceStatusWidget(ui->frame_dev);
m_deviceStatusWidget->setCameraCount(2, true);
m_deviceStatusWidget->setCamera1Name(tr("平面相机"));
m_deviceStatusWidget->setCamera2Name(tr("雷达"));
m_deviceStatusWidget->setRobotVisible(false);
connect(m_deviceStatusWidget, SIGNAL(cameraClicked(int)), this, SLOT(onCameraClicked(int)));
auto* frameDevLayout = new QVBoxLayout(ui->frame_dev);
frameDevLayout->setContentsMargins(0, 0, 0, 0);
frameDevLayout->addWidget(m_deviceStatusWidget);
SingleTargetResultWidget::setupListWidget(ui->detect_result_list, QSize(520, 182));
setButtonsEnabled(false);
std::thread initThread([this]() {
updateStatusLog(tr("系统初始化中..."));
const int result = m_presenter->Init();
if (result != 0) {
updateStatusLog(tr("初始化失败,错误码:%1").arg(result));
} else {
updateStatusLog(tr("初始化完成"));
}
});
initThread.detach();
}
void MainWindow::displayImage(const QImage& image)
{
if (image.isNull()) {
updateStatusLog(tr("无预览图像"));
return;
}
QGraphicsScene* scene = ui->detect_image->scene();
scene->clear();
scene->addPixmap(QPixmap::fromImage(image));
ui->detect_image->fitInView(scene->sceneRect(), Qt::KeepAspectRatio);
}
void MainWindow::addDetectionResult(const DetectionResult& result)
{
ui->detect_result_list->clear();
const int itemWidth = (std::max)(360, ui->detect_result_list->viewport()->width() - 8);
const int itemHeight = 182;
SingleTargetResultWidget::setupListWidget(ui->detect_result_list, QSize(itemWidth, itemHeight));
auto* resultWidget = new SingleTargetResultWidget();
resultWidget->setAutoFillBackground(true);
auto* item = new QListWidgetItem();
item->setBackground(QBrush(Qt::transparent));
item->setSizeHint(QSize(itemWidth, itemHeight));
item->setFlags(item->flags() & ~Qt::ItemIsDragEnabled & ~Qt::ItemIsDropEnabled);
ui->detect_result_list->addItem(item);
ui->detect_result_list->setItemWidget(item, resultWidget);
if (result.parkingSpaceInfoList.empty()) {
resultWidget->setTitle(tr("目标结果"));
resultWidget->setStatus(tr("无结果"), true);
resultWidget->setEmptyText(tr("暂无停机状态结果"));
return;
}
const ParkingSpaceGuideInfo& info = result.parkingSpaceInfoList.front();
const bool hasException = result.errorCode != 0 || info.hasException || IsExceptionGuideState(info.guideStateCode);
resultWidget->setTitle(tr("当前目标"));
resultWidget->setStatus(hasException ? tr("异常") : tr("正常"), hasException);
resultWidget->setFields({
{tr("目标编号"), info.targetId},
{tr("停机位"), info.parkId},
{tr("机型"), info.modelType},
{tr("引导状态"), QStringLiteral("%1 %2").arg(info.guideStateCode).arg(info.guideText)},
{tr("到停止线"), FormatNumber(info.distance, tr("毫米"))},
{tr("横向偏移"), FormatNumber(info.lateralOffset, tr("毫米"))},
{tr("偏转角"), FormatNumber(info.angle, tr(""))},
{tr("速度"), FormatNumber(info.aircraftSpeed, tr("米/秒"), 2)}
});
updateStatusLog(tr("引导结果:目标编号=%1停机位=%2机型=%3距离=%4毫米横向偏移=%5毫米角度=%6度速度=%7米/秒")
.arg(info.targetId)
.arg(info.parkId)
.arg(info.modelType)
.arg(info.distance, 0, 'f', 2)
.arg(info.lateralOffset, 0, 'f', 2)
.arg(info.angle, 0, 'f', 2)
.arg(info.aircraftSpeed, 0, 'f', 2));
}
void MainWindow::OnStatusUpdate(const std::string& statusMessage)
{
updateStatusLog(QString::fromStdString(statusMessage));
}
void MainWindow::OnDetectionResult(const DetectionResult& result)
{
emit detectionResultUpdateRequested(result);
}
void MainWindow::OnCamera1StatusChanged(bool isConnected)
{
if (m_deviceStatusWidget) {
m_deviceStatusWidget->updateCamera1Status(isConnected);
}
}
void MainWindow::OnCamera2StatusChanged(bool isConnected)
{
if (m_deviceStatusWidget) {
m_deviceStatusWidget->updateCamera2Status(isConnected);
}
}
void MainWindow::OnRobotConnectionChanged(bool isConnected)
{
Q_UNUSED(isConnected);
}
void MainWindow::OnSerialConnectionChanged(bool isConnected)
{
if (m_deviceStatusWidget) {
m_deviceStatusWidget->updateSerialStatus(isConnected);
}
}
void MainWindow::OnCameraCountChanged(int cameraCount)
{
Q_UNUSED(cameraCount);
if (m_deviceStatusWidget) {
m_deviceStatusWidget->setCameraCount(2, true);
m_deviceStatusWidget->setCamera1Name(tr("平面相机"));
m_deviceStatusWidget->setCamera2Name(tr("雷达"));
}
}
void MainWindow::OnWorkStatusChanged(WorkStatus status)
{
emit workStatusUpdateRequested(status);
}
void MainWindow::updateWorkStatusLabel(WorkStatus status)
{
if (status == WorkStatus::Working || status == WorkStatus::Detecting) {
clearDetectionLog();
}
if (!ui->label_work) {
return;
}
const QString statusText = QString::fromStdString(WorkStatusToString(status));
ui->label_work->setText(statusText);
updateStatusLog(tr("工作状态:%1").arg(statusText));
switch (status) {
case WorkStatus::Ready:
ui->label_work->setStyleSheet("color: green;");
setButtonsEnabled(true);
break;
case WorkStatus::InitIng:
ui->label_work->setStyleSheet("color: blue;");
setButtonsEnabled(false);
break;
case WorkStatus::Working:
case WorkStatus::Detecting:
ui->label_work->setStyleSheet("color: blue;");
setButtonsEnabled(false);
break;
case WorkStatus::Completed:
ui->label_work->setStyleSheet("color: green; font-weight: bold;");
setButtonsEnabled(true);
break;
case WorkStatus::Error:
ui->label_work->setStyleSheet("color: red; font-weight: bold;");
setButtonsEnabled(true);
break;
default:
ui->label_work->setStyleSheet("");
setButtonsEnabled(false);
break;
}
}
void MainWindow::updateDetectionResultDisplay(const DetectionResult& result)
{
updateStatusLog(tr("检测结果已更新"));
displayImage(result.image);
addDetectionResult(result);
}
void MainWindow::on_btn_start_clicked()
{
if (!m_presenter) {
updateStatusLog(tr("系统未初始化"));
return;
}
clearDetectionLog();
updateStatusLog(tr("开始停车引导检测"));
if (!m_presenter->TriggerDetection(-1)) {
updateStatusLog(tr("启动检测失败"));
}
}
void MainWindow::on_btn_stop_clicked()
{
if (m_presenter) {
m_presenter->StopDetection();
}
}
void MainWindow::showParameterDialog(DialogAlgoArg::ConfigPage page, QPushButton* button)
{
if (m_selectedButton && m_selectedButton != button) {
updateStatusLog(tr("请先关闭当前参数窗口"));
return;
}
if (!m_presenter) {
updateStatusLog(tr("系统未初始化"));
return;
}
setButtonSelectedState(button, true);
if (!ui_dialogAlgoArg) {
ui_dialogAlgoArg = new DialogAlgoArg(this);
connect(ui_dialogAlgoArg, &QDialog::finished, this, [this]() {
if (m_selectedButton) {
setButtonSelectedState(m_selectedButton, false);
}
});
}
ui_dialogAlgoArg->SetPresenter(m_presenter);
ui_dialogAlgoArg->SetCurrentPage(page);
ui_dialogAlgoArg->show();
ui_dialogAlgoArg->raise();
ui_dialogAlgoArg->activateWindow();
}
void MainWindow::on_btn_camera_clicked()
{
updateStatusLog(tr("打开相机参数设置"));
showParameterDialog(DialogAlgoArg::ConfigPage::Camera, ui->btn_camera);
}
void MainWindow::on_btn_algo_config_clicked()
{
updateStatusLog(tr("打开检测参数设置"));
showParameterDialog(DialogAlgoArg::ConfigPage::Detection, ui->btn_algo_config);
}
void MainWindow::on_btn_camera_levelling_clicked()
{
updateStatusLog(tr("相机调平功能预留"));
}
void MainWindow::on_btn_hide_clicked()
{
showMinimized();
}
void MainWindow::on_btn_close_clicked()
{
close();
}
void MainWindow::on_btn_test_clicked()
{
if (!m_presenter) {
StyledMessageBox::warning(this, tr("错误"), tr("系统未初始化"));
return;
}
if (m_selectedButton && m_selectedButton != ui->btn_test) {
updateStatusLog(tr("请先关闭当前参数窗口"));
return;
}
setButtonSelectedState(ui->btn_test, true);
clearDetectionLog();
updateStatusLog(tr("运行预留算法接口测试"));
std::thread t([this]() {
const bool ok = m_presenter->TriggerDetection(-1);
updateStatusLog(ok ? tr("测试检测完成") : tr("测试检测失败"));
QMetaObject::invokeMethod(this, [this]() {
setButtonSelectedState(ui->btn_test, false);
}, Qt::QueuedConnection);
});
t.detach();
}
void MainWindow::setButtonsEnabled(bool enabled)
{
if (ui->btn_start) ui->btn_start->setEnabled(enabled);
if (ui->btn_stop) ui->btn_stop->setEnabled(enabled);
if (ui->btn_camera) ui->btn_camera->setEnabled(enabled);
if (ui->btn_algo_config) ui->btn_algo_config->setEnabled(enabled);
if (ui->btn_camera_levelling) ui->btn_camera_levelling->setEnabled(enabled);
if (ui->btn_test) ui->btn_test->setEnabled(enabled);
}
void MainWindow::setButtonImage(QPushButton* button, const QString& imagePath)
{
if (button) {
button->setStyleSheet(QString("image: url(%1);background-color: rgb(38, 40, 47);border: none;")
.arg(imagePath));
}
}
void MainWindow::setButtonSelectedState(QPushButton* button, bool selected)
{
if (!button) {
return;
}
QString normalImage;
QString selectedImage;
if (button == ui->btn_camera) {
normalImage = ":/common/resource/config_camera.png";
selectedImage = ":/common/resource/config_camera_s.png";
} else if (button == ui->btn_algo_config) {
normalImage = ":/common/resource/config_algo.png";
selectedImage = ":/common/resource/config_algo_s.png";
} else if (button == ui->btn_camera_levelling) {
normalImage = ":/common/resource/config_camera_level.png";
selectedImage = ":/common/resource/config_camera_level_s.png";
} else if (button == ui->btn_test) {
normalImage = ":/common/resource/config_data_test.png";
selectedImage = ":/common/resource/config_data_test_s.png";
}
if (selected) {
setButtonImage(button, selectedImage);
m_selectedButton = button;
setOtherButtonsEnabled(button, false);
} else {
setButtonImage(button, normalImage);
if (m_selectedButton == button) {
m_selectedButton = nullptr;
setOtherButtonsEnabled(nullptr, true);
}
}
}
void MainWindow::restoreAllButtonStates()
{
setButtonSelectedState(ui->btn_camera, false);
setButtonSelectedState(ui->btn_algo_config, false);
setButtonSelectedState(ui->btn_camera_levelling, false);
setButtonSelectedState(ui->btn_test, false);
}
void MainWindow::setOtherButtonsEnabled(QPushButton* exceptButton, bool enabled)
{
const QList<QPushButton*> configButtons = {
ui->btn_camera,
ui->btn_algo_config,
ui->btn_camera_levelling,
ui->btn_test
};
for (QPushButton* button : configButtons) {
if (button && button != exceptButton) {
button->setEnabled(enabled);
}
}
}
void MainWindow::onCameraClicked(int cameraIndex)
{
if (m_presenter) {
m_presenter->SetDefaultCameraIndex(cameraIndex);
}
}
void MainWindow::setupContextMenu()
{
m_contextMenu = new QMenu(this);
m_saveDataAction = new QAction(tr("保存检测数据"), this);
m_contextMenu->addAction(m_saveDataAction);
connect(m_saveDataAction, &QAction::triggered, this, &MainWindow::onSaveDetectionData);
ui->detect_image->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->detect_image, &QGraphicsView::customContextMenuRequested,
this, &MainWindow::showContextMenu);
}
void MainWindow::showContextMenu(const QPoint& pos)
{
if (!m_presenter) {
updateStatusLog(tr("系统未初始化,无法保存数据"));
return;
}
if (m_presenter->GetDetectionDataCacheSize() == 0) {
updateStatusLog(tr("没有可保存的检测数据"));
return;
}
m_contextMenu->exec(ui->detect_image->mapToGlobal(pos));
}
void MainWindow::onSaveDetectionData()
{
if (!m_presenter) {
updateStatusLog(tr("系统未初始化,无法保存数据"));
return;
}
const QString dirPath = QFileDialog::getExistingDirectory(
this,
tr("选择保存目录"),
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if (dirPath.isEmpty()) {
updateStatusLog(tr("保存已取消"));
return;
}
const std::string timeStamp = CVrDateUtils::GetNowTime();
const QString fileName = QString("停车引导_%1_%2.txt")
.arg(m_presenter->GetDetectIndex())
.arg(QString::fromStdString(timeStamp));
const QString fullPath = QDir(dirPath).filePath(fileName);
if (saveDetectionDataToFile(fullPath)) {
updateStatusLog(tr("检测数据已保存:%1").arg(fileName));
} else {
updateStatusLog(tr("保存检测数据失败"));
}
}
bool MainWindow::saveDetectionDataToFile(const QString& filePath)
{
if (!m_presenter) {
return false;
}
try {
const int result = m_presenter->SaveDetectionDataToFile(filePath.toStdString());
if (result != 0) {
updateStatusLog(tr("保存数据失败,错误码:%1").arg(result));
return false;
}
return true;
} catch (const std::exception&) {
updateStatusLog(tr("保存数据异常"));
return false;
}
}