Files
qt5-legatus-legacy/Lts/Plugins/AreaEffectSimulation/Gui/MineSingleVirtual/aes_booby_trap_edit.cpp
T
2026-08-13 18:33:10 +02:00

483 lines
18 KiB
C++
Executable File

/**
* @RDE_HEADER@
* AreaEffectSimulation - Mine Single Virtual Edit Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2023-07-14
*/
#include "aes_booby_trap_edit.h"
#include "ui_aes_booby_trap_edit.h"
#include <LtsWidgets/lts_message_dialog.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 <Plugins/AreaEffectSimulation/aes_info_gui_plugin_interface.h>
#include <AreaEffectSimulation/aes_translation.h>
#include <LtsUtilsSettings/settings.h>
namespace AreaEffectSimulation
{
double BoobyTrapEdit::s_minTriggerRadius;
double BoobyTrapEdit::s_maxTriggerRadius;
double BoobyTrapEdit::s_defaultTriggerRadius;
double BoobyTrapEdit::s_minImpactRadius;
double BoobyTrapEdit::s_maxImpactRadius;
double BoobyTrapEdit::s_defaultImpactRadius;
double BoobyTrapEdit::s_stepRadius;
int BoobyTrapEdit::s_minDurationHours;
int BoobyTrapEdit::s_maxDurationHours;
int BoobyTrapEdit::s_defaultCentralAngle;
static int s_lastChargeTypeBoobyTrap = 251;
static int s_boobyTrap2 = 252;
// Todo: Höhe und Radius in Abhängigkeit der Munitionsart berechnen
BoobyTrapEdit::BoobyTrapEdit(const QString &menuGroup, QWidget *parent)
: BaseEdit(menuGroup, QLatin1String("BoobyTrapEdit"), parent)
, m_ui(new Ui::BoobyTrapEdit)
{
m_ui->setupUi(this);
s_minTriggerRadius = 5;
s_maxTriggerRadius = 5;
s_defaultTriggerRadius = 5;
s_minImpactRadius = 10;
s_maxImpactRadius = 10;
s_defaultImpactRadius = 10;
s_stepRadius = 1;
s_minDurationHours = 1;
s_maxDurationHours = 4;
s_defaultCentralAngle = 90;
m_ui->sbTriggerRadius->setMinimum(s_minTriggerRadius);
m_ui->sbTriggerRadius->setMaximum(s_maxTriggerRadius);
m_ui->sbTriggerRadius->setSingleStep(s_stepRadius);
m_ui->sbTriggerRadius->setValue(s_defaultTriggerRadius);
m_ui->sbImpactRadius->setMinimum(s_minImpactRadius);
m_ui->sbImpactRadius->setMaximum(s_maxImpactRadius);
m_ui->sbImpactRadius->setSingleStep(s_stepRadius);
m_ui->sbImpactRadius->setValue(s_defaultImpactRadius);
#if GUEZ_MODE
m_ui->sbCentralAngle->setMaximum(90.0);
#endif
m_ui->sbCentralAngle->setValue(s_defaultCentralAngle);
m_ui->sbDurationHours->setMinimum(s_minDurationHours);
m_ui->sbDurationHours->setMaximum(s_maxDurationHours);
m_ui->rotationAngleEdit->initAngleEditWidget(1); // need to be done to create spinboxes.
#ifndef LTS_DEBUG
m_ui->lblTriggerRadius->setHidden(true);
m_ui->sbTriggerRadius->setHidden(true);
m_ui->lblImpactRadius->setHidden(true);
m_ui->sbImpactRadius->setHidden(true);
m_ui->sbCentralAngle->setHidden(true);
#endif
m_uiRequirements->addRequirement({m_ui->lblPosition, [this]() { return (!m_ui->positionEdit->coordinate().isNull()); }});
m_uiRequirements->addRequirement({m_ui->lblIdentifier, [this]() { return (!m_ui->leIdentifier->text().isEmpty()); }});
m_uiRequirements->addRequirement({m_ui->lblEffectStart, [this]() { return hasValidEffectStart(m_ui->dteEffectStart); }});
m_uiRequirements->addRequirement({m_ui->lblChargeType, [this]() { return (m_ui->cbChargeType->currentData().toInt() > 0); }});
connect(m_ui->pbClear, &QPushButton::clicked, m_ui->positionEdit, &LtsCoordWidget::LtsCoordWidget::clear);
//connect(m_ui->sbTriggerRadius, &QSpinBox::editingFinished, this, &BoobyTrapEdit::correctSpinBoxValue);
connect(m_ui->sbImpactRadius, &QSpinBox::editingFinished, this, &BoobyTrapEdit::correctSpinBoxValue);
// Not yet needed, as no step is defined:
// connect(m_ui->sbAltitude, &QSpinBox::editingFinished, this, &BoobyTrapEdit::correctSpinBoxValue);
// connect(m_ui->sbDurationMinutes, &QSpinBox::editingFinished, this, &BoobyTrapEdit::correctSpinBoxValue);
enaDisa();
setOnlineHelpFilename(QStringLiteral("Situation-Add_Mine_VLad"));
}
BoobyTrapEdit::~BoobyTrapEdit()
{
delete m_ui;
}
void BoobyTrapEdit::retranslate()
{
translate(m_ui, m_aesItem, tr("BoobyTrap"));
m_pdmGuiIfc->fillBoobyTrapComboBox(m_ui->cbChargeType);
setCombobxToValueIfPossible(m_ui->cbChargeType, s_lastChargeTypeBoobyTrap );
}
void BoobyTrapEdit::enableUiElements(bool enable)
{
m_ui->leIdentifier->setMaxLength(30);
m_ui->cbChargeType->setEnabled(enable);
m_ui->sbDurationHours->setEnabled(enable);
m_ui->cbParty->setEnabled(enable);
setEffectStartEnabled(m_ui->dteEffectStart, enable);
m_ui->pbPosition->setEnabled(m_sitMapGuiIfc && enable);
m_ui->positionEdit->setEnabled(enable);
m_ui->rotationAngleEdit->setEnabled(enable);
m_ui->sbTriggerRadius->setEnabled(enable);
m_ui->sbImpactRadius->setEnabled(enable);
m_ui->leIdentifier->setEnabled(enable || isOnlyNameModifiable());
m_ui->pbClear->setEnabled(enable);
if (m_aesItem.effectStateId() == AesEffectState_def::EsExpired)
{
m_ui->leIdentifier->setText(QString::fromStdString(m_aesItem.name()));
__unchange__();
}
if (m_aesItem.effectStateId() == AesEffectState_def::EsActivated)
{
QString name = m_ui->leIdentifier->text();
auto baseData = m_dtc->boobyTrap(m_aesItem.id());
setData(baseData, false);
__unchange__();
if (name != m_ui->leIdentifier->text())
m_ui->leIdentifier->setText(name);
}
}
bool BoobyTrapEdit::init(int aesItemId, bool copy,
LtsSession::LtsSession *session)
{
if (!BaseEdit::init(aesItemId, session))
return false;
bool editable = (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::MineSingleVirtualEdit)) ||
m_session->app() == LtsAppDescriptor::App::OC;
bool readable = (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::MineSingleVirtualShow));
;
if (!readable && !editable)
return false;
// FIXME: in GUI plugin, nicht ausserhalb!
if (!m_session->getForeignGuiInterface<ProjectDataManagement::ForeignGuiInterface>(m_pdmGuiIfc))
return false;
m_ui->dteEffectStart->setMinimumTimeMSec(m_session->partitionTime());
m_ui->dteEffectStart->setRetrieveCurrentTimeCallback([this]() { return m_session->partitionTime(); });
if (m_sitMapGuiIfc)
connect(m_ui->pbPosition, &QPushButton::clicked, this, &BoobyTrapEdit::getPosition);
else
m_ui->pbPosition->setEnabled(false);
m_pdmGuiIfc->fillBoobyTrapComboBox(m_ui->cbChargeType);
m_ppmGuiCbxIfc->fillPartyComboBox(m_ui->cbParty);
connect(m_ui->cbChargeType,static_cast<void (LtsWidgets::LtsComboBox::*)(int)>(&LtsWidgets::LtsComboBox::currentIndexChanged),
this, &BoobyTrapEdit::onChargeTypeChanged);
if (aesItemId > 0)
{
auto baseData = m_dtc->boobyTrap(aesItemId);
setData(baseData, copy);
}
onChargeTypeChanged(0);
enableUiElements(editable && isModifiable() && !isOnlyNameModifiable());
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::boobyTrapChanged,
this, &BaseEdit::onAreaChanged);
return true;
}
void BoobyTrapEdit::onChargeTypeChanged(int index)
{
Q_UNUSED(index);
int chargeType = m_ui->cbChargeType->currentData().toInt();
if ( chargeType != s_boobyTrap2)
{
m_ui->lblRotation->hide();
m_ui->rotationAngleEdit->hide();
m_ui->lblCentralAngle->hide();
m_ui->sbCentralAngle->hide();
}
else
{
m_ui->lblRotation->show();
m_ui->rotationAngleEdit->show();
#ifdef LTS_DEBUG
m_ui->lblCentralAngle->show();
m_ui->sbCentralAngle->show();
#else
m_ui->lblCentralAngle->hide();
m_ui->sbCentralAngle->hide();
#endif
}
}
void BoobyTrapEdit::setData(const BoobyTrap &bt, bool copy)
{
m_aesItem = bt;
if (copy)
{
m_aesItem.id(0);
m_aesItem.effectStateId(AreaEffectSimulation::AesEffectState_def::get_default());
}
m_ui->leIdentifier->setText(QString::fromStdString(bt.name()));
m_ui->dteEffectStart->setMsecsSinceEpoch(bt.effectStart().is_set() ? bt.effectStart().get() : 0);
m_ui->cbChargeType->setCurrentIndex(m_ui->cbChargeType->findData(bt.chargeStruct().chargeTypeId()));
s_lastChargeTypeBoobyTrap = bt.chargeStruct().chargeTypeId();
m_ui->sbDurationHours->setValue(bt.effectDuration() / 1000 / 60/ 60); // msecs -> mins -> hours
m_ui->sbTriggerRadius->setValue(bt.triggerRadius());
m_ui->sbImpactRadius->setValue(bt.impactRadius());
if (bt.rotationAngle().is_set())
{
// assumed that angle is in degree else need to be converted first
m_ui->rotationAngleEdit->setValue(bt.rotationAngle().get(), LtsAngleEditBase::AngleUnitType::Deg);
}
if (bt.centralAngle().is_set())
m_ui->sbCentralAngle->setValue(bt.centralAngle().get());
m_ui->cbParty->setCurrentIndex(m_ui->cbParty->findData(bt.partyId().is_set() ? bt.partyId().get() : 0));
QPointF impactPosition(bt.impactPosition().lon(), bt.impactPosition().lat());
if (!impactPosition.isNull())
m_ui->positionEdit->setCoordinate(impactPosition);
}
void BoobyTrapEdit::getPosition()
{
if (!has2DMap())
return;
// convert angle in degree before displaying
const double &rotation = m_ui->rotationAngleEdit->getAngleConverted(m_ui->rotationAngleEdit->getAngle(),
LtsAngleEditBase::AngleUnitType::Deg,
m_ui->rotationAngleEdit->getAngleCurrentUnit());
const double &angle = m_ui->sbCentralAngle->value();
const int chargeType = m_ui->cbChargeType->currentData().toInt();
if (chargeType == s_boobyTrap2)
{
// sector:
double trigger = m_ui->sbTriggerRadius->value();
double triggerMin = s_minTriggerRadius;
double triggerMax = s_maxTriggerRadius;
// if all 3 are more or less equal, the screenresolution leads to problems at checkMinMaxRadiusConstraints
// so in that case skip tranfering to rubberband and let the user enter just one CircleSector:
const bool bTriggerRelevant = (!qFuzzyCompare(trigger, triggerMin) && !qFuzzyCompare(trigger, triggerMax)); // Maybe this has to be improved
double impact = m_ui->sbImpactRadius->value();
double impactMin = s_minImpactRadius;
double impactMax = s_maxImpactRadius;
const bool bImpactRelevant = (!qFuzzyCompare(impact, impactMin) && !qFuzzyCompare(impact, impactMax)); // Maybe this has to be improved
if (bTriggerRelevant && bImpactRelevant)
{
if (!m_sitMapGuiIfc->getTwoCircleSector(this,
SLOT(onTwoCircleSectorData(QPointF, double, double, double, double)),
nullptr,
m_ui->sbTriggerRadius->value(), s_minTriggerRadius, s_maxTriggerRadius,
m_ui->sbImpactRadius->value(), s_minImpactRadius, s_maxImpactRadius,
m_ui->positionEdit->coordinate(),
angle,
rotation)) //max
{
ERROR_LOG(m_logger) << LOGFUNCTION << " getTwoCircleSector failed, checkSlots??";
}
} else if (!bTriggerRelevant && bImpactRelevant)
{
// enter impactRadius
if (!m_sitMapGuiIfc->getCircleSector(this,
SLOT(onCircleSectorData(QPointF, double, double, double)),
nullptr,
m_ui->sbImpactRadius->value(), s_minImpactRadius, s_maxImpactRadius,
m_ui->positionEdit->coordinate(),
angle,
rotation)) //max
{
ERROR_LOG(m_logger) << LOGFUNCTION << " getCircleSector failed, checkSlots??";
}
} else if (!bTriggerRelevant && !bImpactRelevant)
{
if (!m_sitMapGuiIfc->getCircleSectorFixRadius(this,
SLOT(onCircleSectorFixRadiusData(QPointF, double, double, double)),
nullptr,
m_ui->sbImpactRadius->value(),
m_ui->positionEdit->coordinate(),
angle,
rotation))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " getCircleSectorFixRadius failed, checkSlots??";
}
} else if (bTriggerRelevant && !bImpactRelevant)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " not supported yet";
}
}
else
{
// circle
if (!m_sitMapGuiIfc->getCircle(this,
SLOT(onCircleData(QPointF, double)),
nullptr,
s_minTriggerRadius,
s_maxTriggerRadius,
m_ui->positionEdit->coordinate(),
m_ui->sbTriggerRadius->value())) //max
{
ERROR_LOG(m_logger) << LOGFUNCTION << " getCircle failed, checkSlots??";
}
}
}
void BoobyTrapEdit::onCircleData(const QPointF &position, double radius)
{
m_previewCircleCenter = position;
// SB corrects value acc. singleStep
m_ui->sbTriggerRadius->setValue(radius);
m_previewCircleRadius = m_ui->sbTriggerRadius->value();
m_ui->positionEdit->setCoordinate(position);
DEBUG_LOG(m_logger) << "Pos X: " << position.x() << " Y: " << position.y() << " R: " << radius;
showPreviewObject();
}
void BoobyTrapEdit::onCircleSectorFixRadiusData(const QPointF &position, double radius, double rotationDeg, double centralAngleDeg)
{
// radius may modified, use original:
Q_UNUSED(radius);
onCircleSectorData(position, m_ui->sbImpactRadius->value(), rotationDeg, centralAngleDeg);
}
void BoobyTrapEdit::onCircleSectorData(const QPointF &position, double radius, double rotationDeg, double centralAngleDeg)
{
m_previewCircleCenter = position;
// SB corrects value acc. singleStep
if (radius > 0)
m_ui->sbImpactRadius->setValue(radius);
m_previewCircleRadius = m_ui->sbTriggerRadius->value();
m_previewRotationDeg = rotationDeg;
m_previewCentralAngleDeg = centralAngleDeg;
m_ui->positionEdit->setCoordinate(position);
m_ui->rotationAngleEdit->setValue(rotationDeg, LtsAngleEditBase::AngleUnitType::Deg);
m_ui->sbCentralAngle->setValue(std::ceil(centralAngleDeg));
DEBUG_LOG(m_logger) << "Pos X: " << position.x() << " Y: " << position.y()
<< ", R1: " << radius
<<", rotation[deg]:" << rotationDeg << ", angle[deg]:" << centralAngleDeg;
showPreviewObject();
}
void BoobyTrapEdit::onTwoCircleSectorData(const QPointF &position, double radius1, double radius2, double rotationDeg, double centralAngleDeg)
{
m_previewCircleCenter = position;
// SB corrects value acc. singleStep
if (radius1 > 0)
m_ui->sbTriggerRadius->setValue(radius1);
if (radius2 > 0)
m_ui->sbImpactRadius->setValue(radius2);
m_previewCircleRadius = m_ui->sbTriggerRadius->value();
m_previewRotationDeg = rotationDeg;
m_previewCentralAngleDeg = centralAngleDeg;
m_ui->positionEdit->setCoordinate(position);
m_ui->rotationAngleEdit->setValue(rotationDeg, LtsAngleEditBase::AngleUnitType::Deg);
m_ui->sbCentralAngle->setValue(std::ceil(centralAngleDeg));
DEBUG_LOG(m_logger) << "Pos X: " << position.x() << " Y: " << position.y()
<< ", R1: " << radius1 << " R2: " << radius2
<<", rotation[deg]:" << rotationDeg << ", angle[deg]:" << centralAngleDeg;
showPreviewObject();
}
bool BoobyTrapEdit::apply()
{
std::string name = m_ui->leIdentifier->text().toStdString();
qint64 effectStart = m_ui->dteEffectStart->msecsSinceEpoch();
int duration = m_ui->sbDurationHours->value() * 60 * 1000 * 60; //hours -> mins -> msecs
int chargeType = m_ui->cbChargeType->currentData().toInt();
s_lastChargeTypeBoobyTrap = chargeType;
int partyId = m_ui->cbParty->currentData().toInt();
int triggerRadius = m_ui->sbTriggerRadius->value();
int impactRadius = m_ui->sbImpactRadius->value();
QPointF p(m_ui->positionEdit->coordinate());
const Core::GeoPosition2D pos(p.x(), p.y());
BoobyTrapModifyRequest req(m_aesItem.id(),
name,
duration,
chargeType,
pos,
impactRadius,
triggerRadius,
dds::core::optional<double>(),
dds::core::optional<double>(),
LtsUtils::toOptional(effectStart),
LtsUtils::toOptional(partyId)
);
if (chargeType == s_boobyTrap2)
{
// the angle is sent in deg. Check if value is in degree
double rotValue = m_ui->rotationAngleEdit->getAngleConverted(m_ui->rotationAngleEdit->getAngle(),
LtsAngleEditBase::AngleUnitType::Deg,
m_ui->rotationAngleEdit->getAngleCurrentUnit());
req.rotationAngle().set(rotValue);
req.centralAngle().set(m_ui->sbCentralAngle->value());
}
m_requestHelper->addRequest([this,req]()
{
return m_ftc->boobyTrapModifyRequest(req, this);
});
return m_requestHelper->startRequests();
}
void BoobyTrapEdit::showPreviewObject()
{
if (!m_sitMapGuiIfc)
return;
if (m_previewCircleCenter.isNull())
return;
if (m_ui->cbChargeType->currentData().toInt() == s_boobyTrap2)
{
m_sitMapGuiIfc->displayPreviewObject(this, m_previewCircleCenter, m_previewCircleRadius, m_previewRotationDeg, m_previewCentralAngleDeg);
}
else
{
m_sitMapGuiIfc->displayPreviewObject(this, m_previewCircleCenter, m_previewCircleRadius);
}
}
} // namespace AreaEffectSimulation