2733 lines
108 KiB
C++
Executable File
2733 lines
108 KiB
C++
Executable File
/**
|
||
* @RDE_HEADER@
|
||
* AreaEffectSimulation - Explosions Edit Dialog
|
||
* $Id$
|
||
* $HeadURL$
|
||
* $Author$
|
||
* Created by Steve Tougnia, SLTE, 2023-09-05
|
||
*/
|
||
|
||
#include "aes_explosions_edit.h"
|
||
|
||
#include "ui_aes_explosions_edit.h"
|
||
|
||
#include "aes_rocket_utility.h"
|
||
|
||
#include <LtsSession/lts_session_data.h>
|
||
#include <PluginInterfaces/gui_plugin_interface.h>
|
||
#include <Plugins/AreaEffectSimulation/aes_data_plugin_interface.h>
|
||
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
|
||
#include <Services/AreaEffectSimulation/Utils/aes_helper.h>
|
||
#include <Services/AreaEffectSimulation/Utils/polygons.h>
|
||
|
||
#include <LtsUtilsSettings/settings.h>
|
||
#include <Services/AreaEffectSimulation/settings.h>
|
||
|
||
#include <Plugins/AdvancedSymbology/asb_data_plugin_interface.h>
|
||
#include <Plugins/ParticipantManagement/Gui/ParticipantSelection/participant_selection.h>
|
||
|
||
#include <AreaEffectSimulation/aes_translation.h>
|
||
#include <ProjectDataManagement/pdm_translation.h>
|
||
#include <Trajectory/trajectory_generator.h>
|
||
#include <Trajectory/trajectory_helper.h>
|
||
|
||
#include <AssetManagement/asm_data_plugin_interface.h>
|
||
#include <ExerciseManagement/exm_data_plugin_interface.h>
|
||
#include <LtsElevation/lts_elevation_manager.h>
|
||
|
||
#include <MathUtils/simple_helper.h>
|
||
|
||
#include <QButtonGroup>
|
||
#include <QDateTime>
|
||
#include <QHash>
|
||
#include <QJsonDocument>
|
||
#include <QSpinBox>
|
||
#include <QStandardItem>
|
||
#include <QTimer>
|
||
|
||
namespace AreaEffectSimulation
|
||
{
|
||
double ExplosionsEdit::s_minWidth;
|
||
double ExplosionsEdit::s_maxWidth;
|
||
double ExplosionsEdit::s_stepWidth;
|
||
double ExplosionsEdit::s_minHeight;
|
||
double ExplosionsEdit::s_maxHeight;
|
||
double ExplosionsEdit::s_stepHeight;
|
||
double ExplosionsEdit::s_minRadius;
|
||
double ExplosionsEdit::s_maxRadius;
|
||
double ExplosionsEdit::s_stepRadius;
|
||
int ExplosionsEdit::s_maxChargeUsage;
|
||
double ExplosionsEdit::s_minAltitude;
|
||
double ExplosionsEdit::s_maxAltitude;
|
||
qint64 ExplosionsEdit::s_maxDurationSeconds;
|
||
|
||
int ExplosionsEdit::s_dropAngleDown;
|
||
int ExplosionsEdit::s_dropAngleUp;
|
||
|
||
static int s_timeUntilMinimumSec = 10; // sec timeUntilSec
|
||
static const char *const VALUE_VALID_PROPERTY_NAME = "VALUE_VALID";
|
||
static const char *const LAST_VALUE = "LAST_VALUE";
|
||
static const int s_laserPrecisionChargeTypeId = 376; // Jira Task E01562-366
|
||
QPair<int, int> getMinMaxDistance(ProjectDataManagement::GearClass_def::type gearClass, int chargeTypeId)
|
||
{
|
||
// [GUEZE-2458] Minimaler und maximaler Abstand Feuereinheit Wirkeld Steilfeuer
|
||
// Es soll zwischen dem Mittelpunkt der Feuereinheit und dem Mittelpunkt des zu bewirkenden Wirkfeldes folgender Abstand als Mindestabstand:
|
||
// Mörser: 800m
|
||
// Rakete: 15000m
|
||
// Haubitze: 3500m
|
||
// Minenrakete: 10000m
|
||
|
||
// Und einem maximalen Abstand von:
|
||
// Mörser: 7800m
|
||
// Rakete: 90000m
|
||
// Haubitze: 40000m
|
||
// Minenrakete: 38400m
|
||
|
||
static QHash<int, QPair<int, int>> contraintsDistance;
|
||
if (contraintsDistance.isEmpty())
|
||
{
|
||
contraintsDistance.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Artillery), {3500, 40000}); // Haubitze
|
||
contraintsDistance.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Mortar), {800, 7800}); // Mörser
|
||
// contraints.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Mortar), {15000, 90000}); // Rakete
|
||
// contraints.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Mortar), {10000, 38400}); // Minenrakete
|
||
}
|
||
|
||
static QHash<int, QPair<int, int>> contraintsDistanceVulcano;
|
||
if (contraintsDistanceVulcano.isEmpty())
|
||
{
|
||
contraintsDistanceVulcano.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Artillery), {10000, 70000}); // Haubitze
|
||
contraintsDistanceVulcano.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Rocket), {10000, 70000}); // Rakete
|
||
}
|
||
if (chargeTypeIdIsVulcano(chargeTypeId))
|
||
{
|
||
auto it = contraintsDistanceVulcano.find(static_cast<int>(gearClass));
|
||
if (it == contraintsDistanceVulcano.end())
|
||
return {0, 100000};
|
||
return *it;
|
||
}
|
||
|
||
// chargeTypeId Is _NOT_ Vulcano
|
||
auto it = contraintsDistance.find(static_cast<int>(gearClass));
|
||
if (it == contraintsDistance.end())
|
||
return {0, 100000};
|
||
return *it;
|
||
}
|
||
int getMinDistance(ProjectDataManagement::GearClass::inner_enum gearClass, int chargeTypeId) { return getMinMaxDistance(gearClass, chargeTypeId).first; }
|
||
int getMaxDistance(ProjectDataManagement::GearClass::inner_enum gearClass, int chargeTypeId) { return getMinMaxDistance(gearClass, chargeTypeId).second; }
|
||
|
||
QPair<int, int> getMinMaxFlightDurationSec(ProjectDataManagement::GearClass::inner_enum gearClass, int chargeTypeId)
|
||
{
|
||
// [GUEZE-2460] Grenzwert Flugdauer Steilfeuer
|
||
// Haubitze: von 6 bis 150 Sekunden
|
||
// Rakete: von 15 bis 200 Sekunden
|
||
// Mörser: Von 20 bis 60 Sekunden
|
||
// Minenrakete: von 40 bis 200 Sekunden
|
||
|
||
static QHash<int, QPair<int, int>> contraintsFlightDuration;
|
||
if (contraintsFlightDuration.isEmpty())
|
||
{
|
||
contraintsFlightDuration.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Artillery), {6, 150}); // Haubitze
|
||
contraintsFlightDuration.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Mortar), {20, 60}); // Mörser
|
||
}
|
||
|
||
static QHash<int, QPair<int, int>> contraintsFlightDurationVulcano;
|
||
if (contraintsFlightDurationVulcano.isEmpty())
|
||
{
|
||
contraintsFlightDurationVulcano.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Artillery), {0, 999}); // Haubitze
|
||
contraintsFlightDurationVulcano.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Rocket), {0, 999}); // Rakete
|
||
}
|
||
if (chargeTypeIdIsVulcano(chargeTypeId))
|
||
{
|
||
auto it = contraintsFlightDurationVulcano.find(static_cast<int>(gearClass));
|
||
if (it == contraintsFlightDurationVulcano.end())
|
||
return {0, 100000};
|
||
return *it;
|
||
}
|
||
|
||
// chargeTypeId Is _NOT_ Vulcano
|
||
auto it = contraintsFlightDuration.find(static_cast<int>(gearClass));
|
||
if (it == contraintsFlightDuration.end())
|
||
return {10, 100};
|
||
return *it;
|
||
}
|
||
int getMinFlightDurationSec(ProjectDataManagement::GearClass::inner_enum gearClass, int chargeTypeId)
|
||
{
|
||
return getMinMaxFlightDurationSec(gearClass, chargeTypeId).first;
|
||
}
|
||
int getMaxFlightDurationSec(ProjectDataManagement::GearClass::inner_enum gearClass, int chargeTypeId)
|
||
{
|
||
return getMinMaxFlightDurationSec(gearClass, chargeTypeId).second;
|
||
}
|
||
|
||
QPair<int, int> getMinMaxPeakHeight(ProjectDataManagement::GearClass::inner_enum gearClass, int chargeTypeId)
|
||
{
|
||
// [GUEZE-2460] Grenzwert Flugdauer Steilfeuer
|
||
// Haubitze: von 250 bis 15000m
|
||
// Rakete: von 100 bis 27000m
|
||
// Mörser: von 500 bis 3000m
|
||
// Minenrakete: von 900 bis 18000m
|
||
|
||
static QHash<int, QPair<int, int>> contraintsPeak;
|
||
if (contraintsPeak.isEmpty())
|
||
{
|
||
contraintsPeak.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Artillery), {250, 15000}); // Haubitze
|
||
contraintsPeak.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Mortar), {500, 3000}); // Mörser
|
||
}
|
||
|
||
static QHash<int, QPair<int, int>> contraintsPeakVulcano;
|
||
if (contraintsPeakVulcano.isEmpty())
|
||
{
|
||
contraintsPeakVulcano.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Artillery), {1000, 37500}); // Haubitze
|
||
contraintsPeakVulcano.insert(static_cast<int>(ProjectDataManagement::GearClass_def::Rocket), {1000, 37500}); // Rakete
|
||
}
|
||
if (chargeTypeIdIsVulcano(chargeTypeId))
|
||
{
|
||
auto it = contraintsPeakVulcano.find(static_cast<int>(gearClass));
|
||
if (it == contraintsPeakVulcano.end())
|
||
return {0, 100000};
|
||
return *it;
|
||
}
|
||
|
||
// chargeTypeId Is _NOT_ Vulcano
|
||
|
||
auto it = contraintsPeak.find(static_cast<int>(gearClass));
|
||
if (it == contraintsPeak.end())
|
||
return {100, 10000};
|
||
return *it;
|
||
}
|
||
int getMinPeakHeight(ProjectDataManagement::GearClass::inner_enum gearClass, int chargeTypeId) { return getMinMaxPeakHeight(gearClass, chargeTypeId).first; }
|
||
int getMaxPeakHeight(ProjectDataManagement::GearClass::inner_enum gearClass, int chargeTypeId) { return getMinMaxPeakHeight(gearClass, chargeTypeId).second; }
|
||
|
||
/*!
|
||
* \brief ExplosionsEdit::ExplosionsEdit
|
||
* \param menuGroup
|
||
* \param parent
|
||
*/
|
||
QString ExplosionsEdit::toString(ExplosionsEdit::FlighDurationOrPeakHeight val)
|
||
{
|
||
switch (val)
|
||
{
|
||
case ExplosionsEdit::FlighDurationOrPeakHeight::FDPH_flightDuration:
|
||
return QStringLiteral("FDPH_flightDuration");
|
||
break;
|
||
case ExplosionsEdit::FlighDurationOrPeakHeight::FDPH_peakHeight:
|
||
return QStringLiteral("FDPH_peakHeight");
|
||
break;
|
||
default:
|
||
return QStringLiteral("PHFD_Unknow");
|
||
break;
|
||
}
|
||
return QStringLiteral("PHFD_Unknow");
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// class ExplosionsEdit
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
ExplosionsEdit::ExplosionsEdit(const QString &menuGroup, QWidget *parent)
|
||
: BaseEdit(menuGroup, QStringLiteral("ExplosionsEdit"), parent)
|
||
, m_ui(new Ui::ExplosionsEdit)
|
||
{
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " START";
|
||
m_ui->setupUi(this);
|
||
|
||
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 = 5940; // 99 Minutes
|
||
|
||
s_dropAngleDown = 30;
|
||
s_dropAngleUp = 70;
|
||
|
||
m_geometrieButtonGroup = new QButtonGroup(this);
|
||
m_geometrieButtonGroup->addButton(m_ui->geometrieAimPointBtn, static_cast<int>(AreaEffectSimulation::AesExplosionGeometry_def::EgAimPoint));
|
||
m_geometrieButtonGroup->addButton(m_ui->geometrieAreaBtn, static_cast<int>(AreaEffectSimulation::AesExplosionGeometry_def::EgArea));
|
||
m_geometrieButtonGroup->addButton(m_ui->geometrieLineBtn, static_cast<int>(AreaEffectSimulation::AesExplosionGeometry_def::EgLine));
|
||
m_geometrieButtonGroup->addButton(m_ui->geometriePointBtn, static_cast<int>(AreaEffectSimulation::AesExplosionGeometry_def::EgPoint));
|
||
m_geometrieButtonGroup->addButton(m_ui->geometrieRadialBtn, static_cast<int>(AreaEffectSimulation::AesExplosionGeometry_def::EgRadial));
|
||
m_geometrieButtonGroup->setExclusive(true);
|
||
|
||
m_gearClassFilter = {ProjectDataManagement::GearClass::Artillery, ProjectDataManagement::GearClass::Mortar};
|
||
LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance();
|
||
const bool enableRocket = ps->value(QStringLiteral("aes"), QStringLiteral("enableRocket"), false).toBool();
|
||
if (enableRocket)
|
||
m_gearClassFilter += ProjectDataManagement::GearClass::Rocket;
|
||
m_uiRequirements->addRequirement({m_ui->lblPosition, [this]() { return (!m_coord.isNull()); }});
|
||
m_uiRequirements->addRequirement({m_ui->lblRadiusBreite, [this]() { return (m_ui->sbDimensionX_BreiteRadius->value() >= s_minWidth); }});
|
||
m_uiRequirements->addRequirement({m_ui->lblDimensionY_Tiefe, [this]() { return (m_ui->sbDimensionY_Tiefe->value() >= s_minHeight); }});
|
||
// m_uiRequirements->addRequirement({m_ui->lblName, [this]() { return (!m_ui->leElement->text().isEmpty()); }});
|
||
m_uiRequirements->addRequirement({m_ui->lblZpNumber, [this]() { return (!m_ui->leZpNumber->text().isEmpty()); }});
|
||
m_uiRequirements->addRequirement({m_ui->lblChargeType, [this]() { return (!m_ui->cbChargeType->currentData().isNull()); }});
|
||
m_uiRequirements->addRequirement({m_ui->lblIgniter, [this]() { return (m_ui->cbIgniterType->currentData().toInt() >= 0); }});
|
||
m_uiRequirements->addRequirement({m_ui->lblParticipantUnits, [this]() { return (m_ui->cbParticipantUnits->styleSheet().isEmpty()); }}); // empty -> ok ; set -> invalid
|
||
m_uiRequirements->addRequirement({m_ui->lblAnnotationString, [this]() { return (m_temporaryData.m_annotationString4Dlg.isEmpty()); }}); // empty -> ok ; set -> invalid
|
||
m_uiRequirements->addRequirement({m_ui->lblTargetGeometrie,
|
||
[this]()
|
||
{
|
||
return (m_ui->geometrieAimPointBtn->isChecked() || m_ui->geometrieRadialBtn->isChecked() || m_ui->geometrieAreaBtn->isChecked()
|
||
|| m_ui->geometrieLineBtn->isChecked() || m_ui->geometriePointBtn->isChecked());
|
||
}});
|
||
|
||
// either no effect start is set or effect start is greater than request time
|
||
m_uiRequirements->addRequirement({m_ui->lblEffectStart,
|
||
[this]()
|
||
{
|
||
// Requirements are checked after data changes on DDS.
|
||
// So also, if data isn´t modifible anymore or becomes EffectState::Active...
|
||
// so don´t check if data isn't modifible anymore:
|
||
|
||
// DEBUG_LOG(m_logger) << "m_explosionClusterData " << m_explosionClusterData;
|
||
if (!m_explosionClusterData.isObjectModifiable())
|
||
return true;
|
||
|
||
bool bEffectStartValid = true;
|
||
// dteEffectStart: check if property exists. Initially the prop is NOT set.
|
||
// (partitionTime has to change first....)
|
||
if (m_ui->dteEffectStart->isCheckable() && m_ui->dteEffectStart->isChecked()
|
||
&& m_ui->dteEffectStart->property(VALUE_VALID_PROPERTY_NAME).isValid())
|
||
bEffectStartValid = m_ui->dteEffectStart->property(VALUE_VALID_PROPERTY_NAME).toBool();
|
||
return hasValidEffectStart(m_ui->dteEffectStart) && bEffectStartValid;
|
||
}});
|
||
m_uiRequirements->addRequirement({m_ui->lblActors, [this]() { return !m_ui->gbEffectMarkingInTa->isChecked() || (m_ui->cbActors->currentData().toInt() > 0); }});
|
||
|
||
// UI-Requirements seems to work on current tab only!?
|
||
// m_uiRequirements->addRequirement({m_ui->lblLcPrf, [this]() { return (m_ui->sbLcPrf->value() != m_ui->sbLcTermination->value()); }});
|
||
// m_uiRequirements->addRequirement({m_ui->lblLcTermination,[this]() { return (m_ui->sbLcPrf->value() != m_ui->sbLcTermination->value()); }});
|
||
|
||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||
// prefer new idClicked...
|
||
connect(m_geometrieButtonGroup, &QButtonGroup::idClicked, this, &ExplosionsEdit::onGeometrySelected);
|
||
#else
|
||
// ... but be compatible to older versions of qt:
|
||
connect(m_geometrieButtonGroup, QOverload<int>::of(&QButtonGroup::buttonClicked), this, &ExplosionsEdit::onGeometrySelected);
|
||
#endif // QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||
|
||
connect(m_ui->cbGearClass, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ExplosionsEdit::onGearClassChanged);
|
||
connect(m_ui->cbParty, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ExplosionsEdit::onPartyChanged);
|
||
connect(m_ui->cbChargeType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ExplosionsEdit::onChargeTypeChanged);
|
||
connect(m_ui->cbIgniterType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ExplosionsEdit::onIgniterTypeChanged);
|
||
connect(m_ui->cbFirePurpose, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ExplosionsEdit::onFirePurposeChanged);
|
||
connect(m_ui->pbClear, &QPushButton::clicked, m_ui->positionEdit, &LtsCoordWidget::LtsCoordWidget::clear);
|
||
connect(m_ui->pbClear, &QPushButton::clicked, m_ui->sbHeight1SBox, &QSpinBox::clear);
|
||
connect(m_ui->pbClear, &QPushButton::clicked, m_ui->positionEdit_2, &LtsCoordWidget::LtsCoordWidget::clear);
|
||
connect(m_ui->pbClear, &QPushButton::clicked, m_ui->sbHeight2SBox, &QSpinBox::clear);
|
||
|
||
// limit impact to limit number of resulting trajectories.
|
||
// The range of dds_ids of the of the sample AesItemView (with AtExplosionClusterSingleTrajectory) is limited
|
||
// There is a single sample per each explosionNumber.
|
||
// See object_explosion_cluster.cpp
|
||
// GUEZ: Hein: max 120
|
||
// update: The maximum is now configurable via lts-cc.conf
|
||
m_ui->sbCannons->setMaximum(Settings::getMaxRoundsAreaWideFire()); // (max should be less than AreaEffectSimulation::s_maxTrajectoriesPerExplosionCluster);
|
||
|
||
// PositionEdit
|
||
connect(m_ui->positionEdit, &LtsCoordWidget::LtsCoordWidget::coordinateChanged, this, &ExplosionsEdit::setCoord);
|
||
connect(m_ui->positionEdit, &LtsCoordWidget::LtsCoordWidget::coordinateChanged, this, &ExplosionsEdit::onPositionChanged);
|
||
connect(m_ui->positionEdit, &LtsCoordWidget::LtsCoordWidget::coordinateChanged, this, &ExplosionsEdit::onLineP1Changed);
|
||
connect(m_ui->positionEdit_2, &LtsCoordWidget::LtsCoordWidget::coordinateChanged, this, &ExplosionsEdit::onLineP2Changed);
|
||
|
||
// Connect Breite (Radius) und Tiefe if m_geometry == Radial
|
||
connect(m_ui->sbDimensionX_BreiteRadius, QOverload<int>::of(&QSpinBox::valueChanged), this, &ExplosionsEdit::onWidthChanged);
|
||
|
||
// "Flugzeit" and "Gipfelhöhe" can not be set at the same time
|
||
connect(m_ui->sBox_flightDuration, QOverload<int>::of(&QSpinBox::valueChanged), this, &ExplosionsEdit::onFlightDurationChanged);
|
||
connect(m_ui->sBox_peakHeight, QOverload<int>::of(&QSpinBox::valueChanged), this, &ExplosionsEdit::onPeakHeigthChanged);
|
||
|
||
// Duration (Effect)
|
||
connect(m_ui->sbDurationSec, QOverload<int>::of(&QSpinBox::valueChanged), this, &ExplosionsEdit::onEffectDurationChanged);
|
||
|
||
// Aiming Axis (for line geometry)
|
||
connect(m_ui->sbAimingAxis, QOverload<int>::of(&QSpinBox::valueChanged), this, &ExplosionsEdit::onAimingAxisChanged);
|
||
|
||
// "Einschießen" is hidden for now
|
||
m_ui->lblFire->hide();
|
||
m_ui->einschlJaBtn->hide();
|
||
m_ui->einschlNoBtn->hide();
|
||
|
||
// "Wirkungsfeuer" is hidden for now
|
||
m_ui->lblFire_2->hide();
|
||
m_ui->wirkungJaBtn->hide();
|
||
m_ui->wirkungNoBtn->hide();
|
||
|
||
// Obere/Untere Winkelgruppen is hidden for now
|
||
m_ui->rbDropAngleDown->hide();
|
||
m_ui->rbDropAngleUp->hide();
|
||
|
||
m_ui->leElement->setMaxLength(30);
|
||
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " END";
|
||
}
|
||
|
||
/*!
|
||
* \brief ExplosionsEdit::~ExplosionsEdit
|
||
*/
|
||
ExplosionsEdit::~ExplosionsEdit()
|
||
{
|
||
if (m_sitMapGuiIfc)
|
||
m_sitMapGuiIfc->removeObjectIndicator();
|
||
delete m_ui;
|
||
}
|
||
|
||
/*!
|
||
* \brief AreaEffectSimulation::ExplosionsEdit::init
|
||
* \param aesItemId
|
||
* \param session
|
||
* \return
|
||
*/
|
||
bool ExplosionsEdit::init(int aesItemId, bool copy, LtsSession::LtsSession *session)
|
||
{
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " START";
|
||
if (!BaseEdit::init(aesItemId, session))
|
||
return false;
|
||
|
||
std::shared_ptr<EffectShowManagement::ForeignGuiComboboxInterface> esmActorCbx;
|
||
|
||
if (!m_session->getForeignGuiInterface(esmActorCbx))
|
||
return false;
|
||
|
||
if (!m_session->getForeignDataInterface<AdvancedSymbology::DataInterface>(m_asbIfc))
|
||
return false;
|
||
|
||
if (!session->getForeignGuiInterface(m_esmActorCbx))
|
||
return false;
|
||
|
||
// P _D_ M
|
||
if (!m_session->getDtc<ProjectDataManagement::DataInterface>(m_pdmDtc))
|
||
{
|
||
ERROR_LOG(m_logger) << " failed to get pdmDtc";
|
||
return false;
|
||
}
|
||
m_pdmDtc->waitForHistoricalDataChargeType(1000);
|
||
m_pdmDtc->waitForHistoricalDataGearType(1000);
|
||
m_pdmDtc->waitForHistoricalDataGearTypeChargeType(1000);
|
||
m_dtc->waitForHistoricalDataExplosionCluster(1000);
|
||
|
||
// P _P_ M
|
||
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;
|
||
|
||
// A _E_ S
|
||
if (!m_session->getDtc<AreaEffectSimulation::DataInterface>(m_aesDtc))
|
||
{
|
||
ERROR_LOG(m_logger) << " failed to get aesDtc";
|
||
return false;
|
||
}
|
||
|
||
// initial adjustment:
|
||
m_explosionClusterData.explosionGeometry(AreaEffectSimulation::AesExplosionGeometry_def::EgArea);
|
||
onGeometrySelected(static_cast<int>(AreaEffectSimulation::AesExplosionGeometry_def::EgArea));
|
||
|
||
// pay attention!!! firePurposeId vs purpose !!!
|
||
m_pdmCbx->fillFirePurposeComboBox(m_ui->cbFirePurpose);
|
||
onFirePurposeChanged();
|
||
|
||
fillGearClasses();
|
||
|
||
m_ui->dteEffectStart->setMinimumTimeMSec(m_session->partitionTime());
|
||
m_ui->dteEffectStart->setRetrieveCurrentTimeCallback(
|
||
[this]()
|
||
{
|
||
if (QGuiApplication::keyboardModifiers().testFlag(Qt::KeyboardModifier::ShiftModifier))
|
||
if (m_temporaryData.m_estimatedFlightDurationMsec > 0)
|
||
return m_session->partitionTime() + m_temporaryData.m_estimatedFlightDurationMsec + 10000;
|
||
else
|
||
return m_session->partitionTime();
|
||
else
|
||
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())));
|
||
|
||
// onGearClassChanged will implicitly switchTo(FlighDurationOrPeakHeight)
|
||
onGearClassChanged();
|
||
fillCharges();
|
||
// // onChargeTypeChanged();
|
||
|
||
// onGearClassChanged will implicitly switchTo..
|
||
// switchTo(FlighDurationOrPeakHeight::FDPH_flightDuration);
|
||
|
||
if (m_sitMapGuiIfc)
|
||
connect(m_ui->pbPosition, &QPushButton::clicked, this, &ExplosionsEdit::getPosition);
|
||
else
|
||
m_ui->pbPosition->setEnabled(false);
|
||
|
||
m_ui->gbEffectMarkingInTa->setChecked(false);
|
||
if (aesItemId > 0)
|
||
{
|
||
const ExplosionCluster &exData = m_dtc->explosionCluster(aesItemId);
|
||
setExplosionClusterData(exData);
|
||
m_ui->gbEffectMarkingInTa->setChecked(exData.effectMarkingInTa());
|
||
|
||
if (copy)
|
||
{
|
||
m_aesItem.id(0);
|
||
m_aesItem.effectStateId(AreaEffectSimulation::AesEffectState_def::get_default());
|
||
m_explosionClusterData.id(m_aesItem.id());
|
||
m_explosionClusterData.effectStateId(m_aesItem.effectStateId());
|
||
}
|
||
}
|
||
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantChanged, this, &ExplosionsEdit::onPartyChanged, Qt::UniqueConnection);
|
||
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantPositionChanged, this, &ExplosionsEdit::onParticipantPositionChanged, Qt::UniqueConnection);
|
||
|
||
m_ui->cbActors->setEnabled(m_ui->gbEffectMarkingInTa->isChecked());
|
||
onFirePurposeChanged();
|
||
connect(m_ui->gbEffectMarkingInTa, &QGroupBox::toggled, m_ui->cbActors, &QComboBox::setEnabled);
|
||
connect(m_ui->gbEffectMarkingInTa, &QGroupBox::toggled, this, &ExplosionsEdit::onFirePurposeChanged);
|
||
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::explosionClusterChanged, this, &ExplosionsEdit::onExplosionClusterChanged, Qt::UniqueConnection);
|
||
|
||
// cbParticipantUnits:
|
||
connect(m_ui->cbParticipantUnits, QOverload<int>::of(&LtsWidgets::LtsComboBox::currentIndexChanged), this, &ExplosionsEdit::onParticipantUnitChanged);
|
||
connect(m_ui->cbParticipantUnits, &LtsWidgets::LtsComboBox::signalEnterEvent, this, &ExplosionsEdit::onParticipantUnitEntered);
|
||
connect(m_ui->cbParticipantUnits, &LtsWidgets::LtsComboBox::signalLeaveEvent, this, &ExplosionsEdit::onParticipantUnitLeaved);
|
||
|
||
connect(m_ui->pbFire, &QPushButton::clicked, this, &ExplosionsEdit::onFireClicked);
|
||
|
||
connect(m_session, &LtsSession::LtsSession::partitionTimeChanged, this, &ExplosionsEdit::onPartitionTimeChanged, Qt::UniqueConnection);
|
||
|
||
setOnlineHelpFilename(QStringLiteral("Situation-Add_Steilfeuer"));
|
||
|
||
if (!m_elevationManager)
|
||
{
|
||
std::shared_ptr<AssetManagement::Interface::Dtc> asmDtc;
|
||
if (!m_session->getDtc<AssetManagement::DataInterface>(asmDtc))
|
||
{
|
||
WARNING_LOG(m_logger) << "getDtc for AssetManagement::DataInterface failed.";
|
||
}
|
||
|
||
std::shared_ptr<ExerciseManagement::Interface::Dtc> exmDtc;
|
||
if (!m_session->getDtc<ExerciseManagement::DataInterface>(exmDtc))
|
||
{
|
||
WARNING_LOG(m_logger) << "getDtc for ExerciseManagement::DataInterface failed.";
|
||
}
|
||
m_elevationManager = LtsElevation::LtsElevationManager::createInstance(m_session->exerciseId(), exmDtc, asmDtc);
|
||
}
|
||
|
||
m_initDone = true;
|
||
|
||
doCalculations();
|
||
|
||
RocketUtility::enaDisaWirkmittel(m_ui->cbGearClass, m_currentGeometry.underlying());
|
||
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " END";
|
||
|
||
return true;
|
||
}
|
||
|
||
void ExplosionsEdit::setExplosionClusterData(const ExplosionCluster &ecData)
|
||
{
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " START, ecData=" << ecData;
|
||
|
||
m_ui->leZpNumber->setText(QString::fromStdString(ecData.name()));
|
||
m_ui->leElement->setText(QString::fromStdString(ecData.element()));
|
||
m_ui->dteEffectStart->setMsecsSinceEpoch(ecData.effectStart().is_set() ? ecData.effectStart().get() : 0);
|
||
|
||
// Geometry/Area
|
||
const AreaEffectSimulation::AesExplosionGeometry_def::type geometryType = ecData.explosionGeometry().underlying();
|
||
switch (geometryType)
|
||
{
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgAimPoint:
|
||
m_ui->geometrieAimPointBtn->setChecked(true);
|
||
break;
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgPoint:
|
||
m_ui->geometriePointBtn->setChecked(true);
|
||
break;
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgArea:
|
||
m_ui->geometrieAreaBtn->setChecked(true);
|
||
break;
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgLine:
|
||
m_ui->geometrieLineBtn->setChecked(true);
|
||
break;
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgRadial:
|
||
m_ui->geometrieRadialBtn->setChecked(true);
|
||
break;
|
||
default:
|
||
// empty
|
||
break;
|
||
} // switch
|
||
onGeometrySelected(static_cast<int>(geometryType)); // which in turn calls fillCharges();
|
||
|
||
m_ui->sbDurationSec->setValue(ecData.effectDuration() / 1000); // msecs -> mins
|
||
|
||
// 1st GearClass
|
||
{
|
||
QSignalBlocker locked(m_ui->cbGearClass);
|
||
ProjectDataManagement::GearClass gc = m_pdmDtc->gearClassFromChargeTypeId(ecData.chargeStruct().chargeTypeId());
|
||
m_ui->cbGearClass->setCurrentIndex(m_ui->cbGearClass->findData(static_cast<int>(gc.underlying())));
|
||
onGearClassChanged(); // update depending comboBox
|
||
}
|
||
|
||
// 2nd Party
|
||
{
|
||
QSignalBlocker locked(m_ui->cbParty);
|
||
if (ecData.partyId().is_set())
|
||
{
|
||
m_ui->cbParty->setCurrentIndex(m_ui->cbParty->findData(ecData.partyId().get()));
|
||
onPartyChanged(); // update depending comboBox
|
||
}
|
||
}
|
||
|
||
// 3rd Originator
|
||
{
|
||
if (ecData.participantId().is_set())
|
||
{
|
||
m_ui->cbParticipantUnits->setCurrentIndex(m_ui->cbParticipantUnits->findData(ecData.participantId().get()));
|
||
onParticipantUnitChanged({});
|
||
}
|
||
}
|
||
|
||
FlightCurve fc(-1, -1, -1, -1, -1, -1, -1, -1);
|
||
if (ecData.flightCurve().is_set())
|
||
fc = ecData.flightCurve().get();
|
||
|
||
if (fc.dropAngle() <= s_dropAngleDown)
|
||
{
|
||
m_ui->rbDropAngleDown->setChecked(true);
|
||
m_ui->rbDropAngleUp->setChecked(false);
|
||
}
|
||
else
|
||
{
|
||
m_ui->rbDropAngleDown->setChecked(false);
|
||
m_ui->rbDropAngleUp->setChecked(true);
|
||
}
|
||
|
||
m_ui->sbCannons->setValue(ecData.impactCount());
|
||
m_ui->cbChargeType->setCurrentIndex(m_ui->cbChargeType->findData(ecData.chargeStruct().chargeTypeId()));
|
||
m_ui->cbIgniterType->setCurrentIndex(m_ui->cbIgniterType->findData(ecData.chargeStruct().igniterTypeId()));
|
||
m_ui->cbFirePurpose->setCurrentIndex(m_ui->cbFirePurpose->findData(ecData.firePurposeId()));
|
||
|
||
m_ui->sbZog->setValue(ecData.tla()); // tla == zog
|
||
|
||
const AreaEffectSimulation::Area &areaRef = ecData.area();
|
||
const QPointF bottomLeft(areaRef.cornerBottomLeft().lon(), areaRef.cornerBottomLeft().lat());
|
||
const QPointF centerpoint = Polygons::bottomLeft2CenterPoint(bottomLeft, areaRef.width(), areaRef.height(), areaRef.rotation());
|
||
if (!bottomLeft.isNull())
|
||
{
|
||
m_ui->positionEdit->setCoordinate(centerpoint);
|
||
setCoord(centerpoint);
|
||
}
|
||
|
||
if (ecData.explosionGeometry().underlying() == AreaEffectSimulation::AesExplosionGeometry_def::EgRadial)
|
||
{
|
||
m_ui->sbDimensionY_Tiefe->setValue(areaRef.height() / 2);
|
||
m_ui->sbDimensionX_BreiteRadius->setValue(areaRef.width() / 2);
|
||
m_ui->sbAimingAxis->setValue(0);
|
||
}
|
||
else
|
||
{
|
||
m_ui->sbDimensionY_Tiefe->setValue(areaRef.height());
|
||
m_ui->sbDimensionX_BreiteRadius->setValue(areaRef.width());
|
||
m_ui->sbAimingAxis->setValue(fmod((90.0 + areaRef.rotation()) * 6400 / 360, 6400));
|
||
}
|
||
|
||
if (!ecData.esmParticipantIds().empty())
|
||
{
|
||
m_ui->cbActors->setCurrentIndex(m_ui->cbActors->findData(ecData.esmParticipantIds().at(0)));
|
||
if (ecData.esmParticipantIds().size() > 1)
|
||
m_ui->cbActors_2->setCurrentIndex(m_ui->cbActors_2->findData(ecData.esmParticipantIds().at(1)));
|
||
}
|
||
|
||
// peakHeight | flightDuration
|
||
{
|
||
if (fc.requestedFlightDuration() > 0)
|
||
{
|
||
// m_ui->sBox_flightDuration->setValue(ecData.originalFlightDuration() / 1000.0);
|
||
switchTo(FlighDurationOrPeakHeight::FDPH_flightDuration);
|
||
setValueAccordingContraints(fc.requestedFlightDuration() / 1000.0, FlighDurationOrPeakHeight::FDPH_flightDuration);
|
||
}
|
||
if (fc.requestedRelativePeakHeight() > 0)
|
||
{
|
||
// m_ui->sBox_peakHeight->setValue(ecData.originalPeakHeight());
|
||
switchTo(FlighDurationOrPeakHeight::FDPH_peakHeight);
|
||
setValueAccordingContraints(fc.requestedRelativePeakHeight(), FlighDurationOrPeakHeight::FDPH_peakHeight);
|
||
}
|
||
|
||
LaserGuiding lg(0, 0, -1, std::numeric_limits<int>::min());
|
||
if (ecData.laserGuiding().is_set())
|
||
lg = ecData.laserGuiding().get();
|
||
|
||
m_ui->sbLcAttack_Prf->setValue(lg.lcAttack());
|
||
m_ui->sbLcTermination_Prf->setValue(lg.lcTermination());
|
||
// keep specialValueText for cloudBase in mind:
|
||
m_ui->spCloudBase->setValue(lg.cloudBase());
|
||
|
||
onExplosionClusterChanged(ecData, DataModification::eAdded);
|
||
}
|
||
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " END, ecData=" << ecData;
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
||
}
|
||
|
||
void ExplosionsEdit::onPartyChanged() { fillParticipantUnit(); }
|
||
|
||
void ExplosionsEdit::onChargeTypeChanged()
|
||
{
|
||
int lastChargeTypeId = -1;
|
||
QVariant lastVal = m_ui->cbChargeType->property(LAST_VALUE);
|
||
if (!lastVal.isNull())
|
||
lastChargeTypeId = lastVal.toInt();
|
||
|
||
const int chargeTypeId = m_ui->cbChargeType->currentData().toInt();
|
||
|
||
if (chargeTypeId <= 0)
|
||
{
|
||
WARNING_LOG(m_logger) << LOGFUNCTION << " chargeTypeId is invalid -> return. chargeTypeId = " << chargeTypeId;
|
||
return;
|
||
}
|
||
|
||
// update comboBox IgniterType:
|
||
m_pdmCbx->fillIgniterComboBox(m_ui->cbIgniterType, chargeTypeId);
|
||
|
||
handleLaserPrecisionChargeType(chargeTypeId); // Jira Task E01562-366
|
||
|
||
if (isGuiVulcano())
|
||
switchTo(FlighDurationOrPeakHeight::FDPH_peakHeight);
|
||
|
||
FlighDurationOrPeakHeight phfd = FlighDurationOrPeakHeight::FDPH_peakHeight;
|
||
const bool useFlightDuration = m_ui->sBox_flightDuration->styleSheet().isEmpty();
|
||
if (useFlightDuration)
|
||
phfd = FlighDurationOrPeakHeight::FDPH_flightDuration;
|
||
|
||
const qint32 gearClassId = m_ui->cbGearClass->currentData().toInt();
|
||
ProjectDataManagement::GearClass::type gearClassType = static_cast<ProjectDataManagement::GearClass::type>(gearClassId);
|
||
setMinMaxConstraints(phfd, gearClassType, chargeTypeId);
|
||
|
||
// only for geometryType::Point:
|
||
if (m_currentGeometry.underlying() == AreaEffectSimulation::AesExplosionGeometry_def::EgPoint)
|
||
{
|
||
int igniterTypeId = m_ui->cbIgniterType->currentData().toInt();
|
||
const double impactRadius = m_pdmDtc->impactRadius(chargeTypeId, igniterTypeId, 0); // GUEZE-2634
|
||
|
||
if (impactRadius <= 0)
|
||
ERROR_LOG(m_logger) << LOGFUNCTION << " impactRadius failed for chargeTypeId=" << chargeTypeId;
|
||
|
||
m_ui->sbDimensionX_BreiteRadius->setValue(std::round(impactRadius * 2.0));
|
||
// to simplify uiRequirements, set that value also to dimY:
|
||
m_ui->sbDimensionY_Tiefe->setValue(std::round(impactRadius * 2.0));
|
||
}
|
||
|
||
// switching from/to vulcano?
|
||
if (lastChargeTypeId != chargeTypeId)
|
||
{
|
||
bool lastWasVulcano = chargeTypeIdIsVulcano(lastChargeTypeId);
|
||
bool newIsVulcano = chargeTypeIdIsVulcano(chargeTypeId);
|
||
|
||
if (lastWasVulcano != newIsVulcano)
|
||
doCalculations();
|
||
}
|
||
|
||
// update last value:
|
||
m_ui->cbChargeType->setProperty(LAST_VALUE, chargeTypeId);
|
||
updateActorComboBox(m_coordGeo);
|
||
}
|
||
|
||
void ExplosionsEdit::onIgniterTypeChanged() {}
|
||
|
||
void ExplosionsEdit::onParticipantUnitChanged(int idx)
|
||
{
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
||
Q_UNUSED(idx);
|
||
|
||
bool bOk = false;
|
||
int particpantUnitId = m_ui->cbParticipantUnits->currentData(Qt::UserRole).toInt(&bOk);
|
||
if (!bOk)
|
||
particpantUnitId = 0; // should be interpreted as error since particpantUnitId ir required in this dialog
|
||
QPointF centerFiringUnitGeo;
|
||
|
||
if (particpantUnitId > 0)
|
||
centerFiringUnitGeo = centerOfFiringUnitGeo(particpantUnitId);
|
||
|
||
if (centerFiringUnitGeo.isNull())
|
||
m_ui->cbParticipantUnits->setStyleSheet(QStringLiteral("background-color: orangered;"));
|
||
else
|
||
m_ui->cbParticipantUnits->setStyleSheet(QLatin1String(""));
|
||
|
||
// maybe empty item at beginning?
|
||
// if (particpantUnitId > 0)
|
||
// centerFiringUnitGeo = centerOfFiringUnitGeo(particpantUnitId);
|
||
|
||
// if ((particpantUnitId > 0) && (centerFiringUnitGeo.isNull()))
|
||
// m_ui->cbParticipantUnits->setStyleSheet(QStringLiteral("background-color: orangered;"));
|
||
// else
|
||
// m_ui->cbParticipantUnits->setStyleSheet(QLatin1String(""));
|
||
|
||
doCalculations();
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
||
}
|
||
|
||
void ExplosionsEdit::onParticipantUnitEntered()
|
||
{
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " ENTER"
|
||
<< " id = " << m_ui->cbParticipantUnits->currentData().toInt();
|
||
showCenterOfFiringUnit(m_ui->cbParticipantUnits->currentData().toInt());
|
||
}
|
||
|
||
void ExplosionsEdit::onParticipantUnitLeaved()
|
||
{
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " LEAVE"
|
||
<< " id = " << m_ui->cbParticipantUnits->currentData().toInt();
|
||
showCenterOfFiringUnit(0);
|
||
}
|
||
|
||
void ExplosionsEdit::onParticipantPositionChanged()
|
||
{
|
||
onParticipantUnitChanged(-1); // index is not used
|
||
}
|
||
|
||
void ExplosionsEdit::getPosition()
|
||
{
|
||
if (!has2DMap())
|
||
return;
|
||
|
||
m_previewCircleCenter = QPointF();
|
||
m_previewPolygon.clear();
|
||
m_previewCircleRadius = 0.0;
|
||
|
||
switch (m_currentGeometry.underlying())
|
||
{
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgArea:
|
||
{
|
||
const bool isNull = qFuzzyCompare(m_coord.x(), 0) && qFuzzyCompare(m_coord.y(), 0);
|
||
const QPointF bottomLeft(Polygons::centerPoint2BottomLeft(m_coord,
|
||
m_ui->sbDimensionX_BreiteRadius->value(),
|
||
m_ui->sbDimensionY_Tiefe->value(),
|
||
(m_ui->sbAimingAxis->value() - 1600) / 6400. * 360));
|
||
|
||
m_sitMapGuiIfc->getRectangle(this,
|
||
SLOT(onMapRecData(QPointF, double, double, double, QPolygonF)),
|
||
nullptr,
|
||
s_minWidth,
|
||
s_maxWidth,
|
||
s_minHeight,
|
||
s_maxHeight,
|
||
isNull ? m_coord : bottomLeft,
|
||
m_ui->sbDimensionX_BreiteRadius->value(),
|
||
m_ui->sbDimensionY_Tiefe->value(),
|
||
(m_ui->sbAimingAxis->value() - 1600) / 6400. * 360);
|
||
|
||
return;
|
||
}
|
||
break;
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgAimPoint:
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgPoint:
|
||
m_sitMapGuiIfc->getCoordinate(this, SLOT(onCoordinate(QPointF)), nullptr);
|
||
return;
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgLine:
|
||
{
|
||
m_sitMapGuiIfc->getPolyline(this, SLOT(onPolylineData(QPolygonF)), nullptr, 2, 2, -1, -1, getCoordinatesQPointF());
|
||
return;
|
||
}
|
||
break;
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgRadial:
|
||
m_sitMapGuiIfc->getCircle(this,
|
||
SLOT(onCircleData(QPointF, double)),
|
||
nullptr,
|
||
s_minRadius,
|
||
s_maxRadius,
|
||
m_ui->positionEdit->coordinate(),
|
||
m_ui->sbDimensionX_BreiteRadius->value()); // max
|
||
return;
|
||
default:
|
||
WARNING_LOG(m_logger) << " Unknown Geometry!";
|
||
return;
|
||
}
|
||
}
|
||
|
||
/*!
|
||
* \brief ExplosionsEdit::onCoordinate
|
||
* \param centerPoint
|
||
*/
|
||
void ExplosionsEdit::onCoordinate(const QPointF ¢erPoint)
|
||
{
|
||
m_previewCircleCenter = centerPoint;
|
||
m_ui->positionEdit->setCoordinate(centerPoint);
|
||
|
||
const int chargeTypeId = m_ui->cbChargeType->currentData().toInt();
|
||
const int igniterTypeId = m_ui->cbIgniterType->currentData().toInt();
|
||
// NOTE: 0 = Default
|
||
const int igniterFunctionId = 0;
|
||
const double impactRadius = m_pdmDtc->impactRadius(chargeTypeId, igniterTypeId, igniterFunctionId);
|
||
m_previewCircleRadius = impactRadius;
|
||
onPositionChanged({});
|
||
showPreviewObject();
|
||
}
|
||
|
||
/*!
|
||
* \brief ExplosionsEdit::onMapRecData
|
||
* \param bottomLeft
|
||
* \param width
|
||
* \param height
|
||
* \param angleDegNorth
|
||
* \param bufferOutlinePolygon
|
||
*/
|
||
void ExplosionsEdit::onMapRecData(const QPointF &bottomLeft, double width, double height, double angleDegNorth, const QPolygonF &bufferOutlinePolygon)
|
||
{
|
||
m_previewPolygon = bufferOutlinePolygon;
|
||
|
||
m_ui->positionEdit->setCoordinate(Polygons::bottomLeft2CenterPoint(bottomLeft, width, height, angleDegNorth));
|
||
m_ui->sbDimensionX_BreiteRadius->setValue(round(width));
|
||
m_ui->sbDimensionY_Tiefe->setValue(round(height));
|
||
m_ui->sbAimingAxis->setValue(fmod((90.0 + angleDegNorth) * 6400 / 360, 6400));
|
||
|
||
onPositionChanged({});
|
||
showPreviewObject();
|
||
const Core::GeoPosition coordGeo(bottomLeft.x(), bottomLeft.y(), 0);
|
||
m_coordGeo = coordGeo;
|
||
updateActorComboBox(coordGeo);
|
||
}
|
||
|
||
void ExplosionsEdit::onCircleData(const QPointF &position, double radius)
|
||
{
|
||
m_previewCircleCenter = position;
|
||
|
||
m_ui->sbDimensionX_BreiteRadius->setValue(radius);
|
||
m_previewCircleRadius = m_ui->sbDimensionX_BreiteRadius->value();
|
||
|
||
m_ui->positionEdit->setCoordinate(position);
|
||
|
||
DEBUG_LOG(m_logger) << "Pos X: " << position.x() << " Y: " << position.y() << " R: " << radius;
|
||
|
||
showPreviewObject();
|
||
}
|
||
|
||
void ExplosionsEdit::onPolylineData(const QPolygonF &polyline)
|
||
{
|
||
if (polyline.size() < 2)
|
||
return;
|
||
|
||
m_previewPolygon = polyline;
|
||
|
||
setCoordinates(polyline, false);
|
||
|
||
const QPointF &geoCoord1 = polyline[0];
|
||
const QPointF &geoCoord2 = polyline[1];
|
||
const LtsCoordTrans::UtmCoordinate utm1 = LtsCoordTrans::UtmCoordinate::fromGeographic(geoCoord1);
|
||
const LtsCoordTrans::UtmCoordinate utm2 = LtsCoordTrans::UtmCoordinate::fromGeographic(geoCoord2);
|
||
|
||
const double angleDegNorth = SituationMap::MathUtils::getAngleDegreeNorth(QPointF(utm1.x, utm1.y), QPointF(utm2.x, utm2.y));
|
||
const double distance = LtsCoordTrans::distance(utm1, utm2);
|
||
|
||
DEBUG_LOG(m_logger) << " onPolylineData "
|
||
<< " geoCoord1: (" << geoCoord1.x() << "|" << geoCoord1.y() << ")"
|
||
<< " geoCoord2: (" << geoCoord2.x() << "|" << geoCoord2.y() << ")"
|
||
<< " geoCoord1Utm: (" << utm1.x << "|" << utm1.y << ")"
|
||
<< " geoCoord2Utm: (" << utm2.x << "|" << utm2.y << ")"
|
||
<< " angleDegNorth: " << angleDegNorth << " distance: " << distance;
|
||
|
||
m_ui->positionEdit->setCoordinate(utm1.geographic());
|
||
m_ui->positionEdit_2->setCoordinate(utm2.geographic());
|
||
m_ui->sbAimingAxis->setValue(fmod((angleDegNorth) * 6400 / 360, 6400));
|
||
m_ui->sbDimensionY_Tiefe->setValue(5);
|
||
m_ui->sbDimensionX_BreiteRadius->setValue(distance);
|
||
|
||
const Core::GeoPosition coordGeo2(utm2.geographic().x(), utm2.geographic().y(), 0);
|
||
const double height2 = getElevation(coordGeo2.lon(), coordGeo2.lat());
|
||
m_ui->sbHeight2SBox->setValue(height2);
|
||
DEBUG_LOG(m_logger) << "sbHeight2SBox setValue=" << height2;
|
||
|
||
showPreviewObject();
|
||
}
|
||
|
||
void ExplosionsEdit::updateActorComboBox(const Core::GeoPosition &coordGeo)
|
||
{
|
||
bool ok;
|
||
int esmParticipantId1 = m_ui->cbActors->currentData().toInt(&ok);
|
||
int esmParticipantId2 = m_ui->cbActors_2->currentData().toInt(&ok);
|
||
|
||
DEBUG_LOG(m_logger) << "currentIndex: " << m_ui->cbActors->currentIndex() << ", currentText: " << m_ui->cbActors->currentText()
|
||
<< ", esmParticipantId1: " << esmParticipantId1 << ", esmParticipantId2: " << esmParticipantId2 << ", ok: " << ok;
|
||
|
||
m_esmActorCbx->fillActorComboBox(m_ui->cbActors, coordGeo, isSmoke(), 0);
|
||
m_esmActorCbx->fillActorComboBox(m_ui->cbActors_2, coordGeo, isSmoke(), 0);
|
||
m_ui->cbActors->setCurrentIndex(m_ui->cbActors->findData(esmParticipantId1));
|
||
m_ui->cbActors_2->setCurrentIndex(m_ui->cbActors_2->findData(esmParticipantId2));
|
||
}
|
||
|
||
void ExplosionsEdit::setCoord(const QPointF &coord)
|
||
{
|
||
m_coord = coord;
|
||
const Core::GeoPosition coordGeo(coord.x(), coord.y(), 0);
|
||
m_coordGeo = coordGeo;
|
||
|
||
const double height = getElevation(coordGeo.lon(), coordGeo.lat());
|
||
m_ui->sbHeight1SBox->setValue(height);
|
||
|
||
updateActorComboBox(coordGeo);
|
||
}
|
||
|
||
bool ExplosionsEdit::isSmoke()
|
||
{
|
||
const qint32 chargeTypeId = m_ui->cbChargeType->currentData().toInt();
|
||
const qint32 igniterTypeId = m_ui->cbIgniterType->currentData().toInt();
|
||
|
||
if (chargeTypeId <= 0)
|
||
return false;
|
||
if (m_pdmDtc)
|
||
return m_pdmDtc->isSmoke(chargeTypeId, igniterTypeId, 0); // NOTE: IgniterFunciton = 0 as long as ExplosionsEdit does not contain values
|
||
return false;
|
||
}
|
||
|
||
void ExplosionsEdit::fillParticipantUnit()
|
||
{
|
||
QHash<int, QSet<int>> units;
|
||
const int partyId = m_ui->cbParty->currentData().toInt();
|
||
gatherParticipantUnits(partyId, units);
|
||
|
||
QSignalBlocker locked(m_ui->cbParticipantUnits);
|
||
|
||
const QVariant &savedCurrentData = m_ui->cbParticipantUnits->currentData();
|
||
|
||
m_ui->cbParticipantUnits->clear();
|
||
// // m_ui->cbParticipantUnits->addItem(QString()); // empty item
|
||
|
||
// sort displayed units texts on combobox
|
||
std::vector<std::tuple<QString, int, int>> unitList;
|
||
// Name, partId, count
|
||
const QList<int> &superiorIds = units.keys();
|
||
for (auto &superiorId : superiorIds)
|
||
{
|
||
const ParticipantManagement::Participant &part = m_ppmDtc->participant(superiorId);
|
||
unitList.push_back(std::tuple<QString, int, int>(QString::fromStdString(part.designator()) + QStringLiteral(" (%1)").arg(units.value(superiorId).count()),
|
||
part.id(),
|
||
units.value(superiorId).count()));
|
||
}
|
||
std::sort(unitList.begin(), unitList.end());
|
||
|
||
// set Combobox
|
||
int idx = 0;
|
||
for (const auto &ul : unitList)
|
||
{
|
||
m_ui->cbParticipantUnits->insertItem(idx, std::get<0>(ul), std::get<1>(ul));
|
||
m_ui->cbParticipantUnits->setItemData(idx, std::get<2>(ul), Qt::UserRole + 1);
|
||
++idx;
|
||
}
|
||
|
||
// restore current if possible, otherwise: if just one, preSelect that:
|
||
int idxToRestore = m_ui->cbParticipantUnits->findData(savedCurrentData);
|
||
if (idxToRestore >= 0)
|
||
m_ui->cbParticipantUnits->setCurrentIndex(idxToRestore);
|
||
|
||
// call xyChanged slot direct:
|
||
onParticipantUnitChanged(-1); // idx is not used within function.
|
||
}
|
||
|
||
void ExplosionsEdit::onFirePurposeChanged()
|
||
{
|
||
const qint32 currentFirePurposeId = m_ui->cbFirePurpose->currentData().toInt();
|
||
|
||
/*
|
||
// in 3D Wisi we need the duration otherwiese the old area effect simulation will fail or behave unexpected
|
||
const bool hasDuration = m_pdmDtc->getWithDurationFirePurpose(currentFirePurposeId);
|
||
m_ui->lblDuration->setVisible(hasDuration);
|
||
m_ui->sbDurationSec->setVisible(hasDuration);
|
||
*/
|
||
const int amountFireMarker = m_pdmDtc->getAmountFireMarkers(currentFirePurposeId);
|
||
m_ui->cbActors_2->setEnabled(amountFireMarker > 1 && m_ui->gbEffectMarkingInTa->isChecked());
|
||
}
|
||
|
||
void ExplosionsEdit::onDropAngleChanged() { doCalculations(true); }
|
||
|
||
void ExplosionsEdit::onWidthChanged(int width)
|
||
{
|
||
if (m_currentGeometry == AreaEffectSimulation::AesExplosionGeometry_def::EgRadial)
|
||
{
|
||
m_ui->sbDimensionY_Tiefe->setValue(width);
|
||
|
||
return;
|
||
}
|
||
|
||
if (m_ui->positionEdit->coordinate() == QPoint() || m_ui->positionEdit_2->coordinate() == QPoint()
|
||
|| m_currentGeometry != AreaEffectSimulation::AesExplosionGeometry::EgLine)
|
||
return;
|
||
|
||
const LtsCoordTrans::UtmCoordinate utm1 = LtsCoordTrans::UtmCoordinate::fromGeographic(m_ui->positionEdit->coordinate());
|
||
const LtsCoordTrans::UtmCoordinate utm2 = LtsCoordTrans::UtmCoordinate::fromGeographic(m_ui->positionEdit_2->coordinate());
|
||
QLineF line(utm1.toPoint(), utm2.toPoint());
|
||
|
||
line.setLength(width);
|
||
|
||
const LtsCoordTrans::UtmCoordinate utm2New = LtsCoordTrans::UtmCoordinate(utm2.zone, line.p2().x(), line.p2().y());
|
||
const QSignalBlocker signalBlocker(m_ui->positionEdit_2);
|
||
|
||
m_ui->positionEdit_2->setCoordinate(utm2New.geographic());
|
||
}
|
||
|
||
void ExplosionsEdit::onAimingAxisChanged(int aimingAxis)
|
||
{
|
||
if (m_ui->positionEdit->coordinate() == QPoint() || m_ui->positionEdit_2->coordinate() == QPoint()
|
||
|| m_currentGeometry != AreaEffectSimulation::AesExplosionGeometry::EgLine)
|
||
return;
|
||
|
||
const LtsCoordTrans::UtmCoordinate utm1 = LtsCoordTrans::UtmCoordinate::fromGeographic(m_ui->positionEdit->coordinate());
|
||
const LtsCoordTrans::UtmCoordinate utm2 = LtsCoordTrans::UtmCoordinate::fromGeographic(m_ui->positionEdit_2->coordinate());
|
||
QLineF line(utm1.toPoint(), utm2.toPoint());
|
||
|
||
line.setAngle(fmod((aimingAxis * 360.0 / 6400.0) - 90.0 + 360.0, 360.0));
|
||
|
||
const LtsCoordTrans::UtmCoordinate utm2New = LtsCoordTrans::UtmCoordinate(utm2.zone, line.p2().x(), line.p2().y());
|
||
const QSignalBlocker signalBlocker(m_ui->positionEdit_2);
|
||
|
||
m_ui->positionEdit_2->setCoordinate(utm2New.geographic());
|
||
|
||
const Core::GeoPosition coordGeo(utm2New.geographic().x(), utm2New.geographic().y(), 0);
|
||
const double height = getElevation(coordGeo.lon(), coordGeo.lat());
|
||
m_ui->sbHeight1SBox->setValue(height);
|
||
}
|
||
|
||
void ExplosionsEdit::onLineP1Changed(const QPointF &point)
|
||
{
|
||
if (m_ui->positionEdit_2->coordinate() == QPoint() || m_currentGeometry != AreaEffectSimulation::AesExplosionGeometry::EgLine)
|
||
return;
|
||
|
||
const LtsCoordTrans::UtmCoordinate utm1 = LtsCoordTrans::UtmCoordinate::fromGeographic(point);
|
||
const LtsCoordTrans::UtmCoordinate utm2 = LtsCoordTrans::UtmCoordinate::fromGeographic(m_ui->positionEdit_2->coordinate());
|
||
const QLineF line(utm1.toPoint(), utm2.toPoint());
|
||
const QSignalBlocker signalBlockerAxis(m_ui->sbAimingAxis);
|
||
const QSignalBlocker signalBlockerWidth(m_ui->sbDimensionX_BreiteRadius);
|
||
|
||
m_ui->sbAimingAxis->setValue(fmod((line.angle() + 90.0) * 6400 / 360, 6400));
|
||
m_ui->sbDimensionX_BreiteRadius->setValue(line.length());
|
||
}
|
||
|
||
void ExplosionsEdit::onLineP2Changed(const QPointF &point)
|
||
{
|
||
if (m_ui->positionEdit->coordinate() == QPoint() || m_currentGeometry != AreaEffectSimulation::AesExplosionGeometry::EgLine)
|
||
return;
|
||
|
||
const LtsCoordTrans::UtmCoordinate utm1 = LtsCoordTrans::UtmCoordinate::fromGeographic(m_ui->positionEdit->coordinate());
|
||
const LtsCoordTrans::UtmCoordinate utm2 = LtsCoordTrans::UtmCoordinate::fromGeographic(point);
|
||
const QLineF line(utm1.toPoint(), utm2.toPoint());
|
||
const QSignalBlocker signalBlockerAxis(m_ui->sbAimingAxis);
|
||
const QSignalBlocker signalBlockerWidth(m_ui->sbDimensionX_BreiteRadius);
|
||
|
||
m_ui->sbAimingAxis->setValue(fmod((line.angle() + 90.0) * 6400 / 360, 6400));
|
||
m_ui->sbDimensionX_BreiteRadius->setValue(line.length());
|
||
}
|
||
|
||
void ExplosionsEdit::onPeakHeigthChanged(int data)
|
||
{
|
||
Q_UNUSED(data);
|
||
|
||
switchTo(FlighDurationOrPeakHeight::FDPH_peakHeight);
|
||
|
||
QSpinBox *sender = m_ui->sBox_peakHeight;
|
||
if (sender)
|
||
doCalculations();
|
||
}
|
||
void ExplosionsEdit::onFlightDurationChanged(int data)
|
||
{
|
||
Q_UNUSED(data);
|
||
|
||
switchTo(FlighDurationOrPeakHeight::FDPH_flightDuration);
|
||
|
||
QSpinBox *sender = m_ui->sBox_flightDuration;
|
||
if (!sender)
|
||
return;
|
||
doCalculations();
|
||
}
|
||
|
||
void ExplosionsEdit::onEffectDurationChanged(int value) { m_ui->sbDurationSec->setToolTip(QDateTime::fromSecsSinceEpoch(value).toString(QStringLiteral("mm:ss"))); }
|
||
|
||
void ExplosionsEdit::setMinMaxConstraints(ExplosionsEdit::FlighDurationOrPeakHeight phfd, ProjectDataManagement::GearClass_def::type gearClass, int chargeTypeId)
|
||
{
|
||
DEBUG_LOG(m_logger) << " setMinMaxConstraints START";
|
||
|
||
const QPair<int, int> &minMaxFd = getMinMaxFlightDurationSec(gearClass, chargeTypeId);
|
||
const QPair<int, int> &minMaxPh = getMinMaxPeakHeight(gearClass, chargeTypeId);
|
||
|
||
switch (phfd)
|
||
{
|
||
case FlighDurationOrPeakHeight::FDPH_flightDuration:
|
||
{
|
||
m_ui->sBox_flightDuration->setMinimum(minMaxFd.first);
|
||
m_ui->sBox_flightDuration->setMaximum(minMaxFd.second);
|
||
m_ui->sBox_flightDuration->setToolTip(QString(QStringLiteral("%1 - %2")).arg(m_ui->sBox_flightDuration->minimum()).arg(m_ui->sBox_flightDuration->maximum()));
|
||
{
|
||
// release constraints of other
|
||
m_ui->sBox_peakHeight->setMinimum(0);
|
||
m_ui->sBox_peakHeight->setMaximum(std::numeric_limits<int>::max());
|
||
// but show their limitations:
|
||
m_ui->sBox_peakHeight->setToolTip(QString(QStringLiteral("%1 - %2")).arg(minMaxPh.first).arg(minMaxPh.second));
|
||
}
|
||
// if constraints changes, we have to call setValueAccordingContraints again:
|
||
setValueAccordingContraints(m_ui->sBox_flightDuration->value(), phfd);
|
||
}
|
||
break;
|
||
case FlighDurationOrPeakHeight::FDPH_peakHeight:
|
||
{
|
||
m_ui->sBox_peakHeight->setMinimum(minMaxPh.first);
|
||
m_ui->sBox_peakHeight->setMaximum(minMaxPh.second);
|
||
m_ui->sBox_peakHeight->setToolTip(QString(QStringLiteral("%1 - %2")).arg(m_ui->sBox_peakHeight->minimum()).arg(m_ui->sBox_peakHeight->maximum()));
|
||
{
|
||
// release constraints of other
|
||
m_ui->sBox_flightDuration->setMinimum(0);
|
||
m_ui->sBox_flightDuration->setMaximum(std::numeric_limits<int>::max());
|
||
// but show their limitations:
|
||
m_ui->sBox_flightDuration->setToolTip(QString(QStringLiteral("%1 - %2")).arg(minMaxFd.first).arg(minMaxFd.second));
|
||
}
|
||
// if constraints changes, we have to call setValueAccordingContraints again:
|
||
setValueAccordingContraints(m_ui->sBox_peakHeight->value(), phfd);
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
DEBUG_LOG(m_logger) << " setMinMaxConstraints END";
|
||
}
|
||
|
||
bool ExplosionsEdit::setValueAccordingContraints(int newValue, ExplosionsEdit::FlighDurationOrPeakHeight phfd)
|
||
{
|
||
DEBUG_LOG(m_logger) << " setValueAccordingContraints START, new Val = " << newValue << " # " << toString(phfd);
|
||
bool bRet = false;
|
||
|
||
const qint32 gearClassId = m_ui->cbGearClass->currentData().toInt();
|
||
const ProjectDataManagement::GearClass::type gearClass = static_cast<ProjectDataManagement::GearClass::type>(gearClassId);
|
||
const qint32 chargeTypeId = m_ui->cbChargeType->currentData().toInt();
|
||
|
||
switch (phfd)
|
||
{
|
||
case ExplosionsEdit::FlighDurationOrPeakHeight::FDPH_flightDuration:
|
||
{
|
||
bRet = isInBounds(newValue, getMinMaxFlightDurationSec(gearClass, chargeTypeId));
|
||
if (bRet)
|
||
{
|
||
m_ui->sBox_flightDuration->setValue(newValue);
|
||
m_ui->sBox_flightDuration->setProperty(VALUE_VALID_PROPERTY_NAME, true);
|
||
}
|
||
else
|
||
{
|
||
m_ui->sBox_flightDuration->setStyleSheet(QStringLiteral("background-color: orange;"));
|
||
m_ui->sBox_flightDuration->setProperty(VALUE_VALID_PROPERTY_NAME, false);
|
||
}
|
||
}
|
||
break;
|
||
case ExplosionsEdit::FlighDurationOrPeakHeight::FDPH_peakHeight:
|
||
{
|
||
bRet = isInBounds(newValue, getMinMaxPeakHeight(gearClass, chargeTypeId));
|
||
if (bRet)
|
||
{
|
||
m_ui->sBox_peakHeight->setValue(newValue);
|
||
m_ui->sBox_peakHeight->setProperty(VALUE_VALID_PROPERTY_NAME, true);
|
||
}
|
||
else
|
||
{
|
||
m_ui->sBox_peakHeight->setStyleSheet(QStringLiteral("background-color: orange;"));
|
||
m_ui->sBox_peakHeight->setProperty(VALUE_VALID_PROPERTY_NAME, false);
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
INFO_LOG(m_logger) << " setValueAccordingContraints END, new Val = " << newValue << " # " << toString(phfd) << ", returns " << bRet;
|
||
return bRet;
|
||
}
|
||
|
||
bool ExplosionsEdit::isGuiVulcano()
|
||
{
|
||
const int curChargeTypeId = m_ui->cbChargeType->currentData().toInt();
|
||
return chargeTypeIdIsVulcano(curChargeTypeId);
|
||
}
|
||
|
||
void ExplosionsEdit::switchTo(FlighDurationOrPeakHeight phfd)
|
||
{
|
||
INFO_LOG(m_logger) << " switchTo " << toString(phfd);
|
||
switch (phfd)
|
||
{
|
||
case FlighDurationOrPeakHeight::FDPH_flightDuration:
|
||
m_ui->sBox_flightDuration->setStyleSheet(QLatin1String());
|
||
m_ui->sBox_peakHeight->setStyleSheet(QStringLiteral("background-color: gray;"));
|
||
break;
|
||
case FlighDurationOrPeakHeight::FDPH_peakHeight:
|
||
m_ui->sBox_flightDuration->setStyleSheet(QStringLiteral("background-color: gray;"));
|
||
m_ui->sBox_peakHeight->setStyleSheet(QLatin1String());
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
const qint32 gearClassId = m_ui->cbGearClass->currentData().toInt();
|
||
ProjectDataManagement::GearClass::type gearClassType = static_cast<ProjectDataManagement::GearClass::type>(gearClassId);
|
||
const qint32 chargeTypeId = m_ui->cbChargeType->currentData().toInt();
|
||
setMinMaxConstraints(phfd, gearClassType, chargeTypeId);
|
||
}
|
||
|
||
class DoCalculationsExitGuard
|
||
{
|
||
public:
|
||
DoCalculationsExitGuard(ExplosionsEdit *parent, bool executeEnableUiElements)
|
||
: m_parent(parent)
|
||
, m_executeEnableUiElements(executeEnableUiElements)
|
||
{
|
||
}
|
||
~DoCalculationsExitGuard()
|
||
{
|
||
if (!m_parent)
|
||
return;
|
||
m_parent->updateAnnotationStr();
|
||
if (m_executeEnableUiElements)
|
||
m_parent->enableUiElements();
|
||
}
|
||
ExplosionsEdit *m_parent{nullptr};
|
||
bool m_executeEnableUiElements{true};
|
||
};
|
||
|
||
void ExplosionsEdit::doCalculations(bool executeEnableUiElements)
|
||
{
|
||
if (!m_initDone)
|
||
return;
|
||
|
||
// do not update while EsPause or similar:
|
||
if ((m_explosionClusterData.effectStateId() == AesEffectState_def::EsPaused) || (m_explosionClusterData.effectStateId() == AesEffectState_def::EsExpired))
|
||
return;
|
||
|
||
DoCalculationsExitGuard exitGuard(this, executeEnableUiElements);
|
||
|
||
QObject *senderObj = sender();
|
||
qDebug() << " doCalculations, sender = " << senderObj;
|
||
|
||
resetTempAndAnnotationStr();
|
||
|
||
// startObj / firingUnit
|
||
const int firingUnitPartId = m_ui->cbParticipantUnits->currentData(Qt::UserRole).toInt();
|
||
if (firingUnitPartId <= 0)
|
||
{
|
||
INFO_LOG(m_logger) << " firingUnitPartId invalid, firingUnitPartId = " << firingUnitPartId;
|
||
addAnnotationStr(tr("no originator"));
|
||
return;
|
||
}
|
||
|
||
const QPointF ¢erFiringUnitGeo = centerOfFiringUnitGeo(firingUnitPartId);
|
||
if (centerFiringUnitGeo.isNull())
|
||
{
|
||
INFO_LOG(m_logger) << LOGFUNCTION << " centerOfFiringUnitGeo is null for firingUnitPartId: " << firingUnitPartId;
|
||
addAnnotationStr(tr("originator has no position"));
|
||
return;
|
||
}
|
||
|
||
// endObj / Area
|
||
const QPointF &posCoordGeo = m_coord;
|
||
|
||
if (posCoordGeo.isNull())
|
||
{
|
||
INFO_LOG(m_logger) << "posCoordGeo is null";
|
||
addAnnotationStr(tr("no target origin"));
|
||
return;
|
||
}
|
||
|
||
LtsUtils::QPoint3D startPos, endPos;
|
||
|
||
// toUtm: (pay attention to the zone)
|
||
// add z-Values:
|
||
{
|
||
LtsCoordTrans::UtmCoordinate endPosUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(posCoordGeo);
|
||
LtsCoordTrans::UtmCoordinate startPosUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(centerFiringUnitGeo, endPosUtm.zone);
|
||
|
||
m_temporaryData.m_distanceFiringToTarget = LtsCoordTrans::distance(endPosUtm, startPosUtm);
|
||
|
||
// static LtsElevation::LTS_ELEVATION_MANAGER_TYPE s_ltsElevationManager = nullptr;
|
||
// if (!s_ltsElevationManager)
|
||
// {
|
||
// std::shared_ptr<AssetManagement::Interface::Dtc> asmDtc;
|
||
// if (!m_session->getDtc<AssetManagement::DataInterface>(asmDtc))
|
||
// {
|
||
// WARNING_LOG(m_logger) << "getDtc for AssetManagement::DataInterface failed.";
|
||
// }
|
||
|
||
// std::shared_ptr<ExerciseManagement::Interface::Dtc> exmDtc;
|
||
// if (!m_session->getDtc<ExerciseManagement::DataInterface>(exmDtc))
|
||
// {
|
||
// WARNING_LOG(m_logger) << "getDtc for ExerciseManagement::DataInterface failed.";
|
||
// }
|
||
// s_ltsElevationManager = LtsElevation::LtsElevationManager::createInstance(m_session->exerciseId(), exmDtc, asmDtc);
|
||
// }
|
||
|
||
if (!m_elevationManager)
|
||
{
|
||
m_temporaryData.m_annotationStringFiring4Dlg = tr("Elevation failed");
|
||
WARNING_LOG(m_logger) << "LtsElevationManager::createInstance.";
|
||
return;
|
||
}
|
||
|
||
double zValue = getElevation(centerFiringUnitGeo.x(), centerFiringUnitGeo.y());
|
||
if (zValue < -300)
|
||
{
|
||
WARNING_LOG(m_logger) << "centerFiringUnitGeo zValue is very small, set to 100";
|
||
zValue = 100;
|
||
}
|
||
startPos = LtsUtils::QPoint3D(startPosUtm.x, startPosUtm.y, zValue);
|
||
|
||
zValue = getElevation(posCoordGeo.x(), posCoordGeo.y());
|
||
if (zValue < -300)
|
||
{
|
||
WARNING_LOG(m_logger) << "posCoordGeo zValue is very small, set to 100";
|
||
zValue = 100;
|
||
}
|
||
endPos = LtsUtils::QPoint3D(endPosUtm.x, endPosUtm.y, zValue);
|
||
}
|
||
|
||
if (isGuiVulcano())
|
||
{
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " vulcano...";
|
||
|
||
const double vOut = TrajectoryGeneratorVulcano::defaultv0();
|
||
const double betaRad = XY::calculateBetaRad(vOut, m_ui->sBox_peakHeight->value()); // betaRad == Abschusswinkel
|
||
const double alphaRad = currentDropAngleRadiant();
|
||
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " vulcano... "
|
||
<< ", vOut = " << vOut << ", betaDeg = " << betaRad * LtsCoordTrans::rad2deg() << ", alphaDeg = " << alphaRad * LtsCoordTrans::rad2deg();
|
||
|
||
// ErrorHandling
|
||
if (std::isnan(betaRad) || (betaRad * LtsCoordTrans::rad2deg() < 0) || (betaRad * LtsCoordTrans::rad2deg() >= 90))
|
||
{
|
||
ERROR_LOG(m_logger) << LOGFUNCTION << " calculateBetaRad failed."
|
||
<< ", betaRad = " << betaRad << "'";
|
||
|
||
addAnnotationStr(tr("calculateBetaRad failed (%1)").arg(betaRad));
|
||
return;
|
||
}
|
||
|
||
TrajectoryGeneratorVulcano::Options opts = TrajectoryGeneratorVulcano::OptIncludeIdealHit;
|
||
|
||
TrajectoryGeoPositionRelTime trajectoryOut;
|
||
Core::GeoPosition idealTapAbsoluteGeoOut;
|
||
QString reasonOut;
|
||
|
||
const QPoint3D startPos_geo3D(centerFiringUnitGeo.x(), centerFiringUnitGeo.y(), startPos.z());
|
||
const QPoint3D endPos_geo3D(posCoordGeo.x(), posCoordGeo.y(), endPos.z());
|
||
|
||
TrajectoryGenerator::generateTrajectoryVulcano(startPos_geo3D,
|
||
endPos_geo3D,
|
||
vOut,
|
||
betaRad,
|
||
alphaRad,
|
||
TrajectoryGeneratorVulcano::defaultTapTimeSec(),
|
||
opts,
|
||
trajectoryOut,
|
||
idealTapAbsoluteGeoOut,
|
||
&reasonOut);
|
||
|
||
// ErrorHandling
|
||
if (!reasonOut.isEmpty())
|
||
{
|
||
WARNING_LOG(m_logger) << LOGFUNCTION << " TrajectoryGenerator::generateTrajectoryVulcano failed."
|
||
<< ", reasonOut = " << reasonOut << "'";
|
||
|
||
addAnnotationStr(tr("flightCurve failed (%1)").arg(reasonOut));
|
||
return;
|
||
}
|
||
|
||
if (trajectoryOut.empty())
|
||
{
|
||
ERROR_LOG(m_logger) << LOGFUNCTION << " TrajectoryGenerator::generateTrajectoryVulcano failed."
|
||
<< ", trajectoryOut is empty";
|
||
|
||
addAnnotationStr(tr("flightCurve failed (size=%1)").arg(trajectoryOut.size()));
|
||
return;
|
||
}
|
||
|
||
const qint64 calculatedFlightDurationMsec = trajectoryOut.back().relativeTime();
|
||
m_temporaryData.m_estimatedFlightDurationMsec = Polygons::explosionClusterEstimatedMaxFlightDuration2DMSec(calculatedFlightDurationMsec); // ( *1.1) + (10 *1000)
|
||
m_temporaryData.m_idealTapAbsoluteGeoOut = idealTapAbsoluteGeoOut;
|
||
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " calculatedFlightDurationMsec " << calculatedFlightDurationMsec;
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_estimatedFlightDurationMsec " << m_temporaryData.m_estimatedFlightDurationMsec;
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_estimatedFlightDurationSec " << m_temporaryData.m_estimatedFlightDurationMsec / 1000.0;
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_distanceFiringToTarget " << m_temporaryData.m_distanceFiringToTarget;
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " idealTapAbsoluteGeoOut " << m_temporaryData.m_idealTapAbsoluteGeoOut;
|
||
|
||
m_temporaryData.m_annotationStringFiring4Dlg = tr("Firing: %1° %2 m/s %3 sec TAP: %4 m")
|
||
.arg(std::round(betaRad * LtsCoordTrans::rad2deg() * 10) / 10)
|
||
.arg(std::round(vOut))
|
||
.arg(std::round(calculatedFlightDurationMsec / 1000.0))
|
||
.arg(std::round(m_temporaryData.m_idealTapAbsoluteGeoOut.z()));
|
||
|
||
QSignalBlocker locked(m_ui->sBox_flightDuration);
|
||
m_ui->sBox_flightDuration->setValue(std::round(calculatedFlightDurationMsec / 1000.0));
|
||
|
||
return;
|
||
}
|
||
|
||
QString reasonOut;
|
||
double betaRadiantOut, vOut;
|
||
|
||
const bool usePeakHeight = m_ui->sBox_peakHeight->styleSheet().isEmpty();
|
||
const bool useFlightDuration = m_ui->sBox_flightDuration->styleSheet().isEmpty();
|
||
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << ", usePeakHeight " << usePeakHeight << ", useFlightDuration " << useFlightDuration;
|
||
if (usePeakHeight)
|
||
{
|
||
// peak to duration
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " peak to duration...";
|
||
const double heightAbs = startPos.z() + m_ui->sBox_peakHeight->value();
|
||
const qint64 durationSec = TrajectoryHelper::flightDurationSec(startPos, endPos, heightAbs, &betaRadiantOut, &vOut, &reasonOut);
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " peak to duration... durationSec=" << durationSec << ", reasonOut=" << reasonOut;
|
||
if (!reasonOut.isEmpty())
|
||
{
|
||
ERROR_LOG(m_logger) << LOGFUNCTION << " TrajectoryHelper::flightDurationSec failed."
|
||
<< ", reasonOut = " << reasonOut << "'";
|
||
|
||
addAnnotationStr(tr("flightDurationSec failed (%1)").arg(reasonOut));
|
||
return;
|
||
}
|
||
|
||
m_temporaryData.m_annotationStringFiring4Dlg = tr("Firing: %1° %2 m/s").arg(std::round(betaRadiantOut * LtsCoordTrans::rad2deg() * 10) / 10).arg(std::round(vOut));
|
||
|
||
QSignalBlocker locked(m_ui->sBox_flightDuration);
|
||
if (!setValueAccordingContraints(durationSec, FlighDurationOrPeakHeight::FDPH_flightDuration))
|
||
addAnnotationStr(tr("FlightDuration (%1 sec) exceeds limits").arg(std::round(durationSec)));
|
||
}
|
||
else if (useFlightDuration)
|
||
{
|
||
// duration to peak
|
||
// DEBUG_LOG(m_logger) << LOGFUNCTION << " duration to peak...";
|
||
|
||
const double timeSec = m_ui->sBox_flightDuration->value();
|
||
const double peakHeightAbs = TrajectoryHelper::peakHeightAbs(startPos, endPos, timeSec, /*tm,*/ &betaRadiantOut, &vOut, &reasonOut);
|
||
const double peakHeightRel = peakHeightAbs - startPos.z();
|
||
|
||
if (!reasonOut.isEmpty())
|
||
{
|
||
ERROR_LOG(m_logger) << LOGFUNCTION << " TrajectoryHelper::peakHeightAbs failed."
|
||
<< ", reasonOut = " << reasonOut << "'";
|
||
|
||
addAnnotationStr(tr("peakHeightAbs failed (%1)").arg(reasonOut));
|
||
return;
|
||
}
|
||
|
||
m_temporaryData.m_annotationStringFiring4Dlg = tr("Firing: %1° %2 m/s").arg(std::round(betaRadiantOut * LtsCoordTrans::rad2deg() * 10) / 10).arg(std::round(vOut));
|
||
QSignalBlocker locked(m_ui->sBox_peakHeight);
|
||
if (!setValueAccordingContraints(peakHeightRel, FlighDurationOrPeakHeight::FDPH_peakHeight))
|
||
addAnnotationStr(tr("PeakHeight (%1 m rel) exceeds limits").arg(std::round(peakHeightRel)));
|
||
}
|
||
|
||
// calc estimatedFlightDuration: ! should be identical to svc !
|
||
Database::AesExplosionCluster explosionCluster;
|
||
explosionCluster.firingCenterPosition = LtsUtils::QPoint3D::fromQPointF(posCoordGeo);
|
||
explosionCluster.impactPosition = centerFiringUnitGeo;
|
||
if (usePeakHeight)
|
||
explosionCluster.peakHeight = m_ui->sBox_peakHeight->value();
|
||
if (useFlightDuration)
|
||
explosionCluster.flightDuration = m_ui->sBox_flightDuration->value() * 1000;
|
||
|
||
const qint64 calculatedFlightDurationMsec = Polygons::explosionClusterCalculatedFlightDuration2DMSec(explosionCluster);
|
||
m_temporaryData.m_estimatedFlightDurationMsec = Polygons::explosionClusterEstimatedMaxFlightDuration2DMSec(calculatedFlightDurationMsec); // ( *1.1) + (10 *1000)
|
||
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " calculatedFlightDurationMsec " << calculatedFlightDurationMsec;
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_estimatedFlightDurationMsec " << m_temporaryData.m_estimatedFlightDurationMsec;
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_estimatedFlightDurationSec " << m_temporaryData.m_estimatedFlightDurationMsec / 1000.0;
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_distanceFiringToTarget " << m_temporaryData.m_distanceFiringToTarget;
|
||
|
||
//
|
||
// check some contraints:
|
||
// [GUEZE-2458] , [GUEZE-2459]
|
||
//
|
||
|
||
const qint32 gearClassId = m_ui->cbGearClass->currentData().toInt();
|
||
ProjectDataManagement::GearClass::type gearClassType = static_cast<ProjectDataManagement::GearClass::type>(gearClassId);
|
||
|
||
const qint32 chargeTypeId = m_ui->cbChargeType->currentData().toInt();
|
||
|
||
if (m_temporaryData.m_distanceFiringToTarget < getMinDistance(gearClassType, chargeTypeId))
|
||
addAnnotationStr(tr("Originator target (%1 m) distance to low").arg(m_temporaryData.m_distanceFiringToTarget));
|
||
else if (m_temporaryData.m_distanceFiringToTarget > getMaxDistance(gearClassType, chargeTypeId))
|
||
addAnnotationStr(tr("Originator target (%1 m) distance to high").arg(m_temporaryData.m_distanceFiringToTarget));
|
||
|
||
// doCalcExitGuard will call enableUiElements
|
||
// if (executeEnableUiElements)
|
||
// enableUiElements();
|
||
// doCalcExitGuard will call enableUiElements
|
||
}
|
||
|
||
void ExplosionsEdit::onPartitionTimeChanged(qint64 newPartitionTimeMsec)
|
||
{
|
||
// TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
||
|
||
if (newPartitionTimeMsec <= 0)
|
||
newPartitionTimeMsec = m_session->partitionTime();
|
||
|
||
m_temporaryData.m_remainingTimeStr.clear();
|
||
|
||
LtsWidgets::LtsDateTimeEdit *dte = m_ui->dteEffectStart;
|
||
if (!dte)
|
||
return;
|
||
|
||
QString styleSheetStr;
|
||
qint64 timeUntilSecInclMinimum = 999999999; // simply big means okay :-)
|
||
|
||
// if there is visible a enabled/disable checkbox -> also allow 0 as effect start
|
||
if (dte->isCheckable() && dte->isChecked() && (!m_coord.isNull()))
|
||
{
|
||
qint64 timeUntilSec = (dte->msecsSinceEpoch() - m_temporaryData.m_estimatedFlightDurationMsec - newPartitionTimeMsec) / 1000;
|
||
timeUntilSecInclMinimum = timeUntilSec - s_timeUntilMinimumSec;
|
||
|
||
static const int timeFrame = 180; // 180 sec == 3 min
|
||
|
||
if (timeUntilSecInclMinimum <= 0)
|
||
styleSheetStr = QStringLiteral("QLineEdit { background-color: red; }");
|
||
else if (timeUntilSec < timeFrame)
|
||
{
|
||
// here timeUntilSec should be >= 10sec
|
||
|
||
double remainFactor = timeUntilSec / static_cast<double>(timeFrame);
|
||
if (remainFactor > 1)
|
||
remainFactor = 1;
|
||
|
||
const int opacity = 255 * remainFactor;
|
||
|
||
// DEBUG_LOG(m_logger) << LOGFUNCTION << " dte->msecsSinceEpoch() " << dte->msecsSinceEpoch()
|
||
// << ", m_estimatedFlightDurationMsec " << m_temporaryData.m_estimatedFlightDurationMsec
|
||
// << ", newPartitionTimeMsec " << newPartitionTimeMsec
|
||
// << ", dteEffectStart timeUntilSec " << timeUntilSec
|
||
// << ", remainFactor " << remainFactor
|
||
// << ", opacity " << opacity;
|
||
|
||
styleSheetStr = QString(QStringLiteral("QLineEdit { background-color: rgba(255, 165, 0, %1); }")).arg(255 - opacity);
|
||
}
|
||
|
||
if ((timeUntilSecInclMinimum >= 0) && (timeUntilSec < (60 * 60))) // 60*60 = 1h
|
||
m_temporaryData.m_remainingTimeStr = tr("remaining editing time: %1").arg(QDateTime::fromSecsSinceEpoch(timeUntilSecInclMinimum).toString(QStringLiteral("mm:ss")));
|
||
|
||
} // if (dte->withCheckBox() && ...
|
||
|
||
// DEBUG_LOG(m_logger) << LOGFUNCTION << " dteEffectStart styleSheetStr = '" << styleSheetStr << "'" << ", timeUntilSec >= 10 -> " << (timeUntilSec >= 10);
|
||
|
||
// property:
|
||
dte->setProperty(VALUE_VALID_PROPERTY_NAME, timeUntilSecInclMinimum > 0);
|
||
|
||
dte->setStyleSheet(styleSheetStr);
|
||
|
||
updateAnnotationStr();
|
||
|
||
// TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
||
}
|
||
|
||
/*!
|
||
* \brief ExplosionsEdit::dialogIdentifier
|
||
* \return
|
||
*/
|
||
QString ExplosionsEdit::dialogIdentifier() const
|
||
{
|
||
return m_aesItem.id() <= 0 ? QStringLiteral("AddExplosionsArtillery") : QStringLiteral("EditExplosionsArtillery%1").arg(m_aesItem.id());
|
||
}
|
||
|
||
void ExplosionsEdit::onExplosionClusterChanged(const ExplosionCluster &ecData, DataModification modType)
|
||
{
|
||
if (ecData.id() != m_aesItem.id())
|
||
return;
|
||
|
||
BaseEdit::onAreaChanged(ecData, modType);
|
||
|
||
if (modType == DataModification::eRemoved)
|
||
{
|
||
m_explosionClusterData = ExplosionCluster();
|
||
return;
|
||
}
|
||
|
||
// detect, if isObjectModifiable() changes to false:
|
||
if (
|
||
// (m_explosionClusterData.isObjectModifiable()) &&
|
||
(!ecData.isObjectModifiable()))
|
||
{
|
||
INFO_LOG(m_logger) << " object becames not modifiable";
|
||
enableUiFireButton(); // "Start" -> "Pause"...
|
||
if (!m_enableFireBtnTimer)
|
||
{
|
||
m_enableFireBtnTimer = new QTimer(this);
|
||
// m_enableFireBtnTimer->setSingleShot(true);
|
||
m_enableFireBtnTimer->setInterval(5000);
|
||
connect(m_enableFireBtnTimer, &QTimer::timeout, this, &ExplosionsEdit::enableUiFireButton, Qt::UniqueConnection);
|
||
}
|
||
m_enableFireBtnTimer->start();
|
||
}
|
||
|
||
m_explosionClusterData = ecData;
|
||
m_currentGeometry = m_explosionClusterData.explosionGeometry();
|
||
|
||
if (ecData.flightCurve().is_set())
|
||
{
|
||
const FlightCurve &fc = ecData.flightCurve().get();
|
||
m_ui->leCalcFlightDuration->setText(QString(QStringLiteral("%1 sec")).arg(fc.calculatedFlightDuration() / 1000.0));
|
||
m_ui->leCalcPeakHeight->setText(QString(QStringLiteral("%1 m abs")).arg(fc.calculatedPeakHeightAbs()));
|
||
}
|
||
else
|
||
{
|
||
m_ui->leCalcFlightDuration->setText(QString(QStringLiteral("- sec")));
|
||
m_ui->leCalcPeakHeight->setText(QString(QStringLiteral("- m abs")));
|
||
}
|
||
|
||
m_ui->leDescription->setText(QString::fromStdString(ecData.annotationString()));
|
||
m_ui->leDescription->setToolTip(m_ui->leDescription->text());
|
||
|
||
if (!m_ui->sbDurationSec->isEnabled())
|
||
{
|
||
const bool hasChanges = hasUnsavedChanges();
|
||
m_ui->sbDurationSec->setValue(m_explosionClusterData.effectDuration() / 1000); // msec -> sec
|
||
if (!hasChanges)
|
||
__unchange__();
|
||
}
|
||
|
||
m_uiRequirements->checkRequirements(this);
|
||
|
||
doCalculations(true);
|
||
}
|
||
|
||
void ExplosionsEdit::handleLaserPrecisionChargeType(const int chargeTypeId)
|
||
{
|
||
const auto geoButtons = m_geometrieButtonGroup->buttons();
|
||
if (chargeTypeId == s_laserPrecisionChargeTypeId)
|
||
{
|
||
onGeometrySelected(static_cast<int>(AreaEffectSimulation::AesExplosionGeometry_def::EgPoint));
|
||
for (QAbstractButton *btn : geoButtons)
|
||
{
|
||
if (m_geometrieButtonGroup->id(btn) != static_cast<int>(AreaEffectSimulation::AesExplosionGeometry_def::EgPoint))
|
||
btn->setCheckable(false);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (QAbstractButton *btn : geoButtons)
|
||
btn->setCheckable(true);
|
||
}
|
||
}
|
||
|
||
QPointF ExplosionsEdit::centerOfFiringUnitGeo(int particpantUnitId)
|
||
{
|
||
if (!m_sitMapGuiIfc || !m_ppmDtc)
|
||
return {};
|
||
if (particpantUnitId <= 0)
|
||
return {};
|
||
|
||
std::vector<Core::GeoPosition> singlePositionsOut_unused;
|
||
const LtsUtils::QPoint3D ¢erCoordGeo = Polygons::calculateFiringUnitCenterNonRecursive(m_ppmDtc, particpantUnitId, singlePositionsOut_unused, m_logger);
|
||
if (centerCoordGeo.isNull() || std::isnan(centerCoordGeo.x()))
|
||
{
|
||
// may happen in real life: so WARNING_LOG to DEBUG_LOG
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " calculateFiringUnitCenterNonRecursive failed, particpantUnitId = " << particpantUnitId;
|
||
return {};
|
||
}
|
||
return centerCoordGeo.toPointF();
|
||
}
|
||
|
||
void ExplosionsEdit::showCenterOfFiringUnit(int particpantUnitId)
|
||
{
|
||
if (!m_sitMapGuiIfc || !m_ppmDtc)
|
||
return;
|
||
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START particpantUnitId: " << particpantUnitId;
|
||
if (particpantUnitId <= 0)
|
||
{
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " showCenterOfFiringUnit: " << particpantUnitId << ", removeObjectIndicator";
|
||
m_sitMapGuiIfc->removeObjectIndicator();
|
||
return;
|
||
}
|
||
|
||
const QPointF ¢erFiringUnitGeo = centerOfFiringUnitGeo(particpantUnitId);
|
||
|
||
if (centerFiringUnitGeo.isNull())
|
||
{
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " showCenterOfFiringUnit: " << particpantUnitId << ", removeObjectIndicator due to invalid position";
|
||
m_sitMapGuiIfc->removeObjectIndicator();
|
||
return;
|
||
}
|
||
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " END particpantUnitId: " << particpantUnitId;
|
||
m_sitMapGuiIfc->objectIndicatorToPoint(centerFiringUnitGeo); // you can provide a duration: lifetimeMsec = -1
|
||
}
|
||
|
||
/*!
|
||
* \brief ExplosionsEdit::retranslate
|
||
*/
|
||
void ExplosionsEdit::retranslate()
|
||
{
|
||
QString title = tr("Artillery");
|
||
|
||
translate(m_ui, m_aesItem, title);
|
||
}
|
||
|
||
void ExplosionsEdit::enableUiElements()
|
||
{
|
||
DEBUG_LOG(m_logger) << " enableUiElements()"
|
||
<< ", m_explosionClusterData.id() = " << m_explosionClusterData.id() << ", isModifiable() = " << isModifiable()
|
||
<< ", isOnlyNameModifiable() = " << isOnlyNameModifiable() << ", ena = " << (isModifiable() && !isOnlyNameModifiable());
|
||
|
||
if (m_explosionClusterData.id() == 0) // while creation, not modification
|
||
enableUiElements(true);
|
||
else
|
||
enableUiElements(isModifiable() && !isOnlyNameModifiable());
|
||
}
|
||
|
||
/*!
|
||
* \brief ExplosionsEdit::enableUiElements
|
||
* \param enable
|
||
*/
|
||
void ExplosionsEdit::enableUiElements(bool enable)
|
||
{
|
||
DEBUG_LOG(m_logger) << "************* enableUiElements enable=" << enable << ", isOnlyNameModifiable = " << isOnlyNameModifiable();
|
||
|
||
// // onGeometrySelected(static_cast<int>(m_explosionClusterData.explosionGeometry().underlying()));
|
||
|
||
m_ui->cbChargeType->setEnabled(enable);
|
||
m_ui->cbIgniterType->setEnabled(enable && m_ui->cbIgniterType->count() > 1);
|
||
m_ui->sbDurationSec->setEnabled(enable);
|
||
m_ui->cbParty->setEnabled(enable);
|
||
m_ui->cbParticipantUnits->setEnabled(enable);
|
||
setEffectStartEnabled(m_ui->dteEffectStart, enable);
|
||
|
||
m_ui->pbPosition->setEnabled(m_sitMapGuiIfc && enable);
|
||
m_ui->positionEdit->setEnabled(enable);
|
||
m_ui->sbDimensionX_BreiteRadius->setEnabled(enable
|
||
&& (m_currentGeometry == AreaEffectSimulation::AesExplosionGeometry_def::EgArea
|
||
|| m_currentGeometry == AreaEffectSimulation::AesExplosionGeometry_def::EgRadial
|
||
|| m_currentGeometry == AreaEffectSimulation::AesExplosionGeometry_def::EgLine));
|
||
m_ui->sbDimensionY_Tiefe->setEnabled(enable && m_currentGeometry == AreaEffectSimulation::AesExplosionGeometry_def::EgArea);
|
||
m_ui->sbHeight1SBox->setEnabled(enable);
|
||
m_ui->sbHeight2SBox->setEnabled(enable && m_currentGeometry == AreaEffectSimulation::AesExplosionGeometry_def::EgLine);
|
||
m_ui->sbAimingAxis->setEnabled(
|
||
enable
|
||
&& (m_currentGeometry == AreaEffectSimulation::AesExplosionGeometry_def::EgArea || m_currentGeometry == AreaEffectSimulation::AesExplosionGeometry_def::EgLine));
|
||
m_ui->cbFirePurpose->setEnabled(enable);
|
||
m_ui->cbGearClass->setEnabled(enable);
|
||
m_ui->cbActors->setEnabled(enable & m_ui->gbEffectMarkingInTa->isChecked());
|
||
|
||
const bool bIsPoint = isPointSimilar(m_currentGeometry);
|
||
|
||
const bool bIsVulcano = isGuiVulcano();
|
||
m_ui->sbCannons->setEnabled(enable /*&& !bIsPoint*/ && !bIsVulcano);
|
||
m_ui->rbDropAngleDown->setEnabled(enable && bIsPoint && bIsVulcano);
|
||
m_ui->rbDropAngleUp->setEnabled(enable && bIsPoint && bIsVulcano);
|
||
|
||
// m_ui->sbRounds->setEnabled(enable && m_type == etArea);
|
||
// m_ui->cbEffectMarkingInTa->setEnabled(enable);
|
||
|
||
// [GUEZE-2960] Explizite Kunden-Forderung:
|
||
// ZpNumber nicht_ mehr modifizierbar, wenn Wirkfeld aktiv
|
||
m_ui->leZpNumber->setEnabled((enable || isOnlyNameModifiable()) && m_aesItem.effectStateId() != AesEffectState_def::EsActivated);
|
||
m_ui->leElement->setEnabled((enable || isOnlyNameModifiable()) && m_aesItem.effectStateId() != AesEffectState_def::EsActivated);
|
||
|
||
m_ui->pbClear->setEnabled(enable);
|
||
|
||
// tbc
|
||
m_ui->sbZog->setEnabled(enable);
|
||
m_ui->sBox_flightDuration->setEnabled(enable && !bIsVulcano);
|
||
m_ui->sBox_peakHeight->setEnabled(enable);
|
||
|
||
int numberTrajectoriesRef = 0;
|
||
{
|
||
const int simToRestrict = m_aesItem.id();
|
||
// avoid data copying:
|
||
auto cb = [simToRestrict, &numberTrajectoriesRef](const AreaEffectSimulation::TrajectoryTimeAware &traj)
|
||
{
|
||
// iterateAll
|
||
// be aware of: [E01562-259] use originatorId, originatorTypeId (instead of aesItemId, aesTypeId)
|
||
if (traj.originatorTypeId() != ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation)
|
||
return;
|
||
const int aesItemId = traj.originatorId();
|
||
if (simToRestrict == aesItemId)
|
||
++numberTrajectoriesRef;
|
||
};
|
||
m_dtc->trajectoryTimeAwares(cb);
|
||
}
|
||
|
||
DEBUG_LOG(m_logger) << "************* enableUiElements numberTrajectoriesRef=" << numberTrajectoriesRef;
|
||
|
||
if ((numberTrajectoriesRef > 0) && !isPointSimilar(m_explosionClusterData.explosionGeometry()))
|
||
{
|
||
// since first TrajectoryTimeAware:
|
||
enableUiFireButton();
|
||
}
|
||
else
|
||
{
|
||
// until first TrajectoryTimeAware:
|
||
m_ui->pbFire->setEnabled(enable && m_aesItem.id() > 0); // tbc
|
||
}
|
||
|
||
// GUEZE-3520: get rid of cbFlightCurve
|
||
// {
|
||
// // GUEZE-2459
|
||
// // Munitionstyp "Mörser" muss die obere Winkelgruppe ausgewählt && die Eingabemaske "Flugkurve" soll ausgrauen.
|
||
// // Beim Wechsel auf einen anderen Munitionstyp soll wieder als Default die untere Winkelgruppe ausgewählt werden &&
|
||
// // die Eingabemaske muss wieder auswählbar sein.
|
||
// const ProjectDataManagement::GearClass_def::type gearClassType =
|
||
// static_cast<ProjectDataManagement::GearClass_def::type>(m_ui->cbGearClass->currentData().toInt());
|
||
|
||
// // const bool usePeakHeight = m_ui->sBox_peakHeight->styleSheet().isEmpty();
|
||
// const bool useFlightDuration = m_ui->sBox_flightDuration->styleSheet().isEmpty();
|
||
|
||
// m_ui->cbFlightCurve->setEnabled(enable &&
|
||
// (gearClassType != ProjectDataManagement::GearClass_def::Mortar) &&
|
||
// useFlightDuration
|
||
// );
|
||
// }
|
||
|
||
const QList<QAbstractButton *> &geoButtons = m_geometrieButtonGroup->buttons();
|
||
for (QAbstractButton *btn : qAsConst(geoButtons))
|
||
btn->setEnabled(enable);
|
||
|
||
// For PreFat
|
||
m_ui->geometrieAimPointBtn->hide();
|
||
}
|
||
|
||
void ExplosionsEdit::enableUiFireButton()
|
||
{
|
||
INFO_LOG(m_logger) << " START pbFire_immediately isEnabled = " << m_ui->pbFire->isEnabled();
|
||
|
||
// In general available 'States' are: Start | Pause | Resume
|
||
|
||
m_ui->pbFire->setEnabled(false);
|
||
|
||
//
|
||
// Start (immediately) should only be enabled, if object is in modification
|
||
// (not initial creation)
|
||
//
|
||
|
||
bool enableBasics = (m_aesItem.id() > 0) // while creation, not modification
|
||
&& isDataSufficiant();
|
||
|
||
INFO_LOG(m_logger) << " enableBasics = " << enableBasics;
|
||
|
||
if (!enableBasics)
|
||
return; // stay disabled
|
||
|
||
if (m_explosionClusterData.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired)
|
||
return; // stay disabled
|
||
|
||
int numberTrajectoriesRef = 0;
|
||
{
|
||
const int simToRestrict = m_aesItem.id();
|
||
// avoid data copying:
|
||
auto cb = [simToRestrict, &numberTrajectoriesRef](const AreaEffectSimulation::TrajectoryTimeAware &traj)
|
||
{
|
||
// [E01562-259] use originatorId, originatorTypeId (instead of aesItemId, aesTypeId)
|
||
if ((traj.originatorTypeId() == ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation) && (simToRestrict == traj.originatorId()))
|
||
++numberTrajectoriesRef;
|
||
};
|
||
m_dtc->trajectoryTimeAwares(cb);
|
||
}
|
||
|
||
INFO_LOG(m_logger) << " numberTrajectoriesRef = " << numberTrajectoriesRef;
|
||
|
||
// keep in mind:
|
||
// a) during typical usage by operator
|
||
// b) for re-openening dialog / restart svcAes
|
||
if ((numberTrajectoriesRef <= 0) && (m_explosionClusterData.effectDuration() == m_explosionClusterData.originalEffectDuration()))
|
||
{
|
||
INFO_LOG(m_logger) << " Firing has NOT started yet";
|
||
return; // stay disabled
|
||
}
|
||
else if (numberTrajectoriesRef >= m_explosionClusterData.impactCount())
|
||
{
|
||
INFO_LOG(m_logger) << " Firing has finished";
|
||
return; // stay disabled
|
||
}
|
||
|
||
// const bool bIsPointSimilar = isPointSimilar(m_currentGeometry);
|
||
|
||
// if firing started once, -> 'States' are reduced: Pause | Resume
|
||
|
||
// toogle via property 'resumeExecution'
|
||
bool bPause = true;
|
||
{
|
||
const QVariant &resumeExecutionVariant = m_ui->pbFire->property("resumeExecution");
|
||
if (resumeExecutionVariant.isValid())
|
||
{
|
||
INFO_LOG(m_logger) << " resumeExecutionVariant = " << resumeExecutionVariant.toBool();
|
||
bPause = !resumeExecutionVariant.toBool();
|
||
}
|
||
else
|
||
{
|
||
// re-openening dialog:
|
||
if (m_explosionClusterData.effectStateId() == AesEffectState_def::EsPaused)
|
||
bPause = false;
|
||
}
|
||
}
|
||
|
||
if (bPause)
|
||
{
|
||
// Pause
|
||
m_ui->pbFire->setEnabled(true);
|
||
m_ui->pbFire->setText(tr("Pause"));
|
||
m_ui->pbFire->setIcon(QIcon(QStringLiteral("icon:avm_pause.svg")));
|
||
m_ui->pbFire->setProperty("resumeExecution", false);
|
||
}
|
||
else
|
||
{
|
||
// Resume
|
||
// but it does not makes sense, if there is no ammo left:
|
||
if ((m_explosionClusterData.impactCount() > 1) && (m_explosionClusterData.impactCount() >= m_explosionClusterData.remainExplosionNumber()))
|
||
{
|
||
m_ui->pbFire->setEnabled(true);
|
||
m_ui->pbFire->setText(tr("Start"));
|
||
m_ui->pbFire->setIcon(QIcon(QStringLiteral("icon:avm_start.svg")));
|
||
m_ui->pbFire->setProperty("resumeExecution", true);
|
||
}
|
||
}
|
||
|
||
INFO_LOG(m_logger) << " pbFire_immediately text = " << m_ui->pbFire->text();
|
||
INFO_LOG(m_logger) << " END pbFire_immediately isEnabled = " << m_ui->pbFire->isEnabled();
|
||
}
|
||
|
||
/*!
|
||
* \brief ExplosionsEdit::showPreviewObject
|
||
*/
|
||
void ExplosionsEdit::showPreviewObject()
|
||
{
|
||
if (!has2DMap(false)) // showMsgBoxIfNot2D
|
||
return;
|
||
|
||
switch (m_currentGeometry.underlying())
|
||
{
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgArea:
|
||
if (m_previewPolygon.isEmpty())
|
||
return;
|
||
|
||
m_sitMapGuiIfc->displayPreviewObject(this, m_previewPolygon, true);
|
||
return;
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgAimPoint:
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgPoint:
|
||
if (m_previewCircleCenter.isNull())
|
||
return;
|
||
|
||
m_sitMapGuiIfc->displayPreviewObject(this, m_previewCircleCenter, m_previewCircleRadius);
|
||
return;
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgLine:
|
||
if (m_previewPolygon.isEmpty())
|
||
return;
|
||
|
||
m_sitMapGuiIfc->displayPreviewObject(this, m_previewPolygon, false);
|
||
return;
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgRadial:
|
||
if (m_previewCircleCenter.isNull())
|
||
return;
|
||
|
||
m_sitMapGuiIfc->displayPreviewObject(this, m_previewCircleCenter, m_previewCircleRadius);
|
||
return;
|
||
default:
|
||
WARNING_LOG(m_logger) << " Unknown Geometry!";
|
||
return;
|
||
}
|
||
}
|
||
|
||
bool ExplosionsEdit::isDataSufficiant()
|
||
{
|
||
// identifier:
|
||
if (m_ui->leZpNumber->text().isEmpty())
|
||
return false;
|
||
|
||
// originator:
|
||
const int partId = m_ui->cbParticipantUnits->currentData(Qt::UserRole).toInt();
|
||
bool bFiringUnitOk = (partId > 0) && m_ui->cbParticipantUnits->styleSheet().isEmpty();
|
||
if (!bFiringUnitOk)
|
||
return false;
|
||
|
||
// target:
|
||
bool bHasTargetArea = !m_coord.isNull();
|
||
switch (m_currentGeometry.underlying())
|
||
{
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgArea:
|
||
bHasTargetArea &= m_ui->sbDimensionX_BreiteRadius->value() > 0;
|
||
bHasTargetArea &= m_ui->sbDimensionY_Tiefe->value() > 0;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
if (!bHasTargetArea)
|
||
return false;
|
||
|
||
// ammo
|
||
const bool bHasAmmo = m_ui->cbChargeType->currentData().toInt() > 0;
|
||
if (!bHasAmmo)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
/*!
|
||
* \brief ExplosionsEdit::apply
|
||
* \return
|
||
*/
|
||
bool ExplosionsEdit::apply()
|
||
{
|
||
ExplosionClusterModifyRequest request;
|
||
request.id(m_aesItem.id());
|
||
request.element(m_ui->leElement->text().toStdString());
|
||
request.name(m_ui->leZpNumber->text().toStdString());
|
||
|
||
if (m_ui->dteEffectStart->isChecked())
|
||
request.effectStart().set(m_ui->dteEffectStart->msecsSinceEpoch());
|
||
if (m_ui->sbDurationSec->isVisible())
|
||
request.effectDuration(m_ui->sbDurationSec->value() * 1000);
|
||
|
||
const bool bIsPoint = isPointSimilar(m_currentGeometry);
|
||
|
||
// Area: (for area-geometry and point-geometry)
|
||
request.explosionGeometry(m_currentGeometry);
|
||
|
||
Core::GeoPosition2D bottomLeftGeo;
|
||
|
||
switch (m_currentGeometry.underlying())
|
||
{
|
||
case AesExplosionGeometry_def::EgRadial:
|
||
{
|
||
QPointF bottomLeft(Polygons::centerPoint2BottomLeft(m_coord, m_ui->sbDimensionX_BreiteRadius->value() * 2, m_ui->sbDimensionY_Tiefe->value() * 2, 0.0));
|
||
bottomLeftGeo = Core::GeoPosition2D(bottomLeft.x(), bottomLeft.y());
|
||
|
||
request.area().cornerBottomLeft(bottomLeftGeo);
|
||
request.area().height(m_ui->sbDimensionY_Tiefe->value() * 2);
|
||
request.area().width(m_ui->sbDimensionX_BreiteRadius->value() * 2);
|
||
|
||
break;
|
||
}
|
||
case AesExplosionGeometry_def::EgLine:
|
||
{
|
||
const QLineF line(m_ui->positionEdit->coordinate(), m_ui->positionEdit_2->coordinate());
|
||
|
||
QPointF bottomLeft(Polygons::centerPoint2BottomLeft(line.center(),
|
||
m_ui->sbDimensionX_BreiteRadius->value(),
|
||
m_ui->sbDimensionY_Tiefe->value(),
|
||
(m_ui->sbAimingAxis->value() - 1600) * 360.0 / 6400));
|
||
bottomLeftGeo = Core::GeoPosition2D(bottomLeft.x(), bottomLeft.y());
|
||
|
||
request.area().cornerBottomLeft(bottomLeftGeo);
|
||
request.area().height(m_ui->sbDimensionY_Tiefe->value());
|
||
request.area().width(m_ui->sbDimensionX_BreiteRadius->value());
|
||
|
||
break;
|
||
}
|
||
case AesExplosionGeometry_def::EgAimPoint:
|
||
case AesExplosionGeometry_def::EgArea:
|
||
case AesExplosionGeometry_def::EgPoint:
|
||
default:
|
||
{
|
||
QPointF bottomLeft(Polygons::centerPoint2BottomLeft(m_coord,
|
||
m_ui->sbDimensionX_BreiteRadius->value(),
|
||
m_ui->sbDimensionY_Tiefe->value(),
|
||
(m_ui->sbAimingAxis->value() - 1600) * 360.0 / 6400));
|
||
bottomLeftGeo = Core::GeoPosition2D(bottomLeft.x(), bottomLeft.y());
|
||
|
||
request.area().cornerBottomLeft(bottomLeftGeo);
|
||
request.area().height(m_ui->sbDimensionY_Tiefe->value());
|
||
request.area().width(m_ui->sbDimensionX_BreiteRadius->value());
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
QVariant currentData = m_ui->cbActors->currentData();
|
||
|
||
// DMF (Actors)
|
||
int esmParticipantId1 = m_ui->cbActors->currentData().toInt(); // participant ID
|
||
int esmParticipantId2 = m_ui->cbActors_2->currentData().toInt(); // participant ID
|
||
|
||
DEBUG_LOG(m_logger) << "currentIndex: " << m_ui->cbActors->currentIndex() << ", currentText: " << m_ui->cbActors->currentText()
|
||
<< ", participantId1: " << esmParticipantId1 << ", participantId2: " << esmParticipantId2;
|
||
|
||
if (esmParticipantId1 > 0 || esmParticipantId2 > 0)
|
||
{
|
||
request.esmParticipantIds().reserve(2);
|
||
if (esmParticipantId1 > 0)
|
||
request.esmParticipantIds().push_back(esmParticipantId1);
|
||
if (esmParticipantId2 > 0 && esmParticipantId1 != esmParticipantId2 && m_ui->cbActors_2->isEnabled())
|
||
request.esmParticipantIds().push_back(esmParticipantId2);
|
||
}
|
||
|
||
if (bIsPoint || m_currentGeometry.underlying() == AreaEffectSimulation::AesExplosionGeometry_def::EgRadial)
|
||
request.area().rotation(0);
|
||
else
|
||
request.area().rotation((m_ui->sbAimingAxis->value() - 1600) * 360.0 / 6400);
|
||
|
||
request.tla(m_ui->sbZog->value()); // tla: aka zog / ZielOrtungsGenauigkeit
|
||
|
||
// mhpCount:
|
||
if (bIsPoint)
|
||
{
|
||
request.mhpCount(1);
|
||
}
|
||
else
|
||
{
|
||
if (m_ui->cbParticipantUnits->currentData().toString() != QString())
|
||
{
|
||
const qint32 mtpCount = m_ui->cbParticipantUnits->currentData(Qt::UserRole + 1).toInt(); //
|
||
const qint32 sbCannonsCount = m_ui->sbCannons->value();
|
||
if (mtpCount <= 0)
|
||
{
|
||
// maybe aes will crash on invalid mtpCount?
|
||
ERROR_LOG(m_logger) << LOGFUNCTION << " mtpCount invalid, (mtpCount=" << mtpCount << ")";
|
||
return false;
|
||
}
|
||
// Wenn weniger Schüsse als Einheiten dann nimm schüsse
|
||
if (mtpCount > sbCannonsCount)
|
||
request.mhpCount(sbCannonsCount);
|
||
else
|
||
request.mhpCount(mtpCount);
|
||
}
|
||
}
|
||
|
||
// partyId
|
||
request.partyId().set(m_ui->cbParty->currentData().toInt());
|
||
// participant id
|
||
bool bOk = false;
|
||
request.participantId().set(m_ui->cbParticipantUnits->currentData(Qt::UserRole).toInt(&bOk));
|
||
if (!bOk)
|
||
request.participantId(0); // should be interpreted as error since particpantUnitId ir required in this dialog
|
||
|
||
// request.flightCurve(static_cast<AreaEffectSimulation::AesFlightCurve::inner_enum>(m_ui->cbFlightCurve->currentData().toInt()));
|
||
request.impactCount(m_ui->sbCannons->value());
|
||
// add GearClass ???
|
||
request.chargeStruct().chargeTypeId(m_ui->cbChargeType->currentData().toInt());
|
||
request.chargeStruct().igniterTypeId(m_ui->cbIgniterType->currentData().toInt());
|
||
request.firePurposeId(m_ui->cbFirePurpose->currentData().toInt());
|
||
|
||
// peakHeight | flightDuration
|
||
{
|
||
request.peakHeight(-1);
|
||
request.flightDuration(-1);
|
||
|
||
const bool usePeakHeight = m_ui->sBox_peakHeight->styleSheet().isEmpty();
|
||
const bool useFlightDuration = m_ui->sBox_flightDuration->styleSheet().isEmpty();
|
||
if (usePeakHeight == useFlightDuration)
|
||
request.peakHeight(m_ui->sBox_peakHeight->value());
|
||
else if (useFlightDuration)
|
||
request.flightDuration(m_ui->sBox_flightDuration->value() * 1000);
|
||
else
|
||
request.peakHeight(m_ui->sBox_peakHeight->value());
|
||
}
|
||
|
||
if (request.flightDuration() >= 600 * 1000)
|
||
{
|
||
for (int i = 0; i < 10; ++i)
|
||
FATAL_LOG(m_logger) << LOGFUNCTION << " !!!!!!!!!!!!!!"
|
||
<< " flightDuration is exceeding limit!"
|
||
<< " m_ui->sBox_flightDuration->value() " << m_ui->sBox_flightDuration->value();
|
||
}
|
||
|
||
request.partyId().set(m_ui->cbParty->currentData().toInt());
|
||
request.effectMarkingInTa(m_ui->gbEffectMarkingInTa->isChecked());
|
||
request.effectSourceId(ProjectDataManagement::EffectSource::ES_Internal);
|
||
|
||
if (isGuiVulcano())
|
||
{
|
||
request.launchVelocity(TrajectoryGeneratorVulcano::defaultv0()); // Velocity == Speed ; m/s
|
||
request.launchAngleDeg(0); // calculated ; parameter is prepared for ADLER
|
||
request.dropAngle(currentDropAngleDegree());
|
||
|
||
if (request.effectStart().is_set())
|
||
request.tapTime(request.effectStart().get() - (TrajectoryGeneratorVulcano::defaultTapTimeSec() * 1000)); // default: 8 sec
|
||
|
||
request.lcAttack(m_ui->sbLcAttack_Prf->value());
|
||
request.lcTermination(m_ui->sbLcTermination_Prf->value());
|
||
|
||
const int cloudBaseValue = m_ui->spCloudBase->value();
|
||
|
||
// specialValueText
|
||
if (cloudBaseValue == m_ui->spCloudBase->minimum())
|
||
request.cloudBase(std::numeric_limits<int>::min());
|
||
else
|
||
request.cloudBase(cloudBaseValue);
|
||
|
||
} // if (isGuiVulcano())
|
||
|
||
m_requestHelper->addRequest([this, request]() { return m_ftc->explosionClusterModifyRequest(request, this); });
|
||
|
||
return m_requestHelper->startRequests();
|
||
}
|
||
|
||
/*!
|
||
* \brief ExplosionsEdit::onGeometrySelected
|
||
* \param type
|
||
*/
|
||
void ExplosionsEdit::onGeometrySelected(int explosionGeometryInt)
|
||
{
|
||
const AreaEffectSimulation::AesExplosionGeometry::inner_enum newGeometry = static_cast<AreaEffectSimulation::AesExplosionGeometry::inner_enum>(explosionGeometryInt);
|
||
if (m_currentGeometry == newGeometry)
|
||
return;
|
||
|
||
m_currentGeometry = newGeometry;
|
||
|
||
RocketUtility::enaDisaWirkmittel(m_ui->cbGearClass, m_currentGeometry.underlying());
|
||
|
||
// cancel active Rubberbands:
|
||
if (m_sitMapGuiIfc)
|
||
m_sitMapGuiIfc->cancelActiveRubberbands();
|
||
|
||
qint32 mtpCount = m_ui->cbParticipantUnits->currentData(Qt::UserRole + 1).toInt(); //
|
||
if (mtpCount < 1)
|
||
mtpCount = 4;
|
||
|
||
QSignalBlocker blocker(m_geometrieButtonGroup);
|
||
QAbstractButton *myBtn = m_geometrieButtonGroup->button(explosionGeometryInt);
|
||
if (myBtn)
|
||
myBtn->click();
|
||
|
||
if (m_sitMapGuiIfc)
|
||
m_sitMapGuiIfc->removePreviewObject();
|
||
|
||
switch (m_currentGeometry.underlying())
|
||
{
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgAimPoint: // What is AimPoint?
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgPoint:
|
||
{
|
||
m_ui->lblRadiusBreite->setText(tr("Breite [m]"));
|
||
m_ui->sbHeight2SBox->setEnabled(false);
|
||
m_ui->sbDimensionX_BreiteRadius->setEnabled(false);
|
||
m_ui->sbDimensionY_Tiefe->setEnabled(false);
|
||
m_ui->sbZog->setEnabled(true);
|
||
m_ui->sbAimingAxis->setEnabled(false);
|
||
m_ui->sbAimingAxis->setValue(1600);
|
||
m_ui->positionEdit->setEnabled(true);
|
||
m_ui->pbPosition->setEnabled(true);
|
||
m_ui->pbClear->setEnabled(true);
|
||
m_ui->positionEdit_2->setEnabled(false);
|
||
|
||
// m_ui->sbCannons->setEnabled(false);
|
||
m_ui->sbCannons->setMinimum(1);
|
||
m_ui->sbCannons->setValue(1);
|
||
|
||
m_ui->sbDurationSec->setValue(1);
|
||
m_ui->sbDurationSec->setSingleStep(1);
|
||
|
||
// adjust some values according geometryType:
|
||
onChargeTypeChanged();
|
||
}
|
||
break;
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgArea:
|
||
{
|
||
m_ui->lblRadiusBreite->setText(tr("Breite [m]"));
|
||
m_ui->sbHeight2SBox->setEnabled(false);
|
||
m_ui->sbDimensionX_BreiteRadius->setEnabled(true);
|
||
m_ui->sbDimensionY_Tiefe->setEnabled(true);
|
||
m_ui->sbZog->setEnabled(true);
|
||
m_ui->sbAimingAxis->setEnabled(true);
|
||
m_ui->positionEdit->setEnabled(true);
|
||
m_ui->pbPosition->setEnabled(true);
|
||
m_ui->pbClear->setEnabled(true);
|
||
m_ui->positionEdit_2->setEnabled(false);
|
||
|
||
m_ui->sbCannons->setEnabled(true);
|
||
m_ui->sbCannons->setMinimum(2);
|
||
m_ui->sbCannons->setValue(mtpCount);
|
||
|
||
m_ui->sbDurationSec->setValue(60); // 1 min
|
||
m_ui->sbDurationSec->setSingleStep(60);
|
||
}
|
||
break;
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgLine:
|
||
{
|
||
m_ui->lblRadiusBreite->setText(tr("Breite [m]"));
|
||
m_ui->sbHeight2SBox->setEnabled(true);
|
||
m_ui->sbDimensionX_BreiteRadius->setEnabled(true);
|
||
m_ui->sbDimensionY_Tiefe->setEnabled(false);
|
||
m_ui->sbZog->setEnabled(true);
|
||
m_ui->sbAimingAxis->setEnabled(true);
|
||
m_ui->positionEdit->setEnabled(true);
|
||
m_ui->pbPosition->setEnabled(true);
|
||
m_ui->pbClear->setEnabled(true);
|
||
m_ui->positionEdit_2->setEnabled(true);
|
||
|
||
m_ui->sbCannons->setEnabled(true);
|
||
m_ui->sbCannons->setMinimum(2); // ToDo
|
||
|
||
m_ui->sbDurationSec->setValue(60); // 1 min
|
||
}
|
||
break;
|
||
case AreaEffectSimulation::AesExplosionGeometry_def::EgRadial:
|
||
{
|
||
m_ui->lblRadiusBreite->setText(tr("Radius [m]"));
|
||
m_ui->sbHeight2SBox->setEnabled(false);
|
||
m_ui->sbDimensionX_BreiteRadius->setEnabled(true);
|
||
m_ui->sbDimensionY_Tiefe->setEnabled(false);
|
||
m_ui->sbZog->setEnabled(true);
|
||
m_ui->sbAimingAxis->setEnabled(false);
|
||
m_ui->positionEdit->setEnabled(true);
|
||
m_ui->pbPosition->setEnabled(true);
|
||
m_ui->pbClear->setEnabled(true);
|
||
m_ui->positionEdit_2->setEnabled(false);
|
||
|
||
m_ui->sbCannons->setEnabled(true);
|
||
m_ui->sbCannons->setMinimum(2); // ToDo
|
||
|
||
m_ui->sbDurationSec->setValue(60); // 1 min
|
||
}
|
||
break;
|
||
default:
|
||
WARNING_LOG(m_logger) << " Unknown Geometrie!";
|
||
break;
|
||
}
|
||
|
||
m_ui->sbDimensionX_BreiteRadius->clear();
|
||
m_ui->sbDimensionY_Tiefe->clear();
|
||
|
||
m_ui->positionEdit->clear();
|
||
m_ui->positionEdit_2->clear();
|
||
|
||
fillCharges();
|
||
}
|
||
|
||
void ExplosionsEdit::onFireClicked()
|
||
{
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
||
if (((m_aesItem.effectStateId() == AesEffectState_def::EsUnknown) || // initially the effectStateId is Unknown...
|
||
(m_aesItem.effectStateId() == AesEffectState_def::EsError) || (m_aesItem.effectStateId() == AesEffectState_def::EsExpired)
|
||
|| (m_aesItem.effectStateId() == AesEffectState_def::EsRemoved) || (m_aesItem.effectStateId() == AesEffectState_def::EsReceived)
|
||
|| (m_aesItem.effectStateId() == AesEffectState_def::EsFired)
|
||
// || (m_aesItem.effectStateId() == AesEffectState_def::EsPaused) // Maybe EsPaused will be supported in future
|
||
)
|
||
|| m_aesItem.id() <= 0)
|
||
return;
|
||
|
||
if (!isDataSufficiant())
|
||
{
|
||
INFO_LOG(m_logger) << LOGFUNCTION << " onFireClicked "
|
||
<< ", data not sufficiant...";
|
||
return;
|
||
}
|
||
|
||
// skip ui requirement of effectStart by deactivating:
|
||
m_ui->dteEffectStart->setChecked(false);
|
||
onPartitionTimeChanged();
|
||
|
||
m_ui->pbFire->setEnabled(false);
|
||
|
||
const bool bIsObjectModifiable = m_explosionClusterData.isObjectModifiable();
|
||
|
||
if ((m_aesItem.effectStateId() == AesEffectState_def::EsCreated || m_aesItem.effectStateId() == AesEffectState_def::EsPrepared) && bIsObjectModifiable)
|
||
{
|
||
m_ui->dteEffectStart->setMsecsSinceEpoch(m_session->partitionTime() + m_temporaryData.m_estimatedFlightDurationMsec + 5000);
|
||
m_ui->dteEffectStart->setChecked(true);
|
||
|
||
INFO_LOG(m_logger) << LOGFUNCTION << " onFireClicked "
|
||
<< ", m_estimatedFlightDurationMsec = " << m_temporaryData.m_estimatedFlightDurationMsec
|
||
<< ", checkRequirements = " << m_uiRequirements->checkRequirements(this);
|
||
|
||
// Todo: Triggering by simulated button press is a little awkward...
|
||
if (m_uiRequirements->checkRequirements(this))
|
||
this->m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->click();
|
||
|
||
enableUiElements(); // to enable/disable fire-btn
|
||
|
||
return;
|
||
}
|
||
|
||
// dialog has to be re-opened.
|
||
// isObjectModifiable() should be false
|
||
if (!bIsObjectModifiable)
|
||
{
|
||
bool resumeExecution = false;
|
||
const QVariant &resumeExecutionVariant = m_ui->pbFire->property("resumeExecution");
|
||
if (resumeExecutionVariant.isValid())
|
||
resumeExecution = resumeExecutionVariant.toBool();
|
||
|
||
m_ui->pbFire->setProperty("resumeExecution", !resumeExecution);
|
||
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " onFireClicked "
|
||
<< " ExplosionClusterPauseResumeRequest with "
|
||
<< " resumeExecution = " << resumeExecution;
|
||
|
||
// slow down FireBtn being pressed by restarting the timer:
|
||
if (m_enableFireBtnTimer)
|
||
m_enableFireBtnTimer->start(); // restart
|
||
|
||
m_requestHelper->addRequest(
|
||
[=]()
|
||
{
|
||
return m_ftc->explosionClusterPauseResumeRequest(ExplosionClusterPauseResumeRequest(m_aesItem.id(),
|
||
resumeExecution, // resumeExecution
|
||
dds::core::optional<rti::core::int64>(),
|
||
dds::core::optional<int32_t>(),
|
||
dds::core::optional<int32_t>()),
|
||
this);
|
||
});
|
||
m_requestHelper->startRequests();
|
||
|
||
} // if (!bIsObjectModifiable)
|
||
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
||
}
|
||
|
||
/*!
|
||
* \brief ExplosionsEdit::onGearClassChanged
|
||
*/
|
||
void ExplosionsEdit::onGearClassChanged()
|
||
{
|
||
const ProjectDataManagement::GearClass_def::type newGearClassType = static_cast<ProjectDataManagement::GearClass_def::type>(m_ui->cbGearClass->currentData().toInt());
|
||
{
|
||
QSignalBlocker sb(m_ui->cbChargeType);
|
||
fillCharges();
|
||
}
|
||
fillParties();
|
||
onPartyChanged();
|
||
|
||
ProjectDataManagement::GearClass_def::type lastGearClassType = ProjectDataManagement::GearClass_def::Mortar;
|
||
QVariant lastVal = m_ui->cbGearClass->property(LAST_VALUE);
|
||
if (!lastVal.isNull())
|
||
lastGearClassType = static_cast<ProjectDataManagement::GearClass_def::type>(lastVal.toInt());
|
||
|
||
{
|
||
// GUEZE-2459
|
||
// Munitionstyp "Mörser" muss die obere Winkelgruppe ausgewählt && die Eingabemaske "Flugkurve" soll ausgrauen.
|
||
// Beim Wechsel auf einen anderen Munitionstyp soll wieder als Default die untere Winkelgruppe ausgewählt werden &&
|
||
// die Eingabemaske muss wieder auswählbar sein.
|
||
|
||
// GUEZE-3520: Aufraeumen -> getm rid of AesFlightCurve
|
||
// if (newGearClassType == ProjectDataManagement::GearClass_def::Mortar)
|
||
// {
|
||
// DEBUG_LOG(m_logger) << " onGearClassChanged switch to Mortar";
|
||
// m_ui->cbFlightCurve->setCurrentIndex( m_ui->cbFlightCurve->findData(static_cast<int>(AreaEffectSimulation::AesFlightCurve_def::FcHigh)) );
|
||
// }
|
||
// else if (lastGearClassType == ProjectDataManagement::GearClass_def::Mortar)
|
||
// {
|
||
// DEBUG_LOG(m_logger) << " onGearClassChanged switch to Non-Mortar";
|
||
// m_ui->cbFlightCurve->setCurrentIndex( m_ui->cbFlightCurve->findData(static_cast<int>(AreaEffectSimulation::AesFlightCurve_def::FcLow)) );
|
||
// }l
|
||
}
|
||
|
||
// change?
|
||
if (lastGearClassType != newGearClassType)
|
||
{
|
||
DEBUG_LOG(m_logger) << " GearClass changed to " << newGearClassType;
|
||
|
||
// [GUEZE-2460]
|
||
// Die Eingabe von kleineren oder größeren Werten soll verblockt sein.
|
||
// -->>> // Bei Wechsel des Munitionstyps muss die Flugdauer übernommen werden und <<-----------
|
||
// dazu dann die neue Gipfelhöhe berechnet werden.
|
||
// Sofern vor dem Wechsel des Munitionstyp ein Wert kleiner oder größer der
|
||
// Grenzwerte des neuen Munitionstyp eingestellt war, muss dann
|
||
// dementsprechend die minimale oder maximale Gipfelhöhe eingestellt werden.
|
||
|
||
switchTo(FlighDurationOrPeakHeight::FDPH_flightDuration);
|
||
}
|
||
|
||
// update last value:
|
||
m_ui->cbGearClass->setProperty(LAST_VALUE, newGearClassType);
|
||
|
||
doCalculations();
|
||
}
|
||
|
||
void ExplosionsEdit::onPositionChanged(const QPointF &newGeoCoord)
|
||
{
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
||
Q_UNUSED(newGeoCoord);
|
||
doCalculations();
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
||
}
|
||
|
||
void ExplosionsEdit::gatherParticipantUnits(const int &partyId, QHash<int, QSet<int>> &units) const
|
||
{
|
||
const qint32 gearClassId = m_ui->cbGearClass->currentData().toInt();
|
||
|
||
auto cb = [this, &partyId, &units, gearClassId](const ParticipantManagement::Participant &part)
|
||
{
|
||
if (part.isDeleted() || part.partyId() != partyId)
|
||
return;
|
||
|
||
const ParticipantManagement::ParticipantData &ppData = m_ppmDtc->convertParticipantToParticipantData(part);
|
||
const ParticipantManagement::Type &ppType = m_ppmDtc->type(ppData.typeId());
|
||
// do not use ADLER Verbund types
|
||
if (m_ppmDtc->isAdlerVerbund(ppType.id()))
|
||
return;
|
||
const QJsonObject typeProperties(QJsonDocument::fromJson(QByteArray::fromStdString(ppType.properties())).object());
|
||
// superior ids in participant ids will be saved
|
||
// participant ids with type units are ignored
|
||
// participant ids with type GroundVehicule are considered
|
||
// participant ids without properties "isArtillery=true" are ignored
|
||
if (typeProperties.isEmpty() || ppType.typeClass() == ParticipantManagement::TypeClass::Unit || //
|
||
!typeProperties[QStringLiteral("isArtillery")].toBool(false) || typeProperties[QStringLiteral("gearClassId")].toInt(-1) != gearClassId)
|
||
return;
|
||
|
||
units[part.superiorId()] += part.id();
|
||
};
|
||
m_ppmDtc->participants(cb);
|
||
|
||
// 2nd filtering remove superiors unit who does not have the property type "artillery"
|
||
for (auto it = units.begin(); it != units.end();)
|
||
{
|
||
const ParticipantManagement::Participant &pp = m_ppmDtc->participant(it.key());
|
||
const ParticipantManagement::Type &typ = m_ppmDtc->type(pp.typeId());
|
||
const QJsonObject typeProperties(QJsonDocument::fromJson(QByteArray::fromStdString(typ.properties())).object());
|
||
if ((typ.typeClass() != ParticipantManagement::TypeClass::Unit && typ.typeClass() != ParticipantManagement::TypeClass::GroundVehicle)
|
||
|| !typeProperties[QStringLiteral("isArtillery")].toBool(false))
|
||
it = units.erase(it++);
|
||
else
|
||
++it;
|
||
}
|
||
}
|
||
|
||
void ExplosionsEdit::fillParties()
|
||
{
|
||
// first fill all parties in
|
||
m_ppmGuiCbxIfc->fillPartyComboBox(m_ui->cbParty);
|
||
|
||
// there is a signal blocker in m_ppmGuiCbxIfc->fillPartyComboBox(), so it is ok to start here
|
||
const QSignalBlocker cbPartyBlocker(m_ui->cbParty);
|
||
|
||
// sort out parties without participants without the chosen gearType
|
||
for (qint32 partyIdx = m_ui->cbParty->count() - 1; partyIdx >= 0; --partyIdx)
|
||
{
|
||
const qint32 partyId = m_ui->cbParty->itemData(partyIdx, Qt::UserRole).toInt();
|
||
|
||
if (isValidParty(partyId))
|
||
continue;
|
||
|
||
m_ui->cbParty->removeItem(partyIdx);
|
||
}
|
||
}
|
||
|
||
bool ExplosionsEdit::isValidParty(qint32 partyId)
|
||
{
|
||
const qint32 gearClassId = m_ui->cbGearClass->currentData().toInt();
|
||
|
||
return m_ppmDtc->participants(static_cast<std::function<bool(const ParticipantManagement::Participant &)>>(
|
||
[this, partyId, gearClassId](const ParticipantManagement::Participant &participant)
|
||
{
|
||
if (participant.partyId() != partyId || participant.isDeleted())
|
||
return false;
|
||
|
||
const ParticipantManagement::Type type = m_ppmDtc->type(participant.typeId());
|
||
const QJsonObject typeProperties = QJsonDocument::fromJson(QByteArray::fromStdString(type.properties())).object();
|
||
const qint32 typeGearClass = typeProperties.value(QStringLiteral("gearClassId")).toInt(-1);
|
||
|
||
return typeGearClass == gearClassId;
|
||
}));
|
||
}
|
||
|
||
void ExplosionsEdit::fillCharges()
|
||
{
|
||
DEBUG_LOG(m_logger) << " fillCharges";
|
||
|
||
if (!m_pdmDtc)
|
||
{
|
||
ERROR_LOG(m_logger) << " no m_pdmDtc";
|
||
return;
|
||
}
|
||
|
||
const ProjectDataManagement::GearClass_def::type curGearClassType = static_cast<ProjectDataManagement::GearClass_def::type>(m_ui->cbGearClass->currentData().toInt());
|
||
const auto chargesForGearClass = m_pdmDtc->chargesForGearClass(curGearClassType);
|
||
|
||
const int curCtDataToRestore = m_ui->cbChargeType->currentData().toInt();
|
||
|
||
// current target geometry:
|
||
const bool bIsPoint = isPointSimilar(m_currentGeometry);
|
||
|
||
DEBUG_LOG(m_logger) << " fillCharges"
|
||
<< ", bIsPoint " << bIsPoint;
|
||
|
||
QSignalBlocker locked(m_ui->cbChargeType);
|
||
|
||
m_ui->cbChargeType->clear();
|
||
for (const ProjectDataManagement::ChargeType &chargeType : qAsConst(chargesForGearClass))
|
||
{
|
||
// Vulcano ist available for point geometry only:
|
||
const bool bIsVulcano = chargeTypeIdIsVulcano(chargeType.id());
|
||
// NOTE: igniterType/IgniterFunciton = 0 as long as ExplosionsEdit does not contain values
|
||
const double impactRadius4Debug = m_pdmDtc->impactRadius(chargeType.id(), 0, 0);
|
||
bool isAdlerChargeType = m_pdmDtc->isAdlerChargeType(chargeType.id());
|
||
|
||
DEBUG_LOG(m_logger) << " fillCharges"
|
||
<< ", id=" << chargeType.id() << ", name= " << chargeType.name() << ", bIsPoint " << bIsPoint << ", bIsVulcano " << bIsVulcano
|
||
<< ", impactRadius4Debug = " << impactRadius4Debug << ", isAdlerChargeType = " << isAdlerChargeType;
|
||
if (isAdlerChargeType || (bIsVulcano && !bIsPoint && curGearClassType != ProjectDataManagement::GearClass_def::Artillery)) // [Jira E01562-21]
|
||
continue;
|
||
m_ui->cbChargeType->addItem(QString::fromStdString(chargeType.name()), chargeType.id());
|
||
}
|
||
|
||
// restore last:
|
||
if (curCtDataToRestore > 0)
|
||
{
|
||
int curIdxFound = m_ui->cbChargeType->findData(curCtDataToRestore);
|
||
if (curIdxFound >= 0)
|
||
m_ui->cbChargeType->setCurrentIndex(curIdxFound);
|
||
}
|
||
|
||
onChargeTypeChanged();
|
||
}
|
||
|
||
void ExplosionsEdit::fillGearClasses()
|
||
{
|
||
const auto &gearClasses = m_pdmDtc->gearTypes();
|
||
for (auto type : qAsConst(gearClasses))
|
||
{
|
||
if (m_gearClassFilter.contains(type.gearClassId()) && m_pdmDtc->getAdlerWeaponSystemId(type.id()) <= 3)
|
||
m_ui->cbGearClass->addItem(m_pdmDtc->trGearTypeName(type.id()), type.gearClassId().underlying());
|
||
}
|
||
}
|
||
|
||
int ExplosionsEdit::currentDropAngleDegree()
|
||
{
|
||
if (m_ui->rbDropAngleDown->isChecked())
|
||
return s_dropAngleDown;
|
||
else
|
||
return s_dropAngleUp;
|
||
}
|
||
|
||
double ExplosionsEdit::currentDropAngleRadiant() { return currentDropAngleDegree() * LtsCoordTrans::deg2rad(); }
|
||
|
||
bool ExplosionsEdit::isOnlyNameModifiable() const
|
||
{
|
||
// BaseClass::isOnlyNameModifiable() -> EsPaused || EsActivated
|
||
// m_explosionClusterData.isObjectModifiable -> since PreStart and s_calculationOffsetMSec
|
||
const bool ret = BaseClass::isOnlyNameModifiable() || !m_explosionClusterData.isObjectModifiable();
|
||
return ret;
|
||
}
|
||
|
||
void ExplosionsEdit::resetTempAndAnnotationStr()
|
||
{
|
||
INFO_LOG(m_logger) << " RESET resetTempAndAnnotationStr";
|
||
m_temporaryData.reset();
|
||
// updateAnnotationStr();
|
||
}
|
||
|
||
void ExplosionsEdit::addAnnotationStr(const QString &newAnno)
|
||
{
|
||
m_temporaryData.m_annotationString4Dlg += newAnno;
|
||
updateAnnotationStr();
|
||
}
|
||
|
||
void ExplosionsEdit::updateAnnotationStr()
|
||
{
|
||
QStringList sl;
|
||
if (!m_temporaryData.m_annotationStringFiring4Dlg.isEmpty())
|
||
sl.append(m_temporaryData.m_annotationStringFiring4Dlg);
|
||
if (!m_temporaryData.m_annotationString4Dlg.isEmpty())
|
||
sl.append(m_temporaryData.m_annotationString4Dlg);
|
||
if (!m_temporaryData.m_remainingTimeStr.isEmpty())
|
||
sl.append(m_temporaryData.m_remainingTimeStr);
|
||
|
||
if (sl.isEmpty())
|
||
m_ui->lblAnnotationStringContent->setText(QStringLiteral("-"));
|
||
else if (sl.size() == 1)
|
||
m_ui->lblAnnotationStringContent->setText(sl.first());
|
||
else
|
||
m_ui->lblAnnotationStringContent->setText(sl.join(QStringLiteral(" ; ")));
|
||
}
|
||
|
||
double ExplosionsEdit::getElevation(const double lon, const double lat) const
|
||
{
|
||
if (m_elevationManager == nullptr)
|
||
return 0.0;
|
||
const double height = m_elevationManager->elevation(lon, lat);
|
||
DEBUG_LOG(m_logger) << "getElevation lon=" << lon << ", lat=" << lat << ", height=" << height;
|
||
return height;
|
||
}
|
||
|
||
} // namespace AreaEffectSimulation
|