/** * @RDE_HEADER@ * ObjectMap for ExplosionCluster * $Id$ * $HeadURL$ * $Author$ */ #pragma once #include "aes_db.h" #include "object_base.h" #include #include #include #include #include #include #include #include using DetonationLocationRequest = CentralEffectSimulation::DetonationLocationRequest; using DetonationLocationReply = CentralEffectSimulation::DetonationLocationReply; namespace AreaEffectSimulation { class ObjectExplosionCluster final : public ObjectBaseTmpl , protected CentralEffectSimulation::Interface::Ftc::DetonationLocationReplyHandler { Q_OBJECT using BaseClass = ObjectBaseTmpl; public: constexpr static double s_defaultZ_Value = 100; // default Elevation for GUEZ / Altmark. Hopefully fine for other areas.. constexpr static double s_invalid_rotation_angle = -1000; explicit ObjectExplosionCluster(MainHandler *mainHandler, QObject *parent = nullptr); ~ObjectExplosionCluster(); bool isActivationTimeReached() const override { if (noTracjectoryRequired()) return BaseClass::isActivationTimeReached(); return false; } qint32 flightDurationMsec() const override { return m_data.flightDuration; } bool isOnlyObjectNameModifiable() const override; bool isObjectModifiable() const override; // is at least one parameter in the object modifyable bool isFiringTimeReached() const override; Core::FunctionReplyObject createObject(const Database::AesExplosionCluster &data); Core::FunctionReplyObject updateObject(const Database::AesExplosionCluster &data) override; Core::FunctionReplyObject removeObject() override; Core::FunctionReplyObject checkObjectData(const Database::AesExplosionCluster &data) const; /*! * \brief removeObjectFromDb * \details Removes the object from db, Used if the object is created but there are to much objects (amount border) * \author Niklas Schmidt * \return bool if the deletion was working */ bool removeObjectFromDb(); Core::FunctionReplyObject resumePause(const ExplosionClusterPauseResumeRequest &req); Core::FunctionReplyObject targetPositionsModify(const TargetPositionsModifyRequest &req); Core::FunctionReplyObject checkPreConditionsComplete(); Core::FunctionReplyObject checkPreConditions(); bool initialCalculations(QString *reasonOut = nullptr); void abortPreStartTimer(); std::vector form() const override { return m_aiv.form(); } QPolygonF generateForm() override; AesType aesType() const override { return AesType::AtExplosionArea; } bool isCentral() const override { return m_data.isCentral; } bool isExplosionClusterMarkedValid() const { return m_temporaryData.annotationString.empty(); } void removeTopics(); void setCookie(const rti::core::SampleIdentity &cookie) {m_cookie = cookie;} // Vulcano bool isVulcano() const { return chargeTypeIdIsVulcano(m_data.chargeTypeId); } // From HLA, no participantId -> -1; // 0 is default. For ExplosionClusterDialog, a value of zero is an error! // to skip trajectory, provied -1. bool isOriginatorParticipantIdProvided() const { return m_data.participantId >= 0; } // !! >= !! For ExplosionClusterDialog, a value of zero is an error! void setAirSupportSource(bool isAirSupportSource) { m_isAirSupportSource = isAirSupportSource; } bool isMhpRotationProvided() const { return m_data.mhpRotationRequest > s_invalid_rotation_angle; } bool noTracjectoryRequired() const { return (m_data.externalId > 0) || (m_data.participantId <= 0); } void setAesProperty(QJsonObject property, bool pubish = true); protected: void setEffectState(AesEffectState newState) override; void publishAiv(); protected Q_SLOTS: void onPublishData() override; void onDetonationLocationReply(const DetonationLocationReply &reply) override; void checkNextFire(); // void checkNextSingleExplosion(); void onFiringTimerFiring(int currentFiringNumber, int totalAmmoCount); void onFiringTimerImpact(int currentExplosionNumber, int totalAmmoCount); void onFiringTimerStateChangeDelayedCommandAsInt(int cmdAsInt, int firedAmmoCount); protected: bool generateTrajectory(int explosionNumber, bool startDetonationLocationRequest = true, QString *reasonOut = nullptr); bool generateTrajectoryVulcanoSpecific(int explosionNumber, const TrajectoryGeneratorVulcano::Options options, QString *reasonOut = nullptr); double getElevationFor(const QPointF &originGeoRadiant, const QString &context) const; void adjustTrajectoryToDetonationLocation(int explosionIndex, const Core::GeoPosition &intersectionLocation, const int buildingId); // void setTrajectoryStartTime(int explosionIndex); Core::FunctionReplyObject setupFiringOrigin(); bool getFiringUnitOrigin(const int firingParticipantId, LtsUtils::QPoint3D &firingOriginOut, QString *reasonOut = nullptr); bool firingOriginIsValid(const LtsUtils::QPoint3D &firingOrigin, QString *reasonOut) const; LtsUtils::QPoint3D getFiringCannonGeoPosition(int explosionIndex) const; LtsUtils::QPoint3D getFiringCannonUtmPosition(int explosionIndex, int zoneToUse) const; std::vector generateGunToTargetForm(QString *reasonOut = nullptr); void publishTrajectory(int explosionIndex); void publishTrajectoryViaHandler(const TrajectoryTimeAware& trajectoryTimeAwareRef); // void publishDetonationAiv(); private: void dump(const char *file = nullptr, int lineNo = 0) const; // void startSingleExplosionHandling(); // void stopSingleExplosionHandling(AesEffectState newState); // qint64 impactDeltaMSec() const; // int currentExplosionNumber() const; DetonationLocationRequest detonationLocationRequest(int explosionIndex) const; private: QTimer *m_singleExplosionTriggerTimer{nullptr}; QTimer *m_preStartTimer{nullptr}; int m_lastSentTriggeredExplosionNumber; int m_chargeCountUntilLastStart; FiringTimer m_firingTimer; ExplosionCluster m_explosionCluster; AesItemView m_aiv; // AtExplosionCluster AesItemView m_aiv2; // AtExplosionClusterSingleExplosions - SingleExplosions bool m_aiv2HasChanged{true}; // Effects Effects m_effects; // replacement for m_aiv2 & TriggeredImpacts AesItemView m_aivGunToTargetTrajectory; // AtExplosionClusterGunToTargetLine bool m_aivGunToTargetTrajectoryDirty{false}; bool m_isAirSupportSource{false}; QMap m_publisedTrajectoriesForDebug; struct temporaryData { std::string annotationString; std::vector mhPoints; double mhpWidth {0.0}; double mhpLength {0.0}; double mhpRotation {0.0}; long estimatedMaxFlightDurationMSec{0}; double calculatedFiringAngle {0}; // degree double calculatedFiringSpeed {0}; // m/sec double calculatedPeakHeightAbs {0}; long calculatedFlightDurationMsec {0}; double calculatedMhpRotationNorthDeg {0}; // center LtsCoordTrans::UtmCoordinate m_firingUnitOriginUtm; LtsUtils::QPoint3D m_firingUnitOriginUtmPoint3D; std::vector gunToTargetLine; // single std::vector singleFiringPositions; // 1..4 bool isObjectModifiable{true}; // will be set to false via checkNextFire by m_preStartTimer void reset() { annotationString.clear(); mhPoints.clear(); mhpWidth = 0.0; mhpLength = 0.0; mhpRotation = 0.0; estimatedMaxFlightDurationMSec = 0; calculatedFiringAngle = 0; calculatedFiringSpeed = 0; calculatedPeakHeightAbs = 0; calculatedFlightDurationMsec = 0; calculatedMhpRotationNorthDeg = 0; gunToTargetLine.clear(); m_firingUnitOriginUtm = LtsCoordTrans::UtmCoordinate(); m_firingUnitOriginUtmPoint3D = LtsUtils::QPoint3D(); singleFiringPositions.clear(); isObjectModifiable = true; } Core::GeoPosition getFiringCannonGeoPosition(int explosionIndex) const { if (singleFiringPositions.empty()) return Core::GeoPosition(std::nan(""), std::nan(""), std::nan("")); const int numberCannons = singleFiringPositions.size(); int curIdx = explosionIndex % numberCannons; return singleFiringPositions[curIdx]; } } m_temporaryData; struct TracjectoryData { int index{-1}; int buildingId{-1}; // BuildingId, which has intersection with trajectory std::vector trajectoryGeometry; TrajectoryTimeAware trajectoryTimeAware; long flightDurationMSec{0}; // idealValue, intersection with building ist not considered double peakHeightAbs{0}; }; int m_explosionIndex = 0; rti::core::SampleIdentity m_cookie; QHash m_trajectoryCont; // key: explosionIndex (which is ExplosionNumber-1) QHash m_trajectoryHandlerExplosionIndexToTopicId; // key: explosionIndex ; value: id in context TrajectoryHandler }; }