#include "mainwindow.h" #include "ui_mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "IVrUtils.h" #include "IVrConfig.h" #include "PathManager.h" #include "dialogcontrolpassword.h" #include "SingleTargetResultWidget.h" #include "StyledMessageBox.h" #include "Version.h" #include "VrDateUtils.h" #include "VrLog.h" namespace { void EnableComboBoxTouchScroll(QComboBox* combo) { if (!combo) { return; } // 触摸屏下拉一次显示有限项,其余靠滑动和滚动条选择。 combo->setMaxVisibleItems(8); QAbstractItemView* view = combo->view(); if (!view) { return; } view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); view->setTextElideMode(Qt::ElideNone); QWidget* viewport = view->viewport(); viewport->setAttribute(Qt::WA_AcceptTouchEvents, true); QScroller::grabGesture(viewport, QScroller::LeftMouseButtonGesture); QScroller* scroller = QScroller::scroller(viewport); if (scroller) { QScrollerProperties properties = scroller->scrollerProperties(); properties.setScrollMetric( QScrollerProperties::VerticalOvershootPolicy, QVariant(static_cast(QScrollerProperties::OvershootAlwaysOff))); scroller->setScrollerProperties(properties); } } 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(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.isEmpty() && normalized != QStringLiteral("未知") && normalized != QStringLiteral("请选择机型"); } 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(resultWidget->layout()); if (mainLayout) { mainLayout->setContentsMargins(96, 12, 96, 12); } if (mainLayout && mainLayout->count() > 0) { auto* headerLayout = qobject_cast(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: 42px; }" "#singleTargetStatus { font-size: 48px; font-weight: 700; padding: 0; }" "#singleTargetEmpty { font-size: 42px; }" "#singleTargetFieldName { font-size: 38px; }" "#singleTargetFieldValue { font-size: 40px; }")); auto* statusLabel = resultWidget->findChild(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); EnableComboBoxTouchScroll(ui->combo_model); 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(); if (ui->label_work) { ui->label_work->setCursor(Qt::PointingHandCursor); ui->label_work->installEventFilter(this); updateWorkModeLabel(); } m_logHelper = new DetectLogHelper(ui->detect_log, this); qRegisterMetaType("DetectionResult"); qRegisterMetaType("WorkStatus"); qRegisterMetaType("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(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) { LOG_INFO("[ParkingSpaceGuide][Shutdown] MainWindow close requested\n"); 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; } LOG_INFO("[ParkingSpaceGuide][Shutdown] MainWindow worker threads complete\n"); if (m_initThread.joinable()) { m_initThread.join(); } if (m_testThread.joinable()) { m_testThread.join(); } if (m_presenter) { LOG_INFO("[ParkingSpaceGuide][Shutdown] MainWindow DeinitApp begin\n"); m_presenter->DeinitApp(); m_presenter->SetStatusCallback(static_cast(nullptr)); LOG_INFO("[ParkingSpaceGuide][Shutdown] MainWindow DeinitApp complete\n"); } 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; } if (obj == ui->label_work && event->type() == QEvent::MouseButtonPress) { if (!m_presenter || safetyControlsLocked() || m_initRunning.load()) { return true; } // 开始引导后禁止切换正常/维护模式。 if (m_presenter->IsDetectionRunning()) { updateStatusLog(tr("停机引导运行中,禁止切换工作模式")); return true; } const bool nextMaintenance = !m_presenter->IsMaintenanceMode(); if (!promptControlPassword(nextMaintenance ? tr("进入维护模式") : tr("退出维护模式"))) { return true; } if (m_presenter->SetMaintenanceMode(nextMaintenance)) { updateWorkModeLabel(); updateStatusLog(nextMaintenance ? tr("已进入维护模式") : tr("已退出维护模式")); } 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(this); connect(m_presenter, &ParkingSpaceGuidePresenter::SelectedModelChanged, this, [this](const QString& modelType) { setAircraftModelSelection(modelType); }, 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(); updateSystemStatusLabel(); 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("初始化完成")); QMetaObject::invokeMethod(this, [this]() { populateAircraftModels(); addDetectionResult(DetectionResult()); updateWorkModeLabel(); }, Qt::QueuedConnection); if (m_simulationOptions.enabled) { updateStatusLog(tr("仿真数据已全部加载到内存,请选择机型并点击开始进行回放")); } else { updateStatusLog(tr("请选择机型并点击开始")); } } m_initRunning.store(false); // 初始化线程结束后再按实际引导状态刷新按钮,避免停止键一直被关掉 if (!m_closing.load()) { QMetaObject::invokeMethod(this, [this]() { if (m_closing.load()) { return; } setButtonsEnabled(true); }, Qt::QueuedConnection); } }); } 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 = currentTargetId(); displayInfo.parkId = currentParkId(); displayInfo.expectedModelType.clear(); displayInfo.modelType = QStringLiteral("未知"); displayInfo.guideStateCode = static_cast(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( QStringLiteral("%1(%2)") .arg(GuideStateName(guideState)) .arg(info.guideStateCode), info.hasException || guideState == ParkingGuideState::EmergencyStop); QLabel* statusLabel = m_resultWidget->findChild( 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("毫米"), 0)}, {tr("横向偏移"), FormatNumber(info.lateralOffset, tr("毫米"), 0)}, {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 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 lock(m_pendingResultMutex); latestResult = m_pendingDetectionResult; m_detectionResultUpdateQueued = false; } updateDetectionResultDisplay(latestResult); }, Qt::QueuedConnection); } void MainWindow::OnPreviewFrame(const QImage& image) { if (image.isNull() || !ui_dialogAlgoArg) { return; } if (QThread::currentThread() == thread()) { ui_dialogAlgoArg->OnPreviewFrame(image); } else { QMetaObject::invokeMethod(this, [this, image]() { if (ui_dialogAlgoArg) { ui_dialogAlgoArg->OnPreviewFrame(image); } }, Qt::QueuedConnection); } } void MainWindow::resizeEvent(QResizeEvent* event) { QMainWindow::resizeEvent(event); updateResultItemSize(); } 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(); } const QString statusText = QString::fromStdString(WorkStatusToString(status)); updateStatusLog(tr("工作状态:%1").arg(statusText)); updateWorkModeLabel(); updateSystemStatusLabel(); switch (status) { case WorkStatus::Ready: resetAircraftModelSelection(); setButtonsEnabled(true); break; case WorkStatus::InitIng: setButtonsEnabled(false); break; case WorkStatus::Working: case WorkStatus::Detecting: setButtonsEnabled(true); break; case WorkStatus::Completed: resetAircraftModelSelection(); setButtonsEnabled(true); break; case WorkStatus::Error: resetAircraftModelSelection(); setButtonsEnabled(true); break; case WorkStatus::EmergencyStop: setButtonsEnabled(false); break; default: 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); } // 急停/故障锁死全部按键;WAIT 解除后按引导是否在跑恢复停止键 setButtonsEnabled(!safetyControlsLocked()); } addDetectionResult(result); updateWorkModeLabel(); } void MainWindow::on_btn_start_clicked() { if (!m_presenter) { updateStatusLog(tr("系统未初始化")); return; } if (!promptControlPassword(tr("开始引导"))) { return; } const QString selectedModel = selectedAircraftModel(); 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 (!promptControlPassword(tr("停止引导"))) { return; } if (m_presenter) { m_presenter->StopDetection(); } } void MainWindow::on_btn_chock_on_clicked() { recordManualChock(true); } void MainWindow::on_btn_chock_off_clicked() { recordManualChock(false); } 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; } if (!promptControlPassword(tr("参数设置"))) { return; } setButtonSelectedState(button, true); if (!ui_dialogAlgoArg) { ui_dialogAlgoArg = new DialogAlgoArg(this); connect(ui_dialogAlgoArg, &QDialog::finished, this, [this]() { if (m_presenter) { m_presenter->SetLivePreviewEnabled(false); } if (m_selectedButton) { setButtonSelectedState(m_selectedButton, false); } }); } ui_dialogAlgoArg->SetPresenter(m_presenter); ui_dialogAlgoArg->SetCurrentPage(page); m_presenter->SetLivePreviewEnabled(true); 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 (!promptControlPassword(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); setButtonsEnabled(true); }, Qt::QueuedConnection); m_testRunning.store(false); }); } void MainWindow::setButtonsEnabled(bool enabled) { const bool detectionRunning = m_presenter && m_presenter->IsDetectionRunning(); const bool safetyLocked = safetyControlsLocked(); const bool initDone = !m_initRunning.load(); const bool canStart = enabled && initDone && !safetyLocked && !detectionRunning; // 自动进入引导后必须能点停止;急停锁定时除外 const bool canStop = !safetyLocked && detectionRunning; const bool canChock = initDone && !safetyLocked && m_presenter; if (ui->combo_model) { ui->combo_model->setEnabled(canStart); } if (ui->btn_start) { ui->btn_start->setEnabled(canStart); } if (ui->btn_stop) { ui->btn_stop->setEnabled(canStop); } if (ui->btn_camera) { ui->btn_camera->setEnabled(canStart); } if (ui->btn_algo_config) { ui->btn_algo_config->setEnabled(canStart); } if (ui->btn_camera_levelling) { ui->btn_camera_levelling->setEnabled(canStart); } if (ui->btn_test) { ui->btn_test->setEnabled(canStart); } if (ui->btn_chock_on) { ui->btn_chock_on->setEnabled(canChock); } if (ui->btn_chock_off) { ui->btn_chock_off->setEnabled(canChock); } updateWorkModeLabel(); updateSystemStatusLabel(); } void MainWindow::resetAircraftModelSelection() { setAircraftModelSelection(QString()); } QString MainWindow::selectedAircraftModel() const { if (!ui->combo_model || ui->combo_model->currentIndex() <= 0) { return QString(); } return ui->combo_model->currentText().trimmed(); } void MainWindow::setAircraftModelSelection(const QString& modelType) { if (!ui->combo_model) { return; } const QString normalized = modelType.trimmed(); const QSignalBlocker blocker(ui->combo_model); if (normalized.isEmpty()) { ui->combo_model->setCurrentIndex(0); return; } int index = ui->combo_model->findText(normalized, Qt::MatchFixedString); if (index < 0) { ui->combo_model->addItem(normalized); index = ui->combo_model->findText(normalized, Qt::MatchFixedString); } ui->combo_model->setCurrentIndex(index > 0 ? index : 0); } void MainWindow::populateAircraftModels() { if (!ui->combo_model) { return; } const QString current = selectedAircraftModel(); const QSignalBlocker blocker(ui->combo_model); ui->combo_model->clear(); ui->combo_model->addItem(QStringLiteral("请选择机型")); AircraftModelsConfig models = AircraftModelsConfig::DefaultModels(); if (m_presenter && m_presenter->GetConfigManager()) { models = m_presenter->GetConfigManager()->GetConfigResult().aircraftModels; if (models.models.empty()) { models = AircraftModelsConfig::DefaultModels(); } } for (const AircraftModelItem& item : models.models) { if (!item.name.trimmed().isEmpty()) { ui->combo_model->addItem(item.name.trimmed()); } } if (!current.isEmpty()) { const int index = ui->combo_model->findText(current, Qt::MatchFixedString); ui->combo_model->setCurrentIndex(index > 0 ? index : 0); } } QString MainWindow::currentTargetId() const { if (m_presenter && m_presenter->GetConfigManager()) { const std::string targetId = m_presenter->GetConfigManager()->GetConfigResult() .udpBroadcastConfig.targetId; if (!targetId.empty()) { return QString::fromStdString(targetId); } } return QStringLiteral("T001"); } QString MainWindow::currentParkId() const { if (m_presenter && m_presenter->GetConfigManager()) { const std::string parkId = m_presenter->GetConfigManager()->GetConfigResult() .udpBroadcastConfig.parkId; if (!parkId.empty()) { return QString::fromStdString(parkId); } } return QStringLiteral("P01"); } void MainWindow::updateSystemStatusLabel() { if (!ui || !ui->label_system_status) { return; } // 开始引导后为系统已激活,停止引导后为未激活 const bool activated = m_presenter && m_presenter->IsDetectionRunning(); ui->label_system_status->setText(activated ? tr("系统已激活") : tr("未激活")); ui->label_system_status->setStyleSheet(activated ? QStringLiteral( "color: rgb(0, 255, 0);" "background-color: transparent;" "border: none;" "font-weight: 700;") : QStringLiteral( "color: rgb(172, 181, 194);" "background-color: transparent;" "border: none;" "font-weight: 700;")); } void MainWindow::updateWorkModeLabel() { if (!ui->label_work) { return; } const bool maintenance = m_presenter && m_presenter->IsMaintenanceMode(); const bool guidanceRunning = m_presenter && m_presenter->IsDetectionRunning(); ui->label_work->setText(maintenance ? tr("维护模式") : tr("正常模式")); ui->label_work->setCursor(guidanceRunning ? Qt::ForbiddenCursor : Qt::PointingHandCursor); if (guidanceRunning) { ui->label_work->setStyleSheet( QStringLiteral("color: rgb(130, 132, 138); font-weight: 700;")); } else { ui->label_work->setStyleSheet(maintenance ? QStringLiteral("color: rgb(255, 160, 40); font-weight: 700;") : QStringLiteral("color: rgb(0, 255, 0); font-weight: 700;")); } } bool MainWindow::promptControlPassword(const QString& title) { if (!m_presenter) { StyledMessageBox::warning(this, tr("错误"), tr("系统未初始化")); return false; } if (m_presenter->ValidateControlPassword(QString())) { return true; } // 有效期内不再弹窗,超时后重新输入。 if (m_passwordValidUntilMs > QDateTime::currentMSecsSinceEpoch()) { return true; } DialogControlPassword dialog(this); dialog.SetTitle(title); dialog.SetPasswordChecker([this](const QString& password) { return m_presenter->ValidateControlPassword(password); }); if (dialog.exec() != QDialog::Accepted) { return false; } RememberControlPassword(); return true; } int MainWindow::ControlPasswordValidSeconds() const { if (!m_presenter || !m_presenter->GetConfigManager()) { return 120; } return m_presenter->GetConfigManager()->GetConfigResult() .controlPanelConfig.validSeconds; } void MainWindow::RememberControlPassword() { const int validSeconds = ControlPasswordValidSeconds(); m_passwordValidUntilMs = QDateTime::currentMSecsSinceEpoch() + static_cast(validSeconds) * 1000; } void MainWindow::recordManualChock(bool chockOn) { if (!m_presenter) { updateStatusLog(tr("系统未初始化")); return; } if (!promptControlPassword(chockOn ? tr("上轮挡") : tr("下轮挡"))) { return; } if (!m_presenter->RecordManualChock(chockOn)) { updateStatusLog(chockOn ? tr("记录上轮挡时间失败") : tr("记录下轮挡时间失败")); return; } updateStatusLog(chockOn ? tr("已记录上轮挡时间") : tr("已记录下轮挡时间")); } 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 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_result_list->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->detect_result_list, &QListWidget::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_result_list->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; } }