881 lines
35 KiB
C++
Executable File
881 lines
35 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* AreaEffectSimulation - Explosion Edit Dialog
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
* Created by Patric Karwat, SLSE, 2016-07-05
|
|
*/
|
|
|
|
|
|
#include "aes_explosion_edit.h"
|
|
#include "ui_aes_explosion_edit.h"
|
|
#include <LtsCoordTrans/lts_coord_trans.h>
|
|
|
|
#include <LtsSession/lts_session_data.h>
|
|
#include <Plugins/AreaEffectSimulation/aes_data_plugin_interface.h>
|
|
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
|
|
#include <PluginInterfaces/gui_plugin_interface.h>
|
|
#include <Services/AreaEffectSimulation/Utils/polygons.h>
|
|
#include <QJsonDocument>
|
|
#include <LtsUtilsSettings/settings.h>
|
|
|
|
#include <Plugins/AdvancedSymbology/asb_data_plugin_interface.h>
|
|
#include <Plugins/ParticipantManagement/Gui/ParticipantSelection/participant_selection.h>
|
|
|
|
#include <QSpinBox>
|
|
|
|
namespace AreaEffectSimulation
|
|
{
|
|
double ExplosionEdit::s_minWidth;
|
|
double ExplosionEdit::s_maxWidth;
|
|
double ExplosionEdit::s_stepWidth;
|
|
double ExplosionEdit::s_minHeight;
|
|
double ExplosionEdit::s_maxHeight;
|
|
double ExplosionEdit::s_stepHeight;
|
|
double ExplosionEdit::s_minRadius;
|
|
double ExplosionEdit::s_maxRadius;
|
|
double ExplosionEdit::s_stepRadius;
|
|
int ExplosionEdit::s_maxChargeUsage;
|
|
double ExplosionEdit::s_minAltitude;
|
|
double ExplosionEdit::s_maxAltitude;
|
|
qint64 ExplosionEdit::s_maxDurationSeconds;
|
|
static int s_lastChargeTypeA = 0;
|
|
static int s_lastChargeTypeM = 0;
|
|
|
|
ExplosionEdit::ExplosionEdit(const QString &menuGroup, ExplosionType type, ProjectDataManagement::GearClass gc, QWidget *parent)
|
|
: BaseEdit(menuGroup, QLatin1String("ExplosionEdit"), parent)
|
|
, m_type(type)
|
|
, m_editable(false)
|
|
, m_gearClass(gc)
|
|
, m_previewCircleRadius(0.0)
|
|
, m_ui(new Ui::ExplosionEdit)
|
|
{
|
|
m_ui->setupUi(this);
|
|
|
|
// Todo: Get these values
|
|
|
|
s_minWidth = 5;
|
|
s_maxWidth = 1275;
|
|
s_stepWidth = 5;
|
|
s_minHeight = 5;
|
|
s_maxHeight = 1275;
|
|
s_minRadius = 5;
|
|
s_maxRadius = 255;
|
|
s_stepRadius = 1;
|
|
s_stepHeight = 5;
|
|
s_minAltitude = 0;
|
|
s_maxAltitude = 1000;
|
|
s_maxChargeUsage = 1000;
|
|
s_maxDurationSeconds = 1800; // 30 Minutes
|
|
|
|
m_ui->sbDimensionX->setMinimum(s_minWidth);
|
|
m_ui->sbDimensionX->setMaximum(s_maxWidth);
|
|
m_ui->sbDimensionX->setSingleStep(s_stepWidth);
|
|
m_ui->sbDimensionY->setMinimum(s_minHeight);
|
|
m_ui->sbDimensionY->setMaximum(s_maxHeight);
|
|
m_ui->sbDimensionY->setSingleStep(s_stepHeight);
|
|
m_ui->sbAltitude->setMinimum(s_minAltitude);
|
|
m_ui->sbAltitude->setMaximum(s_maxAltitude);
|
|
m_ui->sbCannons->setMinimum(1);
|
|
m_ui->sbCannons->setMaximum(s_maxChargeUsage);
|
|
m_ui->sbRounds->setMinimum(1);
|
|
m_ui->sbRounds->setMaximum(s_maxChargeUsage);
|
|
m_ui->sbDurationSeconds->setMinimum(1);
|
|
m_ui->sbDurationSeconds->setMaximum(s_maxDurationSeconds);
|
|
m_ui->sbAimingAxis->setMaximum(6400);
|
|
|
|
m_ui->lblDesignator->clear();
|
|
m_ui->lblIcon->clear();
|
|
m_ui->lblTactIdentifier->clear();
|
|
|
|
|
|
m_uiRequirements->addRequirement({m_ui->lblPosition, [this]() { return (!m_ui->positionEdit->coordinate().isNull()); }});
|
|
m_uiRequirements->addRequirement({m_ui->lblName, [this]() { return (!m_ui->leIdentifier->text().isEmpty()); }});
|
|
m_uiRequirements->addRequirement({m_ui->lblChargeType, [this]() { return (!m_ui->cbChargeType->currentData().isNull()); }});
|
|
// either no effect start is set or effect start is greater than request time
|
|
m_uiRequirements->addRequirement({m_ui->lblEffectStart, [this]()
|
|
{
|
|
return (hasValidEffectStart(m_ui->dteEffectStart) ||
|
|
m_ui->dteEffectStart->msecsSinceEpoch() >= m_ui->dteRequestTime->msecsSinceEpoch());
|
|
}});
|
|
|
|
if (m_type != etArea)
|
|
{
|
|
m_ui->lblAimingAxis->hide();
|
|
m_ui->sbAimingAxis->hide();
|
|
|
|
m_ui->lblDuration->hide();
|
|
m_ui->sbDurationSeconds->hide();
|
|
|
|
m_ui->sbDimensionX->setEnabled(false);
|
|
m_ui->lblOperatorA->hide();
|
|
m_ui->sbDimensionY->hide();
|
|
|
|
m_ui->sbDimensionX->setMinimum(s_minRadius);
|
|
m_ui->sbDimensionX->setMaximum(s_maxRadius);
|
|
m_ui->sbDimensionX->setSingleStep(s_stepRadius);
|
|
|
|
m_ui->sbCannons->setEnabled(false);
|
|
m_ui->sbCannons->hide();
|
|
m_ui->lblOperatorB->hide();
|
|
m_ui->sbRounds->setEnabled(false);
|
|
}
|
|
else
|
|
{
|
|
m_ui->sbDurationSeconds->setValue(60);
|
|
m_ui->sbCannons->setValue(4);
|
|
m_ui->sbRounds->setValue(9);
|
|
}
|
|
|
|
if (m_type != etSingleCentral)
|
|
{
|
|
m_ui->lbxFiringAngle->hide();
|
|
m_ui->sbFiringAngle->hide();
|
|
m_ui->lbxIgniterType->hide();
|
|
m_ui->cbIgniterType->hide();
|
|
m_ui->lbxParticipant->hide();
|
|
m_ui->lblDesignator->hide();
|
|
m_ui->lblIcon->hide();
|
|
m_ui->lblTactIdentifier->hide();
|
|
m_ui->pbParticipant->hide();
|
|
m_ui->cbParty->setEnabled(false);
|
|
}
|
|
else
|
|
m_uiRequirements->addRequirement({m_ui->lbxParticipant, [this]() { return (m_participant.id() != 0); }});
|
|
|
|
enaDisa();
|
|
|
|
connect(m_ui->cbParty, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
this, &ExplosionEdit::onPartyChanged);
|
|
connect(m_ui->cbChargeType, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
this, &ExplosionEdit::onChargeTypeChanged);
|
|
|
|
connect(m_ui->sbDimensionX, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue);
|
|
connect(m_ui->sbDimensionY, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue);
|
|
connect(m_ui->sbAltitude, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue);
|
|
|
|
// Not yet needed (no singleStep defined)
|
|
// connect(m_ui->sbAimingAxis, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue);
|
|
// connect(m_ui->sbCannons, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue);
|
|
// connect(m_ui->sbRounds, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue);
|
|
// connect(m_ui->sbDurationSeconds, &QSpinBox::editingFinished, this, &ExplosionEdit::correctSpinBoxValue);
|
|
|
|
connect(m_ui->sbCannons, QOverload<int>::of(&QSpinBox::valueChanged),
|
|
this, [this](int val) { onCannonsChanged(val); });
|
|
connect(m_ui->sbRounds, QOverload<int>::of(&QSpinBox::valueChanged),
|
|
this, [this](int val) { onRoundsChanged(val); });
|
|
connect(m_ui->pbFire, &QPushButton::clicked,
|
|
this, &ExplosionEdit::onFireClicked);
|
|
|
|
connect(m_ui->pbClear, &QPushButton::clicked,
|
|
m_ui->positionEdit, &LtsCoordWidget::LtsCoordWidget::clear);
|
|
|
|
connect(m_ui->pbParticipant, &QPushButton::clicked,
|
|
this, &ExplosionEdit::onParticipantClicked);
|
|
|
|
m_symbolWatcher = new AdvancedSymbology::SymbolResultWatcherLabel(m_ui->lblIcon);
|
|
}
|
|
|
|
ExplosionEdit::~ExplosionEdit()
|
|
{
|
|
delete m_ui;
|
|
}
|
|
|
|
void ExplosionEdit::retranslate()
|
|
{
|
|
QString title;
|
|
|
|
switch (m_gearClass.underlying())
|
|
{
|
|
case ProjectDataManagement::GearClass::Artillery:
|
|
if (m_type == etSingle)
|
|
title = tr("Single Artillery");
|
|
else if (m_type == etArea)
|
|
title = tr("Area Artillery");
|
|
else
|
|
title = tr ("Single Artillery Central");
|
|
break;
|
|
case ProjectDataManagement::GearClass::Mortar:
|
|
if (m_type == etSingle)
|
|
title = tr("Single Mortar");
|
|
else if (m_type == etArea)
|
|
title = tr("Area Mortar");
|
|
else
|
|
title = tr ("Single Mortar Central");
|
|
break;
|
|
default:
|
|
Q_ASSERT(false);
|
|
}
|
|
translate(m_ui, m_aesItem, title);
|
|
|
|
if (m_type != etArea)
|
|
{
|
|
m_ui->sbCannons->setValue(1);
|
|
m_ui->sbRounds->setValue(1);
|
|
m_ui->lblRounds->setText(tr("Rounds:"));
|
|
m_ui->lblDimension->setText(tr("Diameter [m]:"));
|
|
}
|
|
else
|
|
{
|
|
m_ui->lblRounds->setText(tr("Cannons x Rounds:"));
|
|
m_ui->lblDimension->setText(tr("Dimension [m]:"));
|
|
}
|
|
|
|
setFireButton(m_aesItem.effectStateId());
|
|
|
|
updateMaxLabel(false);
|
|
}
|
|
|
|
void ExplosionEdit::onCannonsChanged(int cannons, bool modifiedFromExternal)
|
|
{
|
|
updateMaxLabel(modifiedFromExternal);
|
|
if (cannons == 0 || maxAvailableRounds() == 0)
|
|
return;
|
|
|
|
m_ui->sbRounds->setMaximum(maxAvailableRounds() / cannons);
|
|
updateMaxLabel(modifiedFromExternal);
|
|
}
|
|
|
|
void ExplosionEdit::onRoundsChanged(int rounds, bool modifiedFromExternal)
|
|
{
|
|
updateMaxLabel(modifiedFromExternal);
|
|
if (rounds == 0 || maxAvailableRounds() == 0)
|
|
return;
|
|
|
|
m_ui->sbCannons->setMaximum(maxAvailableRounds() / rounds);
|
|
updateMaxLabel(modifiedFromExternal);
|
|
}
|
|
|
|
void ExplosionEdit::enableUiElements(bool enable)
|
|
{
|
|
ERROR_LOG(m_logger) << " enableUiElements enable=" << enable;
|
|
|
|
m_ui->leIdentifier->setMaxLength(30);
|
|
|
|
m_ui->cbChargeType->setEnabled(enable);
|
|
m_ui->sbDurationSeconds->setEnabled(enable);
|
|
m_ui->cbParty->setEnabled(enable && m_type != etSingleCentral); // Set via Participant
|
|
setEffectStartEnabled(m_ui->dteEffectStart, enable);
|
|
|
|
m_ui->dteRequestTime->setEnabled(enable);
|
|
m_ui->pbPosition->setEnabled(m_sitMapGuiIfc && enable);
|
|
m_ui->positionEdit->setEnabled(enable);
|
|
m_ui->sbAltitude->setEnabled(enable);
|
|
m_ui->sbDimensionX->setEnabled(enable && m_type == etArea);
|
|
m_ui->sbDimensionY->setEnabled(enable);
|
|
m_ui->sbAimingAxis->setEnabled(enable);
|
|
m_ui->cbPurpose->setEnabled(enable);
|
|
m_ui->sbCannons->setEnabled(enable && m_type == etArea);
|
|
m_ui->sbRounds->setEnabled(enable && m_type == etArea);
|
|
m_ui->cbEffectMarkingInTa->setEnabled(enable);
|
|
m_ui->leIdentifier->setEnabled(enable || isOnlyNameModifiable());
|
|
m_ui->pbClear->setEnabled(enable);
|
|
|
|
m_ui->sbFiringAngle->setEnabled(enable);
|
|
m_ui->pbParticipant->setEnabled(enable);
|
|
m_ui->cbIgniterType->setEnabled(enable);
|
|
|
|
if (!enable)
|
|
{
|
|
bool hasChanges = hasUnsavedChanges();
|
|
m_ui->sbDurationSeconds->setValue(m_aesItem.effectDuration() / 1000); // ms -> s
|
|
if (!hasChanges)
|
|
__unchange__();
|
|
}
|
|
|
|
setFireButton(m_aesItem.effectStateId());
|
|
if (m_aesItem.effectStateId() == AesEffectState_def::EsExpired)
|
|
{
|
|
m_ui->leIdentifier->setText(QString::fromStdString(m_aesItem.name()));
|
|
__unchange__();
|
|
}
|
|
if (m_aesItem.effectStateId() == AesEffectState_def::EsActivated)
|
|
{
|
|
QString name = m_ui->leIdentifier->text();
|
|
AesItem baseData;
|
|
if (m_type == etSingle)
|
|
{
|
|
ExplosionSingle singleData = m_dtc->explosionSingle(m_aesItem.id());
|
|
setSingleData(singleData);
|
|
baseData = singleData;
|
|
}
|
|
else if (m_type == etArea)
|
|
{
|
|
ExplosionArea areaData = m_dtc->explosionArea(m_aesItem.id());
|
|
setAreaData(areaData);
|
|
baseData = areaData;
|
|
}
|
|
setBaseData(baseData, false);
|
|
setFireButton(baseData.effectStateId());
|
|
__unchange__();
|
|
if (name != m_ui->leIdentifier->text())
|
|
m_ui->leIdentifier->setText(name);
|
|
}
|
|
if (m_aesItem.effectStateId() == AesEffectState_def::EsPaused)
|
|
__unchange__();
|
|
}
|
|
|
|
int ExplosionEdit::maxAvailableRounds() const
|
|
{
|
|
int mapMax = m_chargeTypeMap[m_ui->cbChargeType->currentData().toInt()].available[m_ui->cbParty->currentData().toInt()];
|
|
return mapMax > s_maxChargeUsage ? s_maxChargeUsage : mapMax;
|
|
}
|
|
|
|
void ExplosionEdit::setBaseData(const AesItem &baseData, bool copy)
|
|
{
|
|
m_aesItem = baseData;
|
|
if (copy)
|
|
{
|
|
m_aesItem.id(0);
|
|
m_aesItem.effectStateId(AreaEffectSimulation::AesEffectState_def::get_default());
|
|
}
|
|
m_ui->leIdentifier->setText(QString::fromUtf8(baseData.name().c_str()));
|
|
int idx = m_ui->cbChargeType->findData(baseData.chargeStruct().chargeTypeId());
|
|
if (idx < 0)
|
|
{
|
|
auto ct = m_chargeTypeMap[baseData.chargeStruct().chargeTypeId()].chargeType;
|
|
m_ui->cbChargeType->addItem(m_pdmDtc->trChargeTypeShortName(ct.id(), true), ct.id());
|
|
idx = m_ui->cbChargeType->findData(ct.id());
|
|
}
|
|
m_ui->cbChargeType->setCurrentIndex(idx);
|
|
m_ui->dteEffectStart->setMsecsSinceEpoch(baseData.effectStart().is_set() ? baseData.effectStart().get() : 0);
|
|
m_ui->sbDurationSeconds->setValue(baseData.effectDuration() / 1000); // ms -> s
|
|
|
|
m_ui->cbParty->setCurrentIndex(m_ui->cbParty->findData(partyForItem(baseData)));
|
|
m_ui->cbEffectMarkingInTa->setChecked(baseData.effectMarkingInTa());
|
|
m_editable = m_editable && isModifiable() && !isOnlyNameModifiable() && m_session->app() != LtsAppDescriptor::App::OC;
|
|
|
|
}
|
|
|
|
bool ExplosionEdit::init(int aesItemId, bool copy,
|
|
LtsSession::LtsSession *session)
|
|
{
|
|
if (!BaseEdit::init(aesItemId, session))
|
|
return false;
|
|
|
|
if (!m_session->getForeignDataInterface<AdvancedSymbology::DataInterface>(m_asbIfc))
|
|
return false;
|
|
|
|
if (!m_session->getDtc<ParticipantManagement::DataInterface>(m_ppmDtc))
|
|
{
|
|
ERROR_LOG(m_logger) << " failed to get ppmDtc";
|
|
return false;
|
|
}
|
|
|
|
if (!m_session->getForeignGuiInterface<ProjectDataManagement::ForeignGuiInterface>(m_pdmCbx))
|
|
return false;
|
|
|
|
bool readable = false;
|
|
switch (m_gearClass.underlying())
|
|
{
|
|
case ProjectDataManagement::GearClass::Artillery:
|
|
m_editable = (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsEditArtillery) && m_type == etSingle) ||
|
|
(LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::AreaExplosionsEditArtillery) && m_type == etArea) ||
|
|
(LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsCentralEditArtillery) && m_type == etSingleCentral) ||
|
|
(LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::ExplosionEditCluster) && m_type == etArea) ;
|
|
readable = (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsShowArtillery) && m_type == etSingle) ||
|
|
(LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::AreaExplosionsShowArtillery) && m_type == etArea) ||
|
|
(LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsCentralEditArtillery) && m_type == etSingleCentral) ||
|
|
(LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::ExplosionShowCluster) && m_type == etArea);
|
|
if (m_type == etSingle) // Single Artillery
|
|
setOnlineHelpFilename(QStringLiteral("Situation-Add_Single_Artillery"));
|
|
else if (m_type == etArea) // Area Artillery
|
|
setOnlineHelpFilename(QStringLiteral("Situation-Add_Area_Artillery"));
|
|
else if (m_type == etSingleCentral) // Single Central Artillery
|
|
setOnlineHelpFilename(QStringLiteral("Situation-Add_Single_Central_Artillery"));
|
|
break;
|
|
case ProjectDataManagement::GearClass::Mortar:
|
|
m_editable = (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsEditMortar) && m_type == etSingle) ||
|
|
(LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::AreaExplosionsEditMortar) && m_type == etArea) ||
|
|
(LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsCentralEditMortar) && m_type == etSingleCentral) ;
|
|
readable = (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsShowMortar) && m_type == etSingle) ||
|
|
(LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::AreaExplosionsShowMortar) && m_type == etArea) ||
|
|
(LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SingleExplosionsCentralShowMortar) && m_type == etSingleCentral) ;
|
|
if (m_type == etSingle) // Single Artillery
|
|
setOnlineHelpFilename(QStringLiteral("Situation-Add_Single_Mortar"));
|
|
else if (m_type == etArea) // Area Artillery
|
|
setOnlineHelpFilename(QStringLiteral("Situation-Add_Area_Mortar"));
|
|
else if (m_type == etSingleCentral) // Single Central Artillery
|
|
setOnlineHelpFilename(QStringLiteral("Situation-Add_Single_Central_Mortar"));
|
|
break;
|
|
default:
|
|
Q_ASSERT(false);
|
|
break;
|
|
}
|
|
|
|
if (!readable && !m_editable)
|
|
return false;
|
|
|
|
if (!m_session->getDtc<ProjectDataManagement::DataInterface>(m_pdmDtc))
|
|
return false;
|
|
|
|
if (!m_session->getDtc<ParticipantManagement::DataInterface>(m_ppmDtc))
|
|
return false;
|
|
|
|
m_ui->dteEffectStart->setMinimumTimeMSec(m_session->partitionTime());
|
|
m_ui->dteEffectStart->setRetrieveCurrentTimeCallback([this]() { return m_session->partitionTime(); });
|
|
m_ui->dteRequestTime->setRetrieveCurrentTimeCallback([this]() { return m_session->partitionTime(); });
|
|
|
|
LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance();
|
|
const bool enableEffectShowManagement = ps->value(QStringLiteral("esm"),
|
|
QStringLiteral("enableEffectShowManagement")).toBool();
|
|
m_ui->cbEffectMarkingInTa->setVisible(enableEffectShowManagement &&
|
|
((m_session->app() == LtsAppDescriptor::App::Situation ||
|
|
m_session->app() == LtsAppDescriptor::App::SituationTablet) &&
|
|
(!m_session->isReplayPartition())));
|
|
|
|
if (m_sitMapGuiIfc)
|
|
connect(m_ui->pbPosition, &QPushButton::clicked, this, &ExplosionEdit::getPosition);
|
|
else
|
|
m_ui->pbPosition->setEnabled(false);
|
|
|
|
const auto firePurposes = m_pdmDtc->firePurposes();
|
|
for (const auto &firePurpose : firePurposes)
|
|
m_ui->cbPurpose->addItem(m_pdmDtc->trFirePurposeName(firePurpose.id()), firePurpose.id());
|
|
|
|
const auto chargesForGearClass =
|
|
m_pdmDtc->chargesForGearClass(m_gearClass);
|
|
for (const ProjectDataManagement::ChargeType &chargeType : chargesForGearClass)
|
|
m_chargeTypeMap[chargeType.id()].chargeType = chargeType;
|
|
const auto chargeAdmins = m_dtc->chargeAdministrations();
|
|
for (const ChargeAdministration &val : chargeAdmins)
|
|
{
|
|
if (!m_chargeTypeMap.contains(val.chargeTypeId()))
|
|
continue;
|
|
m_chargeTypeMap[val.chargeTypeId()].available.insert(val.partyId(), val.amount() - val.planned());
|
|
}
|
|
|
|
m_ppmGuiCbxIfc->fillPartyComboBox(m_ui->cbParty);
|
|
onPartyChanged();
|
|
|
|
if (aesItemId > 0)
|
|
{
|
|
AesItem baseData;
|
|
if (m_type == etSingle)
|
|
{
|
|
ExplosionSingle singleData = m_dtc->explosionSingle(aesItemId);
|
|
setSingleData(singleData);
|
|
baseData = singleData;
|
|
}
|
|
else if (m_type == etArea)
|
|
{
|
|
ExplosionArea areaData = m_dtc->explosionArea(aesItemId);
|
|
setAreaData(areaData, copy);
|
|
baseData = areaData;
|
|
}
|
|
setBaseData(baseData, copy);
|
|
setFireButton(baseData.effectStateId());
|
|
}
|
|
else
|
|
setFireButton(AreaEffectSimulation::AesEffectState::EsUnknown);
|
|
enableUiElements(m_editable);
|
|
|
|
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::chargeAdministrationChanged,
|
|
this, &ExplosionEdit::onChargeCountChanged);
|
|
|
|
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::explosionAreaChanged,
|
|
this, &BaseEdit::onAreaChanged);
|
|
|
|
onChargeTypeChanged();
|
|
|
|
return true;
|
|
}
|
|
|
|
QString ExplosionEdit::dialogIdentifier() const
|
|
{
|
|
switch (m_gearClass.underlying())
|
|
{
|
|
case ProjectDataManagement::GearClass::Artillery:
|
|
if (m_type == etSingle)
|
|
return m_aesItem.id() <= 0 ? QStringLiteral("AddExplosionSingleArtillery") : QStringLiteral("EditExplosionSingleArtillery%1").arg(m_aesItem.id());
|
|
else if (m_type == etArea)
|
|
return m_aesItem.id() <= 0 ? QStringLiteral("AddExplosionAreaArtillery") : QStringLiteral("EditExplosionAreaArtillery%1").arg(m_aesItem.id());
|
|
else
|
|
return m_aesItem.id() <= 0 ? QStringLiteral("AddExplosionSingleCentralArtillery") : QStringLiteral("EditExplosionSingleCentralArtillery%1").arg(m_aesItem.id());
|
|
case ProjectDataManagement::GearClass::Mortar:
|
|
if (m_type == etSingle)
|
|
return m_aesItem.id() <= 0 ? QStringLiteral("AddExplosionSingleMortar") : QStringLiteral("EditExplosionSingleMortar%1").arg(m_aesItem.id());
|
|
else if (m_type == etArea)
|
|
return m_aesItem.id() <= 0 ? QStringLiteral("AddExplosionAreaMortar") : QStringLiteral("EditExplosionAreaMortar%1").arg(m_aesItem.id());
|
|
else
|
|
return m_aesItem.id() <= 0 ? QStringLiteral("AddExplosionSingleCentralMortar") : QStringLiteral("EditExplosionSingleCentralMortar%1").arg(m_aesItem.id());
|
|
default:
|
|
Q_ASSERT(false);
|
|
break;
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
|
|
void ExplosionEdit::onChargeCountChanged(const AreaEffectSimulation::ChargeAdministration &data, DataModification modType)
|
|
{
|
|
// All relevant Charge Types are in Map. So we can just skip the others.
|
|
if (!m_chargeTypeMap.contains(data.chargeTypeId()))
|
|
return;
|
|
|
|
if (modType == DataModification::eRemoved || data.amount() - data.planned() <= 0)
|
|
m_chargeTypeMap[data.chargeTypeId()].available.remove(data.partyId());
|
|
else
|
|
m_chargeTypeMap[data.chargeTypeId()].available.insert(data.partyId(), data.amount() - data.planned());
|
|
onPartyChanged();
|
|
updateCannonsRounds(true);
|
|
updateMaxLabel(true);
|
|
}
|
|
|
|
void ExplosionEdit::showPreviewObject()
|
|
{
|
|
if (!m_sitMapGuiIfc)
|
|
return;
|
|
|
|
if (m_previewCircleCenter.isNull() && m_previewPolygon.isEmpty())
|
|
return;
|
|
|
|
if (m_previewCircleCenter.isNull())
|
|
m_sitMapGuiIfc->displayPreviewObject(this, m_previewPolygon, true);
|
|
else
|
|
m_sitMapGuiIfc->displayPreviewObject(this, m_previewCircleCenter, m_previewCircleRadius);
|
|
}
|
|
|
|
void ExplosionEdit::setFireButton(AreaEffectSimulation::AesEffectState effectState)
|
|
{
|
|
if (effectState == AesEffectState::EsActivated)
|
|
{
|
|
m_ui->pbFire->setText(tr("Pause"));
|
|
m_ui->pbFire->setIcon(QIcon(QStringLiteral("icon:avm_pause.svg")));
|
|
}
|
|
else
|
|
{
|
|
m_ui->pbFire->setText(tr("Start"));
|
|
m_ui->pbFire->setIcon(QIcon(QStringLiteral("icon:avm_start.svg")));
|
|
}
|
|
|
|
if ((effectState == AesEffectState::EsActivated ||
|
|
effectState == AesEffectState::EsPrepared ||
|
|
effectState == AesEffectState::EsCreated ||
|
|
effectState == AesEffectState::EsPaused)
|
|
&& m_aesItem.id() > 0)
|
|
m_ui->pbFire->setEnabled(true);
|
|
else
|
|
m_ui->pbFire->setEnabled(false);
|
|
}
|
|
|
|
void ExplosionEdit::updateCannonsRounds(bool modifiedFromExternal)
|
|
{
|
|
// If Cannons are already more than availRounds, Reset both to One
|
|
if (m_ui->sbCannons->value() > maxAvailableRounds() && m_editable && m_aesItem.id() <= 0)
|
|
{
|
|
m_ui->sbCannons->setValue(1);
|
|
m_ui->sbRounds->setValue(1);
|
|
}
|
|
// setMaximum on one or both.
|
|
onCannonsChanged(m_ui->sbCannons->value(), modifiedFromExternal);
|
|
onRoundsChanged(m_ui->sbRounds->value(), modifiedFromExternal);
|
|
}
|
|
|
|
void ExplosionEdit::updateMaxLabel(bool modifiedFromExternal)
|
|
{
|
|
QString text = tr("= %1 (avail: %2%3)").arg(m_ui->sbCannons->value() * m_ui->sbRounds->value())
|
|
.arg(maxAvailableRounds() == s_maxChargeUsage ? QLatin1String(">=") : QString())
|
|
.arg(maxAvailableRounds());
|
|
|
|
if (modifiedFromExternal) // Todo: Use palette
|
|
text = QStringLiteral("<span style=\"color:#aa0000;\">%1</span>").arg(text);
|
|
|
|
if (text == m_ui->lblMaxCount->text())
|
|
return;
|
|
|
|
m_ui->lblMaxCount->setText(text);
|
|
m_ui->lblMaxCount->setToolTip(modifiedFromExternal ? tr("Cannons / Rounds potentially modified by external source!") : QString());
|
|
}
|
|
|
|
quint32 ExplosionEdit::partyForItem(const AesItem &aesItem) const
|
|
{
|
|
if (aesItem.participantId().is_set())
|
|
{
|
|
if (m_participant.id() == aesItem.participantId().get())
|
|
return m_participant.partyId();
|
|
else
|
|
return m_ppmDtc->participant(aesItem.participantId().get()).partyId();
|
|
}
|
|
else if (aesItem.partyId().is_set())
|
|
return aesItem.partyId().get();
|
|
|
|
return {};
|
|
}
|
|
|
|
void ExplosionEdit::setSingleData(const ExplosionSingle &singleData)
|
|
{
|
|
QPointF impactPosition(singleData.impactPosition().lon(), singleData.impactPosition().lat());
|
|
if (!impactPosition.isNull())
|
|
m_ui->positionEdit->setCoordinate(impactPosition);
|
|
|
|
m_ui->dteRequestTime->setMsecsSinceEpoch(singleData.fireRequestTime());
|
|
m_ui->cbPurpose->setCurrentText(QString::fromUtf8(singleData.purpose().c_str()));
|
|
m_ui->sbAltitude->setValue(singleData.altitude());
|
|
|
|
if (m_aesItem.id() > 0)
|
|
m_chargeTypeMap[singleData.chargeStruct().chargeTypeId()].available[partyForItem(singleData)] += 1;
|
|
|
|
const double radius = m_pdmDtc->impactRadius(singleData.chargeStruct().chargeTypeId(),singleData.chargeStruct().igniterTypeId(), singleData.chargeStruct().igniterFunctionId());
|
|
m_ui->sbDimensionX->setValue(2 * radius);
|
|
}
|
|
|
|
void ExplosionEdit::setAreaData(const ExplosionArea &areaData, bool copy)
|
|
{
|
|
const Area &area = areaData.area();
|
|
if (!qFuzzyCompare(area.cornerBottomLeft().lon(), 0) ||
|
|
!qFuzzyCompare(area.cornerBottomLeft().lat(), 0))
|
|
{
|
|
QPointF bottomLeft(area.cornerBottomLeft().lon(), area.cornerBottomLeft().lat());
|
|
m_ui->positionEdit->setCoordinate(Polygons::bottomLeft2CenterPoint(bottomLeft, area.width(), area.height(), area.rotation()));
|
|
}
|
|
|
|
m_ui->sbDimensionX->setValue(area.width());
|
|
m_ui->sbDimensionY->setValue(area.height());
|
|
m_ui->sbAimingAxis->setValue(fmod((90.0 + area.rotation()) * 6400 / 360, 6400));
|
|
m_ui->sbCannons->setValue(areaData.cannonCount() == 0 ? 4 : areaData.cannonCount());
|
|
m_ui->sbRounds->setValue(areaData.cannonRounds() == 0 ? 9 : areaData.cannonRounds());
|
|
if (m_aesItem.id() > 0 && !copy)
|
|
{
|
|
if (!m_chargeTypeMap.contains(areaData.chargeStruct().chargeTypeId()))
|
|
ERROR_LOG(m_logger) << "SetAreaData called with a charge Type, that has doesn't match. Id " << areaData.chargeStruct().chargeTypeId() << "is not in m_chargeTypeMap";
|
|
if (!areaData.partyId().is_set())
|
|
ERROR_LOG(m_logger) << "SetAreaData called without a party set. m_chargeTypeMap might be corrupted.";
|
|
m_chargeTypeMap[areaData.chargeStruct().chargeTypeId()].available[areaData.partyId().get()] += areaData.cannonCount() * areaData.cannonRounds();
|
|
}
|
|
|
|
|
|
m_ui->dteRequestTime->setMsecsSinceEpoch(areaData.fireRequestTime());
|
|
m_ui->cbPurpose->setCurrentText(QString::fromUtf8(areaData.purpose().c_str()));
|
|
m_ui->sbAltitude->setValue(areaData.altitude());
|
|
}
|
|
|
|
//slots
|
|
|
|
void ExplosionEdit::getPosition()
|
|
{
|
|
if (!has2DMap())
|
|
return;
|
|
|
|
bool isNull = qFuzzyCompare(m_ui->positionEdit->coordinate().x(), 0) && qFuzzyCompare(m_ui->positionEdit->coordinate().y(), 0);
|
|
|
|
QPointF p(Polygons::centerPoint2BottomLeft(m_ui->positionEdit->coordinate(), m_ui->sbDimensionX->value(), m_ui->sbDimensionY->value(), (m_ui->sbAimingAxis->value() - 1600) / 6400. * 360));
|
|
|
|
if (m_type != etArea)
|
|
m_sitMapGuiIfc->getCoordinate(this, SLOT(onCoordinate(QPointF)), nullptr);
|
|
else
|
|
m_sitMapGuiIfc->getRectangle(this,
|
|
SLOT(onMapRecData(QPointF,double,double,double,QPolygonF)),
|
|
nullptr,
|
|
s_minWidth,
|
|
s_maxWidth,
|
|
s_minHeight,
|
|
s_maxHeight,
|
|
isNull ? m_ui->positionEdit->coordinate() : p,
|
|
m_ui->sbDimensionX->value(),
|
|
m_ui->sbDimensionY->value(),
|
|
(m_ui->sbAimingAxis->value() - 1600) / 6400. * 360);
|
|
}
|
|
|
|
void ExplosionEdit::onCoordinate(const QPointF ¢erPoint)
|
|
{
|
|
m_previewPolygon.clear();
|
|
m_previewCircleCenter = centerPoint;
|
|
m_previewCircleRadius = 0.5 * m_ui->sbDimensionX->value();
|
|
m_ui->positionEdit->setCoordinate(centerPoint);
|
|
|
|
showPreviewObject();
|
|
}
|
|
|
|
void ExplosionEdit::onMapRecData(const QPointF &bottomLeft, double width, double height, double angleDegNorth, const QPolygonF &bufferOutlinePolygon)
|
|
{
|
|
m_previewCircleCenter = QPointF();
|
|
m_previewPolygon = bufferOutlinePolygon;
|
|
|
|
m_ui->positionEdit->setCoordinate(Polygons::bottomLeft2CenterPoint(bottomLeft, width, height, angleDegNorth));
|
|
m_ui->sbDimensionX->setValue(round(width));
|
|
m_ui->sbDimensionY->setValue(round(height));
|
|
m_ui->sbAimingAxis->setValue(fmod((90.0 + angleDegNorth) * 6400 / 360, 6400));
|
|
|
|
showPreviewObject();
|
|
}
|
|
|
|
void ExplosionEdit::onChargeTypeChanged()
|
|
{
|
|
int chargeTypeId =m_ui->cbChargeType->currentData().toInt();
|
|
// update comboBox IgniterType:
|
|
m_pdmCbx->fillIgniterComboBox(m_ui->cbIgniterType, chargeTypeId);
|
|
|
|
updateCannonsRounds();
|
|
if (m_type == etArea)
|
|
return;
|
|
|
|
// NOTE: igniterType/IgniterFunciton = 0 as long as ExplosionEdit does not contain values
|
|
double impactRadius = m_pdmDtc->impactRadius(m_ui->cbChargeType->currentData().toInt(), 0, 0);
|
|
m_ui->sbDimensionX->setValue(2 * impactRadius);
|
|
}
|
|
|
|
void ExplosionEdit::onPartyChanged()
|
|
{
|
|
int partyId = m_ui->cbParty->currentData().toInt();
|
|
int chargeTypeId = m_ui->cbChargeType->currentData().toInt();
|
|
if (chargeTypeId <= 0)
|
|
chargeTypeId = m_gearClass == ProjectDataManagement::GearClass::Artillery ? s_lastChargeTypeA : s_lastChargeTypeM;
|
|
|
|
QSignalBlocker sb(m_ui->cbChargeType);
|
|
m_ui->cbChargeType->clear();
|
|
for (const Charges &charge : qAsConst(m_chargeTypeMap))
|
|
{
|
|
if (charge.available.contains(partyId) && charge.available[partyId] > 0)
|
|
m_ui->cbChargeType->addItem(m_pdmDtc->trChargeTypeShortName(charge.chargeType.id(), true), charge.chargeType.id());
|
|
}
|
|
|
|
m_ui->cbChargeType->setCurrentIndex(m_ui->cbChargeType->findData(chargeTypeId) < 0 ? 0 : m_ui->cbChargeType->findData(chargeTypeId));
|
|
sb.unblock();
|
|
updateCannonsRounds();
|
|
}
|
|
|
|
bool ExplosionEdit::apply()
|
|
{
|
|
std::string name = m_ui->leIdentifier->text().toStdString();
|
|
qint64 effectStart = m_ui->dteEffectStart->msecsSinceEpoch();
|
|
int chargeType = m_ui->cbChargeType->currentData().toInt();
|
|
if (m_gearClass == ProjectDataManagement::GearClass::Artillery)
|
|
s_lastChargeTypeA = chargeType;
|
|
else
|
|
s_lastChargeTypeM = chargeType;
|
|
int partyId = m_ui->cbParty->currentData().toInt();
|
|
// int playerId = m_participant.id();
|
|
int cannons = m_ui->sbCannons->value();
|
|
int rounds = m_ui->sbRounds->value();
|
|
int duration = m_ui->sbDurationSeconds->value() * 1000; // secs --> msecs
|
|
|
|
// Area:
|
|
QPointF bottomLeft(Polygons::centerPoint2BottomLeft(m_ui->positionEdit->coordinate(), m_ui->sbDimensionX->value(), m_ui->sbDimensionY->value(), (m_ui->sbAimingAxis->value() - 1600) * 360.0 / 6400));
|
|
const Core::GeoPosition2D bottomLeftGeo(bottomLeft.x(), bottomLeft.y());
|
|
const Area area(bottomLeftGeo, m_ui->sbDimensionX->value(), m_ui->sbDimensionY->value(), (m_ui->sbAimingAxis->value() - 1600) * 360.0 / 6400);
|
|
|
|
// Single:
|
|
const Core::GeoPosition2D pos(m_ui->positionEdit->coordinate().x(), m_ui->positionEdit->coordinate().y());
|
|
const Core::GeoPosition pos3D(m_ui->positionEdit->coordinate().x(), m_ui->positionEdit->coordinate().y(), 0.0);
|
|
|
|
qint64 fireRequestTime = m_ui->dteRequestTime->msecsSinceEpoch();
|
|
int altitude = m_ui->sbAltitude->value();
|
|
std::string purpose = m_ui->cbPurpose->currentText().toStdString();
|
|
|
|
if (m_type == etSingle)
|
|
{
|
|
const ExplosionSingleModifyRequest req(m_aesItem.id(),
|
|
name,
|
|
LtsUtils::toOptional(effectStart),
|
|
m_aesItem.effectSourceId(),
|
|
chargeType,
|
|
pos,
|
|
LtsUtils::toOptional(partyId),
|
|
LtsUtils::toOptional(0), // PlayerId
|
|
fireRequestTime,
|
|
altitude,
|
|
purpose,
|
|
m_ui->cbEffectMarkingInTa->isChecked(),
|
|
dds::core::optional<rti::core::int64>());
|
|
|
|
m_requestHelper->addRequest([this, req]()
|
|
{
|
|
return m_ftc->explosionSingleModifyRequest(req, this);
|
|
});
|
|
}
|
|
else if (m_type == etArea) // area
|
|
{
|
|
const ExplosionAreaModifyRequest req(m_aesItem.id(),
|
|
name,
|
|
LtsUtils::toOptional(effectStart),
|
|
duration,
|
|
m_aesItem.effectSourceId(),
|
|
chargeType,
|
|
area,
|
|
LtsUtils::toOptional(partyId),
|
|
LtsUtils::toOptional(0), // PlayerId
|
|
cannons,
|
|
rounds,
|
|
fireRequestTime,
|
|
altitude,
|
|
purpose,
|
|
m_ui->cbEffectMarkingInTa->isChecked(),
|
|
dds::core::optional<rti::core::int64>());
|
|
m_requestHelper->addRequest([this, req]()
|
|
{
|
|
return m_ftc->explosionAreaModifyRequest(req, this);
|
|
});
|
|
}
|
|
|
|
return m_requestHelper->startRequests();
|
|
}
|
|
|
|
void ExplosionEdit::onFireClicked()
|
|
{
|
|
m_ui->pbFire->setEnabled(false);
|
|
if ((m_aesItem.effectStateId() != AesEffectState_def::EsActivated &&
|
|
m_aesItem.effectStateId() != AesEffectState_def::EsPaused &&
|
|
m_aesItem.effectStateId() != AesEffectState_def::EsCreated &&
|
|
m_aesItem.effectStateId() != AesEffectState_def::EsPrepared)
|
|
|| m_aesItem.id() <= 0)
|
|
return;
|
|
|
|
// Catches all single fires...
|
|
if (m_aesItem.effectStateId() == AesEffectState_def::EsCreated ||
|
|
m_aesItem.effectStateId() == AesEffectState_def::EsPrepared)
|
|
{
|
|
m_ui->dteEffectStart->setMsecsSinceEpoch(m_session->partitionTime() + 3000);
|
|
m_ui->dteEffectStart->setChecked(true);
|
|
// Todo: Triggering by simulated button press is a little awkward...
|
|
if (m_uiRequirements->checkRequirements(this))
|
|
this->m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->click();
|
|
else
|
|
m_ui->pbFire->setEnabled(true);
|
|
|
|
return;
|
|
}
|
|
|
|
// No more single fires handled here...
|
|
m_requestHelper->addRequest([ = ]()
|
|
{
|
|
return m_ftc->explosionAreaPauseResumeRequest(ExplosionAreaPauseResumeRequest(m_aesItem.id(),
|
|
m_aesItem.effectStateId() != AesEffectState_def::EsActivated,
|
|
dds::core::optional<rti::core::int64>(),
|
|
dds::core::optional<int32_t>(),
|
|
dds::core::optional<int32_t>())
|
|
, this);
|
|
});
|
|
m_requestHelper->startRequests();
|
|
}
|
|
|
|
void ExplosionEdit::onParticipantClicked()
|
|
{
|
|
ParticipantManagement::ParticipantSelection *ps = new ParticipantManagement::ParticipantSelection(m_session, this);
|
|
ps->init();
|
|
ps->setParticipantSelected(m_participant.partyId(), m_participant.id());
|
|
|
|
connect(ps, &ParticipantManagement::ParticipantSelection::selectedParticipants, this, &ExplosionEdit::onParticipant);
|
|
ps->exec();
|
|
}
|
|
|
|
void ExplosionEdit::onParticipant(const QVector<int> &participants)
|
|
{
|
|
if (participants.isEmpty())
|
|
return;
|
|
|
|
if (participants[0] == m_participant.id())
|
|
return;
|
|
m_participant = m_ppmDtc->participant(participants[0]);
|
|
|
|
m_symbolWatcher->setSymbolResult(m_asbIfc->findImage(m_participant));
|
|
m_ui->lblDesignator->setText(QString::fromStdString(m_participant.designator()));
|
|
m_ui->lblTactIdentifier->setText(QString::fromStdString(m_participant.tacticalIdentifier()));
|
|
|
|
// m_ui->lblParticipant->setText(participant.equipmentNumber().is_set() ? QString::number(participant.equipmentNumber().get()) : tr("Id: %1").arg(participant.id()));
|
|
m_ui->cbParty->setCurrentIndex(m_ui->cbParty->findData(m_participant.partyId()));
|
|
slotChangesMade();
|
|
}
|
|
} // namespace AreaEffectSimulation
|