1062 lines
33 KiB
C++

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QApplication>
#include <QBrush>
#include <QCloseEvent>
#include <QComboBox>
#include <QDateTime>
#include <QDir>
#include <QFileDialog>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QGraphicsView>
#include <QHBoxLayout>
#include <QIcon>
#include <QLabel>
#include <QListWidgetItem>
#include <QMetaObject>
#include <QPixmap>
#include <QPushButton>
#include <QResizeEvent>
#include <QStandardPaths>
#include <QThread>
#include <QTimer>
#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 {
QString GuideStateName(ParkingGuideState state)
{
switch (state) {
case ParkingGuideState::DockingStarted:
return QStringLiteral("泊位引导启动");
case ParkingGuideState::Capturing:
return QStringLiteral("捕获");
case ParkingGuideState::Tracking:
return QStringLiteral("跟踪");
case ParkingGuideState::ApproachRate:
return QStringLiteral("接近率");
case ParkingGuideState::CenterLineAligned:
return QStringLiteral("对准中心线");
case ParkingGuideState::Slow:
return QStringLiteral("慢速");
case ParkingGuideState::AzimuthGuidance:
return QStringLiteral("方位引导");
case ParkingGuideState::StopPositionReached:
return QStringLiteral("到达停止位置");
case ParkingGuideState::DockingCompleted:
return QStringLiteral("泊位引导完成");
case ParkingGuideState::Overshot:
return QStringLiteral("越位");
case ParkingGuideState::StoppedShort:
return QStringLiteral("提前停止");
case ParkingGuideState::Waiting:
return QStringLiteral("等待");
case ParkingGuideState::SlowBadWeather:
return QStringLiteral("异常慢速-恶劣天气");
case ParkingGuideState::SlowAircraftLost:
return QStringLiteral("异常慢速-飞机丢失");
case ParkingGuideState::AircraftVerificationFailed:
return QStringLiteral("飞机验证失败");
case ParkingGuideState::GateBlocked:
return QStringLiteral("机位受阻");
case ParkingGuideState::ViewBlocked:
return QStringLiteral("视野受阻");
case ParkingGuideState::SbuStop:
return QStringLiteral("SBU停止");
case ParkingGuideState::TooFast:
return QStringLiteral("速度过快");
case ParkingGuideState::EmergencyStop:
return QStringLiteral("紧急停止");
case ParkingGuideState::ChocksOn:
return QStringLiteral("挡轮挡");
case ParkingGuideState::SystemError:
return QStringLiteral("系统错误");
case ParkingGuideState::SystemFailure:
return QStringLiteral("系统故障");
case ParkingGuideState::PowerFailure:
return QStringLiteral("电源故障");
case ParkingGuideState::Unknown:
default:
return QStringLiteral("未知");
}
}
QString FormatGuideState(const ParkingSpaceGuideInfo& info)
{
const ParkingGuideState state = ParkingGuideStateFromCode(info.guideStateCode);
const int displayCode = state == ParkingGuideState::Unknown
? info.guideStateCode
: static_cast<int>(state);
QString text = QStringLiteral("%1 %2").arg(displayCode).arg(GuideStateName(state));
const QString guideText = info.guideText.trimmed();
if (!guideText.isEmpty()) {
text += QStringLiteral(" / %1").arg(guideText);
}
return text;
}
QString FormatNumber(double value, const QString& unit, int precision = 2)
{
return QStringLiteral("%1%2").arg(value, 0, 'f', precision).arg(unit);
}
bool IsKnownAircraftModel(const QString& modelType)
{
const QString normalized = modelType.trimmed().toUpper();
return normalized == QStringLiteral("A320") ||
normalized == QStringLiteral("B737");
}
QString FormatModelVerification(const ParkingSpaceGuideInfo& info)
{
const QString expected = info.expectedModelType.trimmed().toUpper();
const QString recognized = info.modelType.trimmed().toUpper();
if (!IsKnownAircraftModel(recognized)) {
return QStringLiteral("未知");
}
if (!IsKnownAircraftModel(expected)) {
return QStringLiteral("实测(%1)").arg(recognized);
}
if (info.modelMatched) {
return QStringLiteral("匹配(%1)").arg(recognized);
}
if (recognized.compare(expected, Qt::CaseInsensitive) != 0) {
return QStringLiteral("不匹配(%1)").arg(recognized);
}
return QStringLiteral("未知");
}
void ConfigureResultWidget(SingleTargetResultWidget* resultWidget)
{
if (!resultWidget) {
return;
}
// Keep this presentation local to ParkingSpaceGuide; UICommon defaults are shared by other apps.
auto* mainLayout = qobject_cast<QVBoxLayout*>(resultWidget->layout());
if (mainLayout && mainLayout->count() > 0) {
auto* headerLayout = qobject_cast<QHBoxLayout*>(mainLayout->itemAt(0)->layout());
if (headerLayout && headerLayout->count() == 2) {
headerLayout->setSpacing(12);
headerLayout->setStretch(0, 0);
headerLayout->setStretch(1, 0);
headerLayout->addStretch(1);
}
}
resultWidget->setStyleSheet(
resultWidget->styleSheet()
+ QStringLiteral(
"#singleTargetTitle { font-size: 28px; }"
"#singleTargetStatus { font-size: 34px; font-weight: 700; padding: 0; }"
"#singleTargetEmpty { font-size: 28px; }"
"#singleTargetFieldName { font-size: 24px; }"
"#singleTargetFieldValue { font-size: 26px; }"));
auto* statusLabel = resultWidget->findChild<QLabel*>(QStringLiteral("singleTargetStatus"));
if (statusLabel) {
statusLabel->setStyleSheet(QStringLiteral("background-color: transparent;"));
}
}
} // namespace
MainWindow::MainWindow(QWidget* parent)
: MainWindow(ParkingSpaceGuideSimulationOptions(), parent)
{
}
MainWindow::MainWindow(const ParkingSpaceGuideSimulationOptions& simulationOptions,
QWidget* parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, m_simulationOptions(simulationOptions)
{
ui->setupUi(this);
setWindowTitle(m_simulationOptions.enabled
? QStringLiteral("停机引导(仿真)")
: 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,
Qt::QueuedConnection);
setupContextMenu();
updateStatusLog(tr("设备初始化开始"));
Init();
}
MainWindow::~MainWindow()
{
if (m_presenter) {
m_presenter->RequestShutdown();
}
if (m_initThread.joinable()) {
m_initThread.join();
}
if (m_testThread.joinable()) {
m_testThread.join();
}
if (m_presenter) {
m_presenter->DeinitApp();
m_presenter->SetStatusCallback(static_cast<IYParkingSpaceGuideStatus*>(nullptr));
delete m_presenter;
m_presenter = nullptr;
}
delete ui;
}
void MainWindow::closeEvent(QCloseEvent* event)
{
if (m_shutdownComplete) {
event->accept();
return;
}
event->ignore();
if (m_closing.exchange(true)) {
return;
}
QApplication::setQuitOnLastWindowClosed(false);
setEnabled(false);
hide();
if (m_presenter) {
m_presenter->RequestShutdown();
}
QTimer::singleShot(0, this, &MainWindow::continueShutdown);
}
void MainWindow::continueShutdown()
{
if (m_initRunning.load() || m_testRunning.load()) {
QTimer::singleShot(20, this, &MainWindow::continueShutdown);
return;
}
if (m_initThread.joinable()) {
m_initThread.join();
}
if (m_testThread.joinable()) {
m_testThread.join();
}
if (m_presenter) {
m_presenter->DeinitApp();
m_presenter->SetStatusCallback(static_cast<IYParkingSpaceGuideStatus*>(nullptr));
}
m_shutdownComplete = true;
QApplication::quit();
}
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 (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, [this, message]() {
updateStatusLog(message);
}, Qt::QueuedConnection);
return;
}
if (m_logHelper) {
m_logHelper->appendLog(message);
}
}
void MainWindow::clearDetectionLog()
{
if (m_logHelper) {
m_logHelper->clearLog();
}
}
void MainWindow::Init()
{
m_presenter = new ParkingSpaceGuidePresenter(m_simulationOptions);
m_presenter->SetStatusCallback<IYParkingSpaceGuideStatus>(this);
connect(m_presenter,
&ParkingSpaceGuidePresenter::SelectedModelChanged,
this,
[this](const QString& modelType) {
const int index = ui->combo_aircraft_model->findText(
modelType, Qt::MatchFixedString);
if (index >= 0 &&
ui->combo_aircraft_model->currentIndex() != index) {
ui->combo_aircraft_model->setCurrentIndex(index);
}
},
Qt::QueuedConnection);
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, 420));
ensureResultWidget();
setButtonsEnabled(false);
m_initRunning.store(true);
m_initThread = std::thread([this]() {
updateStatusLog(tr("系统初始化中..."));
const int result = m_presenter->Init();
if (m_closing.load()) {
m_initRunning.store(false);
return;
}
if (result != 0) {
updateStatusLog(tr("初始化失败,错误码:%1").arg(result));
} else {
updateStatusLog(tr("初始化完成"));
if (m_simulationOptions.enabled) {
updateStatusLog(tr("仿真数据已全部加载到内存,请选择机型并点击开始进行回放"));
} else if (!m_presenter->StartAutomaticDetection(-1)) {
updateStatusLog(tr("自动实时检测启动失败,请检查雷达和安全状态"));
}
}
m_initRunning.store(false);
});
}
void MainWindow::displayImage(const QImage& image)
{
if (image.isNull()) {
updateStatusLog(tr("无预览图像"));
return;
}
if (m_imagePixmapItem && m_lastImageCacheKey == image.cacheKey()) {
return;
}
QGraphicsScene* scene = ui->detect_image->scene();
const QPixmap pixmap = QPixmap::fromImage(image);
if (!m_imagePixmapItem) {
m_imagePixmapItem = scene->addPixmap(pixmap);
m_imagePixmapItem->setTransformationMode(Qt::SmoothTransformation);
} else {
m_imagePixmapItem->setPixmap(pixmap);
}
const bool sizeChanged = m_lastImageSize != image.size();
m_lastImageSize = image.size();
m_lastImageCacheKey = image.cacheKey();
if (sizeChanged) {
scene->setSceneRect(m_imagePixmapItem->boundingRect());
ui->detect_image->fitInView(m_imagePixmapItem, Qt::KeepAspectRatio);
}
}
void MainWindow::ensureResultWidget()
{
if (m_resultWidget && m_resultItem) {
return;
}
m_resultWidget = new SingleTargetResultWidget();
m_resultWidget->setAutoFillBackground(true);
m_resultItem = new QListWidgetItem();
m_resultItem->setBackground(QBrush(Qt::transparent));
m_resultItem->setFlags(m_resultItem->flags() &
~Qt::ItemIsDragEnabled &
~Qt::ItemIsDropEnabled);
ui->detect_result_list->addItem(m_resultItem);
ui->detect_result_list->setItemWidget(m_resultItem, m_resultWidget);
ConfigureResultWidget(m_resultWidget);
updateResultItemSize();
addDetectionResult(DetectionResult());
}
void MainWindow::updateResultItemSize()
{
if (!m_resultItem || !ui || !ui->detect_result_list) {
return;
}
const int itemWidth = (std::max)(
360,
ui->detect_result_list->viewport()->width() - 8);
const int itemHeight = (std::max)(
360,
ui->detect_result_list->viewport()->height());
const QSize itemSize(itemWidth, itemHeight);
if (m_resultItem->sizeHint() != itemSize) {
m_resultItem->setSizeHint(itemSize);
SingleTargetResultWidget::setupListWidget(
ui->detect_result_list,
itemSize);
}
}
void MainWindow::addDetectionResult(const DetectionResult& result)
{
ensureResultWidget();
ParkingSpaceGuideInfo displayInfo;
if (result.parkingSpaceInfoList.empty()) {
displayInfo.targetId = QStringLiteral("T001");
displayInfo.parkId = QStringLiteral("P01");
displayInfo.expectedModelType.clear();
displayInfo.modelType = QStringLiteral("未知");
displayInfo.guideStateCode = static_cast<int>(ParkingGuideState::Waiting);
displayInfo.guideText = QStringLiteral("WAIT");
} else {
displayInfo = result.parkingSpaceInfoList.front();
}
const ParkingSpaceGuideInfo& info = displayInfo;
const ParkingGuideState guideState = ParkingGuideStateFromCode(info.guideStateCode);
m_resultWidget->setTitle(tr("当前状态"));
m_resultWidget->setStatus(
GuideStateName(guideState),
info.hasException || guideState == ParkingGuideState::EmergencyStop);
QLabel* statusLabel = m_resultWidget->findChild<QLabel*>(
QStringLiteral("singleTargetStatus"));
if (statusLabel) {
statusLabel->setStyleSheet(
QStringLiteral("background-color: transparent;"));
}
m_resultWidget->setFields({
{tr("目标编号"), info.targetId},
{tr("停机位"), info.parkId},
{tr("机型校验"), FormatModelVerification(info)},
{tr("轮挡区域人员"), info.personInChockRegion
? tr("已检测") : tr("未检测")},
{tr("区域人数"), QString::number(info.personCount)},
{tr("引导状态"), FormatGuideState(info)},
{tr("到停止线"), FormatNumber(info.distance, tr("毫米"))},
{tr("横向偏移"), FormatNumber(info.lateralOffset, tr("毫米"))},
{tr("偏转角"), FormatNumber(info.angle, tr(""))},
{tr("速度"), FormatNumber(info.aircraftSpeed, tr("米/秒"), 2)}
});
}
void MainWindow::OnStatusUpdate(const std::string& statusMessage)
{
updateStatusLog(QString::fromStdString(statusMessage));
}
void MainWindow::OnDetectionResult(const DetectionResult& result)
{
const ParkingGuideState guideState =
result.parkingSpaceInfoList.empty()
? ParkingGuideState::Unknown
: ParkingGuideStateFromCode(
result.parkingSpaceInfoList.front().guideStateCode);
const bool preserveStateTransition =
result.imageIsRoi ||
guideState == ParkingGuideState::StopPositionReached ||
guideState == ParkingGuideState::DockingCompleted ||
guideState == ParkingGuideState::ChocksOn;
if (preserveStateTransition) {
if (QThread::currentThread() == thread()) {
updateDetectionResultDisplay(result);
} else {
QMetaObject::invokeMethod(this, [this, result]() {
updateDetectionResultDisplay(result);
}, Qt::QueuedConnection);
}
return;
}
bool queueUpdate = false;
{
std::lock_guard<std::mutex> lock(m_pendingResultMutex);
m_pendingDetectionResult = result;
if (!m_detectionResultUpdateQueued) {
m_detectionResultUpdateQueued = true;
queueUpdate = true;
}
}
if (!queueUpdate) {
return;
}
QMetaObject::invokeMethod(this, [this]() {
DetectionResult latestResult;
{
std::lock_guard<std::mutex> lock(m_pendingResultMutex);
latestResult = m_pendingDetectionResult;
m_detectionResultUpdateQueued = false;
}
updateDetectionResultDisplay(latestResult);
}, Qt::QueuedConnection);
}
void MainWindow::resizeEvent(QResizeEvent* event)
{
QMainWindow::resizeEvent(event);
updateResultItemSize();
if (m_imagePixmapItem && ui && ui->detect_image) {
ui->detect_image->fitInView(m_imagePixmapItem, Qt::KeepAspectRatio);
}
}
void MainWindow::OnCamera1StatusChanged(bool isConnected)
{
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, [this, isConnected]() {
OnCamera1StatusChanged(isConnected);
}, Qt::QueuedConnection);
return;
}
if (m_deviceStatusWidget) {
m_deviceStatusWidget->updateCamera1Status(isConnected);
}
}
void MainWindow::OnCamera2StatusChanged(bool isConnected)
{
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, [this, isConnected]() {
OnCamera2StatusChanged(isConnected);
}, Qt::QueuedConnection);
return;
}
if (m_deviceStatusWidget) {
m_deviceStatusWidget->updateCamera2Status(isConnected);
}
}
void MainWindow::OnRobotConnectionChanged(bool isConnected)
{
Q_UNUSED(isConnected);
}
void MainWindow::OnSerialConnectionChanged(bool isConnected)
{
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, [this, isConnected]() {
OnSerialConnectionChanged(isConnected);
}, Qt::QueuedConnection);
return;
}
if (m_deviceStatusWidget) {
m_deviceStatusWidget->updateSerialStatus(isConnected);
}
}
void MainWindow::OnCameraCountChanged(int cameraCount)
{
Q_UNUSED(cameraCount);
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, [this]() {
OnCameraCountChanged(2);
}, Qt::QueuedConnection);
return;
}
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)
{
m_emergencyStopControlsLocked =
status == WorkStatus::EmergencyStop;
if (ui_dialogAlgoArg) {
const bool detectionRunning =
m_presenter && m_presenter->IsDetectionRunning();
ui_dialogAlgoArg->setEnabled(
!safetyControlsLocked() && !detectionRunning);
}
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;");
resetAircraftModelSelection();
setButtonsEnabled(true);
if (ui->btn_stop) ui->btn_stop->setEnabled(false);
break;
case WorkStatus::InitIng:
ui->label_work->setStyleSheet("color: blue;");
setButtonsEnabled(false);
break;
case WorkStatus::Working:
ui->label_work->setStyleSheet("color: blue;");
setButtonsEnabled(false);
if (ui->btn_stop) ui->btn_stop->setEnabled(true);
break;
case WorkStatus::Detecting:
ui->label_work->setStyleSheet("color: blue;");
setButtonsEnabled(false);
break;
case WorkStatus::Completed:
ui->label_work->setStyleSheet("color: green; font-weight: bold;");
resetAircraftModelSelection();
setButtonsEnabled(true);
if (ui->btn_stop) ui->btn_stop->setEnabled(false);
break;
case WorkStatus::Error:
ui->label_work->setStyleSheet("color: red; font-weight: bold;");
resetAircraftModelSelection();
setButtonsEnabled(true);
if (ui->btn_stop) ui->btn_stop->setEnabled(false);
break;
case WorkStatus::EmergencyStop:
ui->label_work->setStyleSheet("color: red; font-weight: bold;");
setButtonsEnabled(false);
if (ui->btn_stop) ui->btn_stop->setEnabled(false);
break;
default:
ui->label_work->setStyleSheet("");
setButtonsEnabled(false);
break;
}
}
void MainWindow::updateDetectionResultDisplay(const DetectionResult& result)
{
if (!result.parkingSpaceInfoList.empty()) {
const ParkingGuideState state = ParkingGuideStateFromCode(
result.parkingSpaceInfoList.front().guideStateCode);
if (state == ParkingGuideState::EmergencyStop ||
state == ParkingGuideState::SystemFailure) {
m_safetyResultControlsLocked = true;
} else if (state == ParkingGuideState::Waiting) {
m_safetyResultControlsLocked = false;
}
if (ui_dialogAlgoArg) {
const bool detectionRunning =
m_presenter && m_presenter->IsDetectionRunning();
ui_dialogAlgoArg->setEnabled(
!safetyControlsLocked() && !detectionRunning);
}
if (safetyControlsLocked()) {
setButtonsEnabled(false);
}
}
if (!result.image.isNull()) {
displayImage(result.image);
}
addDetectionResult(result);
}
void MainWindow::on_btn_start_clicked()
{
if (!m_presenter) {
updateStatusLog(tr("系统未初始化"));
return;
}
const QString selectedModel =
ui->combo_aircraft_model->currentIndex() > 0
? ui->combo_aircraft_model->currentText().trimmed()
: QString();
setButtonsEnabled(false);
if (ui_dialogAlgoArg) {
ui_dialogAlgoArg->setEnabled(false);
}
clearDetectionLog();
updateStatusLog(selectedModel.isEmpty()
? tr("开始停机引导检测,机型以实际检测结果为准")
: tr("开始%1停机引导检测").arg(selectedModel));
if (!m_presenter->StartDetection(selectedModel, -1)) {
updateStatusLog(tr("启动检测失败"));
setButtonsEnabled(true);
if (ui_dialogAlgoArg) {
ui_dialogAlgoArg->setEnabled(!safetyControlsLocked());
}
}
}
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;
}
if (m_presenter->IsDetectionRunning()) {
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("打开雷达地面调平参数"));
showParameterDialog(DialogAlgoArg::ConfigPage::Calibration, ui->btn_camera_levelling);
}
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);
setButtonsEnabled(false);
clearDetectionLog();
updateStatusLog(tr("运行预留算法接口测试"));
if (m_testThread.joinable()) {
m_testThread.join();
}
m_testRunning.store(true);
m_testThread = std::thread([this]() {
const bool ok = m_presenter->TriggerDetection(-1);
if (!m_closing.load()) {
updateStatusLog(ok ? tr("测试检测完成") : tr("测试检测失败"));
}
QMetaObject::invokeMethod(this, [this]() {
if (m_closing.load()) {
return;
}
setButtonSelectedState(ui->btn_test, false);
const bool running = m_presenter && m_presenter->IsDetectionRunning();
setButtonsEnabled(!running);
if (ui->btn_stop) {
ui->btn_stop->setEnabled(
running && !safetyControlsLocked());
}
}, Qt::QueuedConnection);
m_testRunning.store(false);
});
}
void MainWindow::setButtonsEnabled(bool enabled)
{
const bool controlsEnabled =
enabled &&
!safetyControlsLocked() &&
!(m_presenter && m_presenter->IsDetectionRunning());
if (ui->combo_aircraft_model) {
ui->combo_aircraft_model->setEnabled(controlsEnabled);
}
if (ui->btn_start) {
ui->btn_start->setEnabled(controlsEnabled);
}
if (ui->btn_stop) ui->btn_stop->setEnabled(controlsEnabled);
if (ui->btn_camera) ui->btn_camera->setEnabled(controlsEnabled);
if (ui->btn_algo_config) ui->btn_algo_config->setEnabled(controlsEnabled);
if (ui->btn_camera_levelling) ui->btn_camera_levelling->setEnabled(controlsEnabled);
if (ui->btn_test) ui->btn_test->setEnabled(controlsEnabled);
}
void MainWindow::resetAircraftModelSelection()
{
if (ui->combo_aircraft_model) {
ui->combo_aircraft_model->setCurrentIndex(0);
}
}
bool MainWindow::safetyControlsLocked() const
{
return m_emergencyStopControlsLocked ||
m_safetyResultControlsLocked;
}
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
};
const bool controlsEnabled =
enabled &&
!safetyControlsLocked() &&
!(m_presenter && m_presenter->IsDetectionRunning());
for (QPushButton* button : configButtons) {
if (button && button != exceptButton) {
button->setEnabled(controlsEnabled);
}
}
}
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;
}
}