Files
pilotclient/src/gui/components/dbautostashingcomponent.cpp
2025-11-01 16:15:12 +01:00

356 lines
13 KiB
C++

// SPDX-FileCopyrightText: Copyright (C) 2016 swift Project Community / Contributors
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
#include "gui/components/dbautostashingcomponent.h"
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QFlags>
#include <QIntValidator>
#include <QLineEdit>
#include <QProgressBar>
#include <QRadioButton>
#include <QString>
#include <QStringBuilder>
#include <QWidget>
#include <Qt>
#include <QtGlobal>
#include "ui_dbautostashingcomponent.h"
#include "core/webdataservices.h"
#include "gui/components/dbmappingcomponent.h"
#include "gui/components/dbstashcomponent.h"
#include "gui/guiapplication.h"
#include "gui/views/aircraftmodelview.h"
#include "gui/views/statusmessageview.h"
#include "gui/views/viewbase.h"
#include "misc/aviation/livery.h"
#include "misc/logcategories.h"
#include "misc/sequence.h"
#include "misc/simulation/aircraftmodel.h"
#include "misc/simulation/aircraftmodellist.h"
#include "misc/statusmessagelist.h"
using namespace swift::core;
using namespace swift::misc;
using namespace swift::misc::aviation;
using namespace swift::misc::network;
using namespace swift::misc::simulation;
using namespace swift::gui::views;
namespace swift::gui::components
{
const QStringList &CDbAutoStashingComponent::getLogCategories()
{
static const QStringList cats { CLogCategories::mapping(), CLogCategories::guiComponent() };
return cats;
}
CDbAutoStashingComponent::CDbAutoStashingComponent(QWidget *parent)
: QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint),
CDbMappingComponentAware(qobject_cast<CDbMappingComponent *>(parent)), ui(new Ui::CDbAutoStashingComponent)
{
ui->setupUi(this);
ui->tvp_StatusMessages->setResizeMode(CAircraftModelView::ResizingAuto);
ui->tvp_StatusMessages->menuAddItems(CAircraftModelView::MenuSave);
ui->le_MaxModelsStashed->setValidator(new QIntValidator(10, CDbStashComponent::MaxModelPublished, this));
Q_ASSERT_X(this->getMappingComponent(), Q_FUNC_INFO, "Expect mapping componet");
connect(ui->tb_ResetDescription, &QToolButton::clicked, this, &CDbAutoStashingComponent::resetDescription);
this->resetDescription();
}
CDbAutoStashingComponent::~CDbAutoStashingComponent() = default;
void CDbAutoStashingComponent::accept()
{
switch (m_state)
{
case Running: return;
case Completed:
{
if (!m_modelsToStash.isEmpty())
{
// this removes previously stashed models
this->getMappingComponent()->replaceStashedModelsUnvalidated(m_modelsToStash);
if (ui->cb_RemovedChecked->isChecked())
{
this->currentModelView()->removeModelsWithModelString(m_modelsToStash);
}
const CStatusMessage stashedMsg(this, CStatusMessage::SeverityInfo,
QStringLiteral("Auto stashed %1 models").arg(m_modelsToStash.size()));
this->addStatusMessage(stashedMsg);
m_modelsToStash.clear();
}
QDialog::accept();
break;
}
default:
{
if (this->getSelectedOrAllCount() < 1)
{
const CStatusMessage m(this, CStatusMessage::SeverityError, u"No data, nothing to do");
this->addStatusMessage(m);
QDialog::accept();
}
this->tryToStashModels();
}
}
}
int CDbAutoStashingComponent::exec()
{
this->initGui();
return QDialog::exec();
}
void CDbAutoStashingComponent::showLastResults()
{
ui->bb_AutoStashing->setStandardButtons(QDialogButtonBox::Close);
this->setVisible(true);
}
void CDbAutoStashingComponent::resetDescription()
{
ui->rb_DescriptionEmptyOnly->setChecked(true);
ui->le_Description->setText(CAircraftModel::autoGenerated());
}
void CDbAutoStashingComponent::initGui()
{
m_state = Idle;
ui->bb_AutoStashing->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
ui->tvp_StatusMessages->clear();
m_noData = 0;
m_noValidationFailed = 0;
m_noStashed = 0;
this->updateProgressIndicator(0);
if (!this->currentModelView())
{
const CStatusMessage m(this, CStatusMessage::SeverityError, u"No data for auto stashing");
this->addStatusMessage(m);
}
else
{
int selected = this->currentModelView()->selectedRowCount();
int all = this->currentModelView()->rowCount();
ui->le_Selected->setText(QString::number(selected));
QString allStr(QString::number(all));
if (all > CDbStashComponent::MaxModelPublished)
{
allStr += u" (Max." % QString::number(CDbStashComponent::MaxModelPublished) % u")";
}
ui->le_All->setText(allStr);
if (ui->le_MaxModelsStashed->text().isEmpty())
{
ui->le_MaxModelsStashed->setText(all > 100 ? QStringLiteral("100") : QString());
}
if (selected > 0)
{
ui->rb_Selected->setChecked(true);
ui->rb_Selected->setEnabled(true);
}
else
{
ui->rb_All->setChecked(true);
ui->rb_Selected->setEnabled(false);
}
}
}
void CDbAutoStashingComponent::updateProgressIndicator(int percent)
{
percent = std::clamp(percent, 0, 100);
ui->pb_StashingProgress->setValue(percent);
ui->le_Stashed->setText(QString::number(m_noStashed));
ui->le_NoData->setText(QString::number(m_noData));
ui->le_ValidationFailed->setText(QString::number(m_noValidationFailed));
}
int CDbAutoStashingComponent::getSelectedOrAllCount() const
{
if (!this->currentModelView()) { return 0; }
if (ui->rb_Selected->isChecked()) { return this->currentModelView()->selectedRowCount(); }
else { return this->currentModelView()->rowCount(); }
}
CAircraftModelView *CDbAutoStashingComponent::currentModelView() const
{
return this->getMappingComponent()->currentModelView();
}
void CDbAutoStashingComponent::addStatusMessage(const CStatusMessage &msg)
{
if (msg.isEmpty()) { return; }
ui->tvp_StatusMessages->insert(msg);
}
void CDbAutoStashingComponent::addStatusMessage(const CStatusMessage &msg, const CAircraftModel &model)
{
if (msg.isEmpty()) { return; }
if (model.hasModelString())
{
CStatusMessage prefixMessage(msg);
prefixMessage.prependMessage(QString(model.getModelString() + ", " + model.getMembersDbStatus() + ": "));
ui->tvp_StatusMessages->insert(prefixMessage);
}
else { ui->tvp_StatusMessages->insert(msg); }
}
void CDbAutoStashingComponent::tryToStashModels()
{
Q_ASSERT_X(this->currentModelView(), Q_FUNC_INFO, "No view");
const CAircraftModelList models(ui->rb_Selected->isChecked() ?
this->currentModelView()->selectedObjects() :
this->currentModelView()->containerOrFilteredContainer());
if (models.isEmpty()) { return; }
if (!sGui || sGui->isShuttingDown()) { return; }
// we have data and are good to go
m_state = Running;
const int all = models.size();
// maximum
int max = CDbStashComponent::MaxModelPublished;
const QString maxStr(ui->le_MaxModelsStashed->text());
bool okMaxStr = true;
if (!maxStr.isEmpty()) { max = maxStr.toInt(&okMaxStr); }
if (!okMaxStr || max > all) { max = all; }
// override description
const QString description(ui->le_Description->text().trimmed());
// temp livery if applicable
const CLivery tempLivery(ui->cb_UseTempLivery->isChecked() ? getTempLivery() : CLivery());
int c = 0;
CAircraftModelList autoStashed;
for (const CAircraftModel &model : models)
{
CAircraftModel stashModel(model);
const bool stashed = this->tryToStashModel(stashModel, tempLivery);
if (stashed)
{
if (!description.isEmpty()) { this->setModelDescription(stashModel, description); }
autoStashed.push_back(stashModel);
}
c++;
if (c % 25 == 0)
{
Q_ASSERT_X(c <= all, Q_FUNC_INFO, "illegal numbers");
sGui->processEventsToRefreshGui();
int percent = c * 100 / all;
if (max < all)
{
int maxPercent = autoStashed.size() * 100 / max;
percent = std::max(maxPercent, percent);
}
this->updateProgressIndicator(percent);
}
if (autoStashed.size() >= max) { break; }
}
this->updateProgressIndicator(100);
sGui->processEventsToRefreshGui();
const CStatusMessage stashedMsg(this, CStatusMessage::SeverityInfo,
QStringLiteral("Ready to auto stash %1 models").arg(autoStashed.size()));
this->addStatusMessage(stashedMsg);
m_modelsToStash = autoStashed;
m_state = Completed;
}
bool CDbAutoStashingComponent::tryToStashModel(CAircraftModel &model, const CLivery &tempLivery)
{
const bool useTempLivery = tempLivery.isLoadedFromDb();
// no airline and no livery, here replaced by temp livery
if (useTempLivery && !model.hasAirlineDesignator() && !model.getLivery().hasValidDbKey())
{
model.setLivery(tempLivery);
}
//! Some upfront tests
if (!model.hasModelString())
{
this->addStatusMessage(CStatusMessage(this, CStatusMessage::SeverityError, u"No model string"));
m_noData++;
return false;
}
if (!model.hasAircraftDesignator())
{
this->addStatusMessage(CStatusMessage(this, CStatusMessage::SeverityError, u"No aircraft designator"),
model);
m_noData++;
return false;
}
if (!model.hasAirlineDesignator() && !model.getLivery().hasValidDbKey())
{
// no valid airline and NO DB livery
// we try one fallback
const QString liveryCombinedCode = model.getLivery().getCombinedCode();
bool fallback = false;
if (liveryCombinedCode.length() == 3 || liveryCombinedCode.length() == 4)
{
// could we use the combined code as airline
if (CAirlineIcaoCode::isValidAirlineDesignator(liveryCombinedCode))
{
model.setAirlineIcaoDesignator(liveryCombinedCode);
fallback = true;
}
}
// if there is no livery (normal) we need an airline
if (!fallback)
{
this->addStatusMessage(CStatusMessage(this, CStatusMessage::SeverityError, u"No airline designator"),
model);
m_noData++;
return false;
}
}
// stash here consolidates with DB data and validates
bool stashed = false;
const CAircraftModel stashModel(this->getMappingComponent()->consolidateModel(model));
CStatusMessageList validationMsgs(stashModel.validate(true));
validationMsgs.removeWarningsAndBelow();
CStatusMessage msg = validationMsgs.toSingleMessage();
if (msg.getSeverity() == CStatusMessage::SeverityError) { m_noValidationFailed++; }
else
{
msg = CStatusMessage(this, CStatusMessage::SeverityInfo, u"Stashed succesfully");
stashed = true;
m_noStashed++;
model = stashModel;
}
this->addStatusMessage(msg, stashModel);
return stashed;
}
void CDbAutoStashingComponent::setModelDescription(CAircraftModel &model, const QString &description) const
{
if (description.isEmpty()) { return; }
if (ui->rb_All->isChecked()) { model.setDescription(description); }
else
{
// only for "empty" ones
if (model.hasDescription(true)) { return; }
model.setDescription(description);
}
}
swift::misc::aviation::CLivery CDbAutoStashingComponent::getTempLivery()
{
if (!sGui || !sGui->hasWebDataServices()) { return {}; }
return sGui->getWebDataServices()->getTempLiveryOrDefault();
}
} // namespace swift::gui::components