/** * @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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 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> contraintsDistance; if (contraintsDistance.isEmpty()) { contraintsDistance.insert(static_cast(ProjectDataManagement::GearClass_def::Artillery), {3500, 40000}); // Haubitze contraintsDistance.insert(static_cast(ProjectDataManagement::GearClass_def::Mortar), {800, 7800}); // Mörser // contraints.insert(static_cast(ProjectDataManagement::GearClass_def::Mortar), {15000, 90000}); // Rakete // contraints.insert(static_cast(ProjectDataManagement::GearClass_def::Mortar), {10000, 38400}); // Minenrakete } static QHash> contraintsDistanceVulcano; if (contraintsDistanceVulcano.isEmpty()) { contraintsDistanceVulcano.insert(static_cast(ProjectDataManagement::GearClass_def::Artillery), {10000, 70000}); // Haubitze contraintsDistanceVulcano.insert(static_cast(ProjectDataManagement::GearClass_def::Rocket), {10000, 70000}); // Rakete } if (chargeTypeIdIsVulcano(chargeTypeId)) { auto it = contraintsDistanceVulcano.find(static_cast(gearClass)); if (it == contraintsDistanceVulcano.end()) return {0, 100000}; return *it; } // chargeTypeId Is _NOT_ Vulcano auto it = contraintsDistance.find(static_cast(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 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> contraintsFlightDuration; if (contraintsFlightDuration.isEmpty()) { contraintsFlightDuration.insert(static_cast(ProjectDataManagement::GearClass_def::Artillery), {6, 150}); // Haubitze contraintsFlightDuration.insert(static_cast(ProjectDataManagement::GearClass_def::Mortar), {20, 60}); // Mörser } static QHash> contraintsFlightDurationVulcano; if (contraintsFlightDurationVulcano.isEmpty()) { contraintsFlightDurationVulcano.insert(static_cast(ProjectDataManagement::GearClass_def::Artillery), {0, 999}); // Haubitze contraintsFlightDurationVulcano.insert(static_cast(ProjectDataManagement::GearClass_def::Rocket), {0, 999}); // Rakete } if (chargeTypeIdIsVulcano(chargeTypeId)) { auto it = contraintsFlightDurationVulcano.find(static_cast(gearClass)); if (it == contraintsFlightDurationVulcano.end()) return {0, 100000}; return *it; } // chargeTypeId Is _NOT_ Vulcano auto it = contraintsFlightDuration.find(static_cast(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 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> contraintsPeak; if (contraintsPeak.isEmpty()) { contraintsPeak.insert(static_cast(ProjectDataManagement::GearClass_def::Artillery), {250, 15000}); // Haubitze contraintsPeak.insert(static_cast(ProjectDataManagement::GearClass_def::Mortar), {500, 3000}); // Mörser } static QHash> contraintsPeakVulcano; if (contraintsPeakVulcano.isEmpty()) { contraintsPeakVulcano.insert(static_cast(ProjectDataManagement::GearClass_def::Artillery), {1000, 37500}); // Haubitze contraintsPeakVulcano.insert(static_cast(ProjectDataManagement::GearClass_def::Rocket), {1000, 37500}); // Rakete } if (chargeTypeIdIsVulcano(chargeTypeId)) { auto it = contraintsPeakVulcano.find(static_cast(gearClass)); if (it == contraintsPeakVulcano.end()) return {0, 100000}; return *it; } // chargeTypeId Is _NOT_ Vulcano auto it = contraintsPeak.find(static_cast(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(AreaEffectSimulation::AesExplosionGeometry_def::EgAimPoint)); m_geometrieButtonGroup->addButton(m_ui->geometrieAreaBtn, static_cast(AreaEffectSimulation::AesExplosionGeometry_def::EgArea)); m_geometrieButtonGroup->addButton(m_ui->geometrieLineBtn, static_cast(AreaEffectSimulation::AesExplosionGeometry_def::EgLine)); m_geometrieButtonGroup->addButton(m_ui->geometriePointBtn, static_cast(AreaEffectSimulation::AesExplosionGeometry_def::EgPoint)); m_geometrieButtonGroup->addButton(m_ui->geometrieRadialBtn, static_cast(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::of(&QButtonGroup::buttonClicked), this, &ExplosionsEdit::onGeometrySelected); #endif // QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) connect(m_ui->cbGearClass, QOverload::of(&QComboBox::currentIndexChanged), this, &ExplosionsEdit::onGearClassChanged); connect(m_ui->cbParty, QOverload::of(&QComboBox::currentIndexChanged), this, &ExplosionsEdit::onPartyChanged); connect(m_ui->cbChargeType, QOverload::of(&QComboBox::currentIndexChanged), this, &ExplosionsEdit::onChargeTypeChanged); connect(m_ui->cbIgniterType, QOverload::of(&QComboBox::currentIndexChanged), this, &ExplosionsEdit::onIgniterTypeChanged); connect(m_ui->cbFirePurpose, QOverload::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::of(&QSpinBox::valueChanged), this, &ExplosionsEdit::onWidthChanged); // "Flugzeit" and "Gipfelhöhe" can not be set at the same time connect(m_ui->sBox_flightDuration, QOverload::of(&QSpinBox::valueChanged), this, &ExplosionsEdit::onFlightDurationChanged); connect(m_ui->sBox_peakHeight, QOverload::of(&QSpinBox::valueChanged), this, &ExplosionsEdit::onPeakHeigthChanged); // Duration (Effect) connect(m_ui->sbDurationSec, QOverload::of(&QSpinBox::valueChanged), this, &ExplosionsEdit::onEffectDurationChanged); // Aiming Axis (for line geometry) connect(m_ui->sbAimingAxis, QOverload::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 esmActorCbx; if (!m_session->getForeignGuiInterface(esmActorCbx)) return false; if (!m_session->getForeignDataInterface(m_asbIfc)) return false; if (!session->getForeignGuiInterface(m_esmActorCbx)) return false; // P _D_ M if (!m_session->getDtc(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(m_ppmDtc)) { ERROR_LOG(m_logger) << " failed to get ppmDtc"; return false; } if (!m_session->getForeignGuiInterface(m_pdmCbx)) return false; // A _E_ S if (!m_session->getDtc(m_aesDtc)) { ERROR_LOG(m_logger) << " failed to get aesDtc"; return false; } // initial adjustment: m_explosionClusterData.explosionGeometry(AreaEffectSimulation::AesExplosionGeometry_def::EgArea); onGeometrySelected(static_cast(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::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 asmDtc; if (!m_session->getDtc(asmDtc)) { WARNING_LOG(m_logger) << "getDtc for AssetManagement::DataInterface failed."; } std::shared_ptr exmDtc; if (!m_session->getDtc(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(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(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::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(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> 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> unitList; // Name, partId, count const QList &superiorIds = units.keys(); for (auto &superiorId : superiorIds) { const ParticipantManagement::Participant &part = m_ppmDtc->participant(superiorId); unitList.push_back(std::tuple(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 &minMaxFd = getMinMaxFlightDurationSec(gearClass, chargeTypeId); const QPair &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::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::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(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(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 asmDtc; // if (!m_session->getDtc(asmDtc)) // { // WARNING_LOG(m_logger) << "getDtc for AssetManagement::DataInterface failed."; // } // std::shared_ptr exmDtc; // if (!m_session->getDtc(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(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(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(AreaEffectSimulation::AesExplosionGeometry_def::EgPoint)); for (QAbstractButton *btn : geoButtons) { if (m_geometrieButtonGroup->id(btn) != static_cast(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 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(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(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 &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(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::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(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(), dds::core::optional(), dds::core::optional()), 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(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(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(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(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> &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>( [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(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