#include "MainWindow.h" #include #include #include #include #include #include #include #include "RemoteGuideClient.h" #include "IVrUtils.h" #include "Version.h" #include "ui_MainWindow.h" namespace { QString NonEmpty(const QString& value, const QString& fallback) { const QString trimmed = value.trimmed(); return trimmed.isEmpty() ? fallback : trimmed; } bool IsKnownAircraftModel(const QString& modelType) { const QString normalized = modelType.trimmed().toUpper(); return normalized == QStringLiteral("A320") || normalized == QStringLiteral("B737"); } QString FormatModelVerification(const QString& expectedModel, const QString& detectedModel, bool modelMatched) { const QString expected = expectedModel.trimmed().toUpper(); const QString detected = detectedModel.trimmed().toUpper(); if (!IsKnownAircraftModel(detected)) { return QStringLiteral("未知"); } if (!IsKnownAircraftModel(expected)) { return QStringLiteral("实测(%1)").arg(detected); } if (modelMatched) { return QStringLiteral("匹配(%1)").arg(detected); } if (detected.compare(expected, Qt::CaseInsensitive) != 0) { return QStringLiteral("不匹配(%1)").arg(detected); } return QStringLiteral("未知"); } } // namespace MainWindow::MainWindow(int discoveryPort, QWidget* parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); setWindowTitle(QStringLiteral("停机引导远程显示 %1") .arg(QString::fromLatin1( PARKINGSPACEGUIDEVIEW_FULL_VERSION_STRING))); const QString versionWithBuildTime = QStringLiteral("%1_%2%3%4%5%6%7") .arg(QString::fromLatin1( PARKINGSPACEGUIDEVIEW_FULL_VERSION_STRING)) .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')); ui->versionLabel->setText(versionWithBuildTime); statusBar()->addPermanentWidget(ui->versionLabel); connect(ui->windowButton, &QPushButton::clicked, this, &QWidget::showMinimized); connect(ui->closeWindowButton, &QPushButton::clicked, this, &QWidget::close); m_client = new RemoteGuideClient(discoveryPort, this); connect(m_client, &RemoteGuideClient::ConnectionStateChanged, this, &MainWindow::HandleConnectionState); connect(m_client, &RemoteGuideClient::StatusReceived, this, &MainWindow::ApplyStatus); connect(m_client, &RemoteGuideClient::GuidanceRunningChanged, this, [this](bool running) { const bool wasRunning = m_guidanceRunning; m_guidanceRunning = running; if (wasRunning && !running) { const QSignalBlocker blocker(ui->modelCombo); ui->modelCombo->setCurrentIndex(0); } UpdateControls(); }); connect(m_client, &RemoteGuideClient::CommandFinished, this, &MainWindow::HandleCommandFinished); connect(ui->modelCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { if (index > 0 && m_client && m_client->IsConnected()) { m_commandPending = true; UpdateControls(); m_client->SelectModel(ui->modelCombo->currentText()); } UpdateControls(); }); connect(ui->startButton, &QPushButton::clicked, this, [this]() { m_commandPending = true; UpdateControls(); m_client->StartGuidance( ui->modelCombo->currentIndex() > 0 ? ui->modelCombo->currentText() : QString()); }); connect(ui->stopButton, &QPushButton::clicked, this, [this]() { m_commandPending = true; UpdateControls(); m_client->StopGuidance(); }); ApplyStatus(QJsonObject()); HandleConnectionState(false, QStringLiteral("正在搜索停机引导设备……"), QString(), QString()); } MainWindow::~MainWindow() { delete ui; } void MainWindow::ApplyStatus(const QJsonObject& status) { ui->stateImage->SetStatus(status); int stateCode = status.value("guideStateCode").toInt(12); if (stateCode < 1 || stateCode > 24) { stateCode = 12; } ApplyGuideState(stateCode, status.value("guideText").toString()); ui->targetIdLabel->setText(NonEmpty( status.value("targetId").toString(), QStringLiteral("T001"))); ui->parkIdLabel->setText(NonEmpty( status.value("parkId").toString(), QStringLiteral("未知"))); QString expectedModel = status.value("expectedModelType") .toString().trimmed(); if (m_guidanceRunning && IsKnownAircraftModel(expectedModel) && ui->modelCombo->currentText() != expectedModel) { const QSignalBlocker blocker(ui->modelCombo); ui->modelCombo->setCurrentText(expectedModel); } if (expectedModel.isEmpty() && ui->modelCombo->currentIndex() > 0) { expectedModel = ui->modelCombo->currentText(); } const QString detectedModel = NonEmpty( status.value("modelType").toString(), QStringLiteral("未知")); ui->modelMatchLabel->setText(FormatModelVerification( expectedModel, detectedModel, status.value("modelMatched").toBool())); const int personCount = status.value("personCount").toInt(0); ui->personLabel->setText(status.value("personInChockRegion").toBool() ? QStringLiteral("已检测") : QStringLiteral("未检测")); ui->personCountLabel->setText(QString::number(personCount)); SetMetric(ui->distanceLabel, status, "distanceToStop", QStringLiteral("毫米")); SetMetric(ui->offsetLabel, status, "lateralOffset", QStringLiteral("毫米")); SetMetric(ui->yawLabel, status, "bodyYawAngle", QStringLiteral("度")); SetMetric(ui->speedLabel, status, "aircraftSpeed", QStringLiteral("米/秒")); } void MainWindow::ApplyGuideState(int stateCode, const QString& guideText) { const GuidePresentation& presentation = PresentationForState(stateCode); ui->stateNameLabel->setText(presentation.name); QString stateText = QStringLiteral("%1 %2") .arg(stateCode) .arg(presentation.name); const QString trimmedGuideText = guideText.trimmed(); if (!trimmedGuideText.isEmpty()) { stateText += QStringLiteral(" / %1").arg(trimmedGuideText); } ui->guideStateLabel->setText(stateText); if (stateCode != m_currentStateCode) { m_currentStateCode = stateCode; ui->stateCodeLabel->setText( QStringLiteral("状态 #%1").arg(stateCode, 2, 10, QChar('0'))); } } void MainWindow::UpdateControls() { ui->modelCombo->setEnabled(m_connected && !m_guidanceRunning && !m_commandPending); ui->startButton->setEnabled(m_connected && !m_guidanceRunning && !m_commandPending); ui->stopButton->setEnabled(m_connected && m_guidanceRunning && !m_commandPending); } void MainWindow::SetMetric(QLabel* label, const QJsonObject& status, const char* field, const QString& unit, int precision, double divisor) { const double safeDivisor = divisor == 0.0 ? 1.0 : divisor; const double value = status.value(QString::fromLatin1(field)) .toDouble(0.0) / safeDivisor; label->setText(QString::number(value, 'f', precision) + unit); } void MainWindow::HandleConnectionState(bool connected, const QString& message, const QString& deviceName, const QString& address) { Q_UNUSED(message); Q_UNUSED(deviceName); Q_UNUSED(address); m_connected = connected; m_commandPending = false; m_guidanceRunning = connected && m_client ? m_client->IsGuidanceRunning() : false; if (!connected) { const QSignalBlocker blocker(ui->modelCombo); ui->modelCombo->setCurrentIndex(0); } ui->deviceLabel->setText(connected ? QStringLiteral("已连接") : QStringLiteral("未连接")); UpdateControls(); } void MainWindow::HandleCommandFinished(const QString& command, bool ok, const QString& message, const QJsonObject& response) { Q_UNUSED(response); Q_UNUSED(message); m_commandPending = false; if (ok && command == QStringLiteral("start")) { m_guidanceRunning = true; } else if (ok && command == QStringLiteral("stop")) { m_guidanceRunning = false; const QSignalBlocker blocker(ui->modelCombo); ui->modelCombo->setCurrentIndex(0); } else if (!ok && command == QStringLiteral("select_model")) { const QSignalBlocker blocker(ui->modelCombo); ui->modelCombo->setCurrentIndex(0); } if (m_client && m_client->IsConnected()) { m_guidanceRunning = m_client->IsGuidanceRunning(); } UpdateControls(); } void MainWindow::resizeEvent(QResizeEvent* event) { QMainWindow::resizeEvent(event); } const MainWindow::GuidePresentation& MainWindow::PresentationForState(int stateCode) { static const std::array states = {{ {QStringLiteral("泊位引导启动"), QStringLiteral("START-OF-DOCKING"), QStringLiteral("WAIT"), QStringLiteral("系统已进入泊位引导等待状态。"), QStringLiteral("等待运行箭头或接近率条出现,并确认机型正确。"), QStringLiteral("warn")}, {QStringLiteral("捕获"), QStringLiteral("CAPTURE"), QStringLiteral("CAPTURE"), QStringLiteral("运行箭头和飞机类型表示系统正在搜索接近中的飞机。"), QStringLiteral("确认机型正确,沿引导线低速滑行。"), QStringLiteral("good")}, {QStringLiteral("跟踪"), QStringLiteral("TRACKING"), QStringLiteral("TRACKING"), QStringLiteral("中心线和转向箭头提示飞机位置及修正方向。"), QStringLiteral("按红色箭头小幅修正方向,保持低速。"), QStringLiteral("good")}, {QStringLiteral("接近率"), QStringLiteral("CLOSING RATE"), QStringLiteral("CLOSING"), QStringLiteral("数字和接近率条显示距停止位置的剩余距离。"), QStringLiteral("保持低速并按箭头修正,准备在 STOP 出现时停车。"), QStringLiteral("good")}, {QStringLiteral("对准中心线"), QStringLiteral("ALIGNED TO CENTRE"), QStringLiteral("ALIGNED"), QStringLiteral("显示距离但无转向箭头,表示飞机已对准中心线。"), QStringLiteral("继续低速向停止位滑行。"), QStringLiteral("good")}, {QStringLiteral("慢速"), QStringLiteral("SLOW"), QStringLiteral("SLOW"), QStringLiteral("飞机接近速度高于当前区域的允许速度。"), QStringLiteral("立即减速,观察是否恢复正常接近率显示。"), QStringLiteral("warn")}, {QStringLiteral("方位引导"), QStringLiteral("AZIMUTH GUIDANCE"), QStringLiteral("GUIDANCE"), QStringLiteral("位置箭头和转向箭头共同提示方位修正。"), QStringLiteral("按转向箭头修正并保持低速。"), QStringLiteral("good")}, {QStringLiteral("到达停止位置"), QStringLiteral("STOP POSITION"), QStringLiteral("STOP"), QStringLiteral("飞机已经到达正确停止位置。"), QStringLiteral("立即停车,等待地面确认。"), QStringLiteral("stop")}, {QStringLiteral("泊位引导完成"), QStringLiteral("DOCKING COMPLETED"), QStringLiteral("OK"), QStringLiteral("泊位引导流程已经完成。"), QStringLiteral("保持停稳,等待地面服务。"), QStringLiteral("good")}, {QStringLiteral("越位"), QStringLiteral("OVERSHOOT"), QStringLiteral("TOO FAR"), QStringLiteral("飞机已经越过目标停止位置。"), QStringLiteral("立即停止,等待人工处置。"), QStringLiteral("stop")}, {QStringLiteral("提前停止"), QStringLiteral("STOP SHORT"), QStringLiteral("STOP OK"), QStringLiteral("飞机已静止,但尚未到达预定停止位置。"), QStringLiteral("等待地面确认是否继续或结束。"), QStringLiteral("warn")}, {QStringLiteral("等待"), QStringLiteral("WAIT"), QStringLiteral("WAIT"), QStringLiteral("系统等待视野恢复或重新检测到飞机。"), QStringLiteral("保持等待,在接近率条恢复前不要继续。"), QStringLiteral("warn")}, {QStringLiteral("异常慢速-恶劣天气"), QStringLiteral("SLOW - WEATHER"), QStringLiteral("SLOW"), QStringLiteral("天气原因导致系统进入异常慢速捕获模式。"), QStringLiteral("低速等待接近率条出现。"), QStringLiteral("warn")}, {QStringLiteral("异常慢速-飞机丢失"), QStringLiteral("SLOW - LOST"), QStringLiteral("SLOW"), QStringLiteral("引导过程中暂时丢失飞机目标。"), QStringLiteral("低速等待系统重新检测飞机。"), QStringLiteral("warn")}, {QStringLiteral("飞机验证失败"), QStringLiteral("ID FAIL"), QStringLiteral("STOP ID FAIL"), QStringLiteral("检测机型与选择机型验证失败。"), QStringLiteral("停止飞机,等待人工复核,不得自行继续。"), QStringLiteral("stop")}, {QStringLiteral("机位受阻"), QStringLiteral("GATE BLOCKED"), QStringLiteral("WAIT GATE BLOCK"), QStringLiteral("机位前方或进近视野存在障碍。"), QStringLiteral("等待障碍物清除,未恢复前不要继续。"), QStringLiteral("warn")}, {QStringLiteral("视野受阻"), QStringLiteral("VIEW BLOCKED"), QStringLiteral("WAIT VIEW BLOCK"), QStringLiteral("传感器视野受阻,当前无法可靠检测飞机。"), QStringLiteral("等待视野恢复,必要时切换人工引导。"), QStringLiteral("warn")}, {QStringLiteral("SBU停止"), QStringLiteral("SBU STOP"), QStringLiteral("STOP SBU"), QStringLiteral("自动引导因不可恢复错误进入安全备份状态。"), QStringLiteral("立即停止并使用人工引导,不得自行恢复。"), QStringLiteral("stop")}, {QStringLiteral("速度过快"), QStringLiteral("TOO FAST"), QStringLiteral("STOP TOO FAST"), QStringLiteral("飞机速度超出系统可以安全处理的范围。"), QStringLiteral("立即停止飞机,等待系统重启或人工引导。"), QStringLiteral("stop")}, {QStringLiteral("紧急停止"), QStringLiteral("EMERGENCY STOP"), QStringLiteral("STOP"), QStringLiteral("现场急停或急停线路断开已经触发。"), QStringLiteral("立即停止,等待现场确认和处置。"), QStringLiteral("stop")}, {QStringLiteral("挡轮挡"), QStringLiteral("CHOCKS ON"), QStringLiteral("CHOCK ON"), QStringLiteral("轮挡区域人员检测已完成。"), QStringLiteral("保持停稳,进入地面保障流程。"), QStringLiteral("good")}, {QStringLiteral("系统错误"), QStringLiteral("ERROR"), QStringLiteral("ERROR"), QStringLiteral("系统发生错误,自动引导不可继续。"), QStringLiteral("停止自动引导并通知维护人员。"), QStringLiteral("stop")}, {QStringLiteral("系统故障"), QStringLiteral("SYSTEM BREAKDOWN"), QStringLiteral("BLACK"), QStringLiteral("系统发生严重故障,自动引导不可用。"), QStringLiteral("使用人工备份程序。"), QStringLiteral("maint")}, {QStringLiteral("电源故障"), QStringLiteral("POWER FAILURE"), QStringLiteral("BLACK"), QStringLiteral("显示和引导系统电源中断。"), QStringLiteral("使用人工备份程序并检查供电。"), QStringLiteral("maint")} }}; if (stateCode < 1 || stateCode > static_cast(states.size())) { stateCode = 12; } return states[static_cast(stateCode - 1)]; }