/** * @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 #include #include #include #include #include #include #include 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(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(&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(), dds::core::optional(), 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