#include "flightplancomponent.h" #include "ui_flightplancomponent.h" using namespace BlackMisc; using namespace BlackMisc::Aviation; using namespace BlackMisc::PhysicalQuantities; namespace BlackGui { CFlightPlanComponent::CFlightPlanComponent(QWidget *parent) : QTabWidget(parent), CRuntimeBasedComponent(nullptr, false), ui(new Ui::CFlightPlanComponent) { ui->setupUi(this); connect(this->ui->pb_Send, &QPushButton::pressed, this, &CFlightPlanComponent::sendFlightPlan); connect(this->ui->pb_Load, &QPushButton::pressed, this, &CFlightPlanComponent::loadFlightPlanFromNetwork); connect(this->ui->pb_Reset, &QPushButton::pressed, this, &CFlightPlanComponent::resetFlightPlan); connect(this->ui->pb_ValidateFlightPlan, &QPushButton::pressed, this, &CFlightPlanComponent::validateFlightPlan); bool c; c = connect(this->ui->cb_VoiceCapabilities, SIGNAL(currentIndexChanged(int)), this, SLOT(buildRemarkString())); Q_ASSERT(c); c = connect(this->ui->cb_NavigationEquipment, SIGNAL(currentIndexChanged(int)), this, SLOT(buildRemarkString())); Q_ASSERT(c); c = connect(this->ui->cb_PerformanceCategory, SIGNAL(currentIndexChanged(int)), this, SLOT(buildRemarkString())); Q_ASSERT(c); c = connect(this->ui->cb_PilotRating, SIGNAL(currentIndexChanged(int)), this, SLOT(buildRemarkString())); Q_ASSERT(c); c = connect(this->ui->cb_RequiredNavigationPerformance, SIGNAL(currentIndexChanged(int)), this, SLOT(buildRemarkString())); Q_ASSERT(c); c = connect(this->ui->cb_NoSidsStarts, SIGNAL(toggled(bool)), this, SLOT(buildRemarkString())); Q_ASSERT(c); c = connect(this->ui->le_AircraftRegistration, SIGNAL(textChanged(QString)), this, SLOT(buildRemarkString())); Q_ASSERT(c); c = connect(this->ui->le_AirlineOperator, SIGNAL(textChanged(QString)), this, SLOT(buildRemarkString())); Q_ASSERT(c); Q_UNUSED(c); connect(this->ui->pte_AdditionalRemarks, &QPlainTextEdit::textChanged, this, &CFlightPlanComponent::buildRemarkString); connect(this->ui->frp_SelcalCode, &CSelcalCodeSelector::valueChanged, this, &CFlightPlanComponent::buildRemarkString); connect(this->ui->pb_CopyOver, &QPushButton::pressed, this, &CFlightPlanComponent::copyRemarks); connect(this->ui->pb_RemarksGenerator, &QPushButton::clicked, this, &CFlightPlanComponent::currentTabGenerator); this->ui->frp_SelcalCode->resetSelcalCodes(true); this->resetFlightPlan(); this->buildRemarkString(); } CFlightPlanComponent::~CFlightPlanComponent() { delete ui; } void CFlightPlanComponent::prefillWithAircraftData(const BlackMisc::Aviation::CAircraft &ownAircraft) { this->ui->le_Callsign->setText(ownAircraft.getCallsign().asString()); this->ui->le_AircraftType->setText(ownAircraft.getIcaoInfo().getAircraftDesignator()); this->ui->le_PilotsName->setText(ownAircraft.getPilot().getRealName()); } void CFlightPlanComponent::fillWithFlightPlanData(const BlackMisc::Aviation::CFlightPlan &flightPlan) { this->ui->le_AlternateAirport->setText(flightPlan.getAlternateAirportIcao().asString()); this->ui->le_DestinationAirport->setText(flightPlan.getAlternateAirportIcao().asString()); this->ui->le_OriginAirport->setText(flightPlan.getAlternateAirportIcao().asString()); this->ui->pte_Route->setPlainText(flightPlan.getRoute()); this->ui->pte_Remarks->setPlainText(flightPlan.getRemarks()); this->ui->le_TakeOffTimePlanned->setText(flightPlan.getTakeoffTimePlannedHourMin()); this->ui->le_FuelOnBoard->setText(flightPlan.getFuelTimeHourMin()); this->ui->le_EstimatedTimeEnroute->setText(flightPlan.getEnrouteTimeHourMin()); this->ui->le_CruiseTrueAirspeed->setText(flightPlan.getCruiseTrueAirspeed().valueRoundedWithUnit(BlackMisc::PhysicalQuantities::CSpeedUnit::kts(), 0)); CAltitude cruiseAlt = flightPlan.getCruiseAltitude(); if (cruiseAlt.isFlightLevel()) this->ui->le_CrusingAltitude->setText(cruiseAlt.toQString()); else this->ui->le_CrusingAltitude->setText(cruiseAlt.valueRoundedWithUnit(BlackMisc::PhysicalQuantities::CLengthUnit::ft(), 0)); } CFlightPlan CFlightPlanComponent::getFlightPlan() const { return this->m_flightPlan; } BlackMisc::CStatusMessageList CFlightPlanComponent::validateAndInitializeFlightPlan(BlackMisc::Aviation::CFlightPlan &flightPlan) { BlackMisc::CStatusMessageList messages; QString v; CFlightPlan::FlightRules rule = CFlightPlan::IFR; if (this->ui->rb_TypeIfr->isChecked()) rule = CFlightPlan::IFR; else if (this->ui->rb_TypeVfr->isChecked()) rule = CFlightPlan::VFR; flightPlan.setFlightRule(rule); v = ui->le_Callsign->text().trimmed(); if (v.isEmpty()) { QString m = QString("Missing %1").arg(this->ui->lbl_Callsign->text()); messages.push_back(CStatusMessage::getValidationError(m)); } v = ui->pte_Route->toPlainText().trimmed(); if (v.isEmpty()) { QString m = QString("Missing flight plan route"); messages.push_back(CStatusMessage::getValidationError(m)); } else if (v.length() > CFlightPlan::MaxRouteLength) { QString m = QString("Flight plan route length exceeded (%1 chars max.)").arg(CFlightPlan::MaxRouteLength); messages.push_back(CStatusMessage::getValidationError(m)); } else flightPlan.setRoute(v); v = ui->pte_Remarks->toPlainText().trimmed(); if (v.isEmpty()) { QString m = QString("No remarks, voice capabilities are mandatory"); messages.push_back(CStatusMessage::getValidationError(m)); } else if (v.length() > CFlightPlan::MaxRemarksLength) { QString m = QString("Flight plan remarks length exceeded (%1 chars max.)").arg(CFlightPlan::MaxRemarksLength); messages.push_back(CStatusMessage::getValidationError(m)); } else flightPlan.setRemarks(v); v = ui->le_EstimatedTimeEnroute->text(); if (v.isEmpty() || v == defaultTime()) { QString m = QString("Missing %1").arg(this->ui->lbl_EstimatedTimeEnroute->text()); messages.push_back(CStatusMessage::getValidationError(m)); } else flightPlan.setEnrouteTime(v); v = ui->le_FuelOnBoard->text(); if (v.isEmpty() || v == defaultTime()) { QString m = QString("Missing %1").arg(this->ui->lbl_FuelOnBorad->text()); messages.push_back(CStatusMessage::getValidationError(m)); } else flightPlan.setFuelTime(v); v = ui->le_TakeOffTimePlanned->text(); if (v.isEmpty() || v == defaultTime()) { QString m = QString("Missing %1").arg(this->ui->lbl_TakeOffTimePlanned->text()); messages.push_back(CStatusMessage::getValidationError(m)); } else flightPlan.setTakeoffTimePlanned(v); static const QRegExp withUnit("\\D+"); v = ui->le_CrusingAltitude->text().trimmed(); if (!v.isEmpty() && withUnit.indexIn(v) < 0) { v += "ft"; this->ui->le_CrusingAltitude->setText(v); } CAltitude cruisingAltitude(v, CPqString::SeparatorsLocale); if (v.isEmpty() || cruisingAltitude.isNull()) { QString m = QString("Wrong %1").arg(this->ui->lbl_CrusingAltitude->text()); messages.push_back(CStatusMessage::getValidationError(m)); } else flightPlan.setCruiseAltitude(cruisingAltitude); v = this->ui->le_AlternateAirport->text(); if (v.isEmpty() || v.endsWith(defaultIcao(), Qt::CaseInsensitive)) { QString m = QString("Missing %1").arg(this->ui->lbl_AlternateAirport->text()); messages.push_back(CStatusMessage::getValidationError(m)); flightPlan.setAlternateAirportIcao(defaultIcao()); } else flightPlan.setAlternateAirportIcao(v); v = this->ui->le_DestinationAirport->text(); if (v.isEmpty() || v.endsWith(defaultIcao(), Qt::CaseInsensitive)) { QString m = QString("Missing %1").arg(this->ui->lbl_DestinationAirport->text()); messages.push_back(CStatusMessage::getValidationError(m)); flightPlan.setDestinationAirportIcao(defaultIcao()); } else flightPlan.setDestinationAirportIcao(v); v = this->ui->le_CruiseTrueAirspeed->text(); BlackMisc::PhysicalQuantities::CSpeed cruiseTAS; cruiseTAS.parseFromString(v, CPqString::SeparatorsLocale); if (cruiseTAS.isNull()) { QString m = QString("Wrong TAS, %1").arg(this->ui->lbl_CruiseTrueAirspeed->text()); messages.push_back(CStatusMessage::getValidationError(m)); flightPlan.setDestinationAirportIcao(defaultIcao()); } else flightPlan.setCruiseTrueAirspeed(cruiseTAS); v = this->ui->le_OriginAirport->text(); if (v.isEmpty() || v.endsWith(defaultIcao(), Qt::CaseInsensitive)) { QString m = QString("Missing %1").arg(this->ui->lbl_OriginAirport->text()); messages.push_back(CStatusMessage::getValidationError(m)); flightPlan.setOriginAirportIcao(defaultIcao()); } else flightPlan.setOriginAirportIcao(v); return messages; } void CFlightPlanComponent::sendFlightPlan() { CFlightPlan flightPlan; CStatusMessageList messages = this->validateAndInitializeFlightPlan(flightPlan); if (messages.isEmpty()) { // no error, send if possible CStatusMessage m; if (this->getIContextNetwork()->isConnected()) { flightPlan.setWhenLastSentOrLoaded(QDateTime::currentDateTimeUtc()); this->getIContextNetwork()->sendFlightPlan(flightPlan); this->ui->le_LastSent->setText(flightPlan.whenLastSentOrLoaded().toString()); m = CStatusMessage::getInfoMessage("Sent flight plan", CStatusMessage::TypeTrafficNetwork); } else { flightPlan.setWhenLastSentOrLoaded(QDateTime()); // empty this->ui->le_LastSent->clear(); m = CStatusMessage::getErrorMessage("No errors, but not connected, cannot send flight plan", CStatusMessage::TypeTrafficNetwork); } this->sendStatusMessage(m); this->m_flightPlan = flightPlan; // last valid FP } else { this->sendStatusMessages(messages); } } void CFlightPlanComponent::validateFlightPlan() { CFlightPlan flightPlan; CStatusMessageList messages = this->validateAndInitializeFlightPlan(flightPlan); if (messages.isEmpty()) { this->sendStatusMessage(CStatusMessage::getInfoMessage("No errors", CStatusMessage::TypeTrafficNetwork)); } else { this->sendStatusMessages(messages); } } void CFlightPlanComponent::resetFlightPlan() { if (this->getIContextNetwork()) { this->prefillWithAircraftData(this->getIContextOwnAircraft()->getOwnAircraft()); } this->ui->le_AircraftRegistration->clear(); this->ui->le_AirlineOperator->clear(); this->ui->le_CrusingAltitude->setText("FL70"); this->ui->le_CruiseTrueAirspeed->setText("100 kts"); this->ui->pte_Remarks->clear(); this->ui->pte_Route->clear(); this->ui->le_AlternateAirport->setText(defaultIcao()); this->ui->le_DestinationAirport->setText(defaultIcao()); this->ui->le_OriginAirport->setText(defaultIcao()); this->ui->le_FuelOnBoard->setText(defaultTime()); this->ui->le_EstimatedTimeEnroute->setText(defaultTime()); this->ui->le_TakeOffTimePlanned->setText(QDateTime::currentDateTimeUtc().addSecs(30 * 60).toString("hh:mm")); } void CFlightPlanComponent::loadFlightPlanFromNetwork() { if (!this->getIContextNetwork()) { this->sendStatusMessage(CStatusMessage::getInfoMessage("Cannot load flight plan, network not available", CStatusMessage::TypeTrafficNetwork)); return; } if (!this->getIContextNetwork()->isConnected()) { this->sendStatusMessage(CStatusMessage::getWarningMessage("Cannot load flight plan, network not connected", CStatusMessage::TypeTrafficNetwork)); return; } CAircraft ownAircraft = this->getIContextOwnAircraft()->getOwnAircraft(); CFlightPlan loadedPlan = this->getIContextNetwork()->loadFlightPlanFromNetwork(ownAircraft.getCallsign()); if (loadedPlan.wasSentOrLoaded()) { this->fillWithFlightPlanData(loadedPlan); this->sendStatusMessage(CStatusMessage::getInfoMessage("Updated with loaded flight plan", CStatusMessage::TypeTrafficNetwork)); } else { this->sendStatusMessage(CStatusMessage::getWarningMessage("No flight plan data", CStatusMessage::TypeTrafficNetwork)); } } void CFlightPlanComponent::buildRemarkString() { QString rem; QString v = this->ui->cb_VoiceCapabilities->currentText().toUpper(); if (v.contains("TEXT")) rem.append("/T/ "); else if (v.contains("VOICE")) rem.append("/V/ "); else if (v.contains("RECEIVE")) rem.append("/R/ "); v = this->ui->le_AirlineOperator->text().trimmed(); if (!v.isEmpty()) rem.append("OPR/").append(v).append(" "); v = this->ui->le_AircraftRegistration->text().trimmed(); if (!v.isEmpty()) rem.append("REG/").append(v).append(" "); v = this->ui->cb_PilotRating->currentText().toUpper(); if (v.contains("P1")) rem.append("PR/P1 "); else if (v.contains("P2")) rem.append("PR/P2 "); else if (v.contains("P3")) rem.append("PR/P3 "); else if (v.contains("P4")) rem.append("PR/P4 "); else if (v.contains("P5")) rem.append("PR/P5 "); v = this->ui->cb_RequiredNavigationPerformance->currentText().toUpper(); if (v.contains("10")) rem.append("RNP10 "); else if (v.contains("4")) rem.append("RNP4 "); v = this->ui->cb_NavigationEquipment->currentText().toUpper(); if (v.contains("VORS")) rem.append("NAV/VORNDB "); else if (v.contains("SIDS")) rem.append("NAV/GPSRNAV "); if (v.contains("DEFAULT")) rem.append("NAV/GPS "); else if (v.contains("OCEANIC")) rem.append("NAV/GPSOCEANIC "); v = this->ui->cb_PerformanceCategory->currentText().toUpper(); if (v.startsWith("A")) rem.append("PER/A "); else if (v.startsWith("B")) rem.append("PER/B "); else if (v.startsWith("C")) rem.append("PER/C "); else if (v.startsWith("D")) rem.append("PER/D "); else if (v.startsWith("E")) rem.append("PER/E "); if (this->ui->frp_SelcalCode->hasValidCode()) { rem.append("SEL/").append(this->ui->frp_SelcalCode->getSelcalCode()); rem.append(" "); } if (this->ui->cb_NoSidsStarts->isChecked()) rem.append("NO SID/STAR "); v = this->ui->pte_AdditionalRemarks->toPlainText().trimmed(); if (!v.isEmpty()) rem.append(v); rem = rem.simplified().trimmed(); this->ui->pte_RemarksGenerated->setPlainText(rem); } void CFlightPlanComponent::copyRemarks() { this->ui->pte_Remarks->setPlainText(this->ui->pte_RemarksGenerated->toPlainText()); this->sendStatusMessage(CStatusMessage::getInfoMessage("Copied remarks", CStatusMessage::TypeTrafficNetwork)); } void CFlightPlanComponent::currentTabGenerator() { this->setCurrentWidget(this->ui->tb_RemarksGenerator); } }