Files
qt5-legatus-legacy/Lts/Services/AreaEffectSimulation/object_effect_cluster.h
T
2026-08-13 18:33:10 +02:00

314 lines
14 KiB
C++
Executable File

/**
* @RDE_HEADER@
* ObjectMap for EffectCluster
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_db.h"
#include "object_base.h"
#include <CentralEffectSimulation/ces_ftc.h>
#include <TrainingAreaBase/tab_ftc.h>
#include <Trajectory/trajectory_generator.h>
#include <LtsUtilsCore/qpoint3d.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <Utils/firing_timer.h>
#include <Utils/polygons.h>
#include <Utils/aes_helper.h>
#include <AreaEffectSimulation/Utils/fire_automaton.h>
#include <QDateTime>
using DetonationLocationRequest = CentralEffectSimulation::DetonationLocationRequest;
using DetonationLocationReply = CentralEffectSimulation::DetonationLocationReply;
const static int DEFAULT_EFFECT_DURATION = 777777;
namespace AreaEffectSimulation
{
//inline bool chargeTypeIdIsVulcano(int chargeTypeId) { return (chargeTypeId == 376) || (chargeTypeId == 393) ; }
class ObjectEffectCluster final
: public ObjectBaseTmpl<Database::AesEffectCluster>
, protected CentralEffectSimulation::Interface::Ftc::DetonationLocationReplyHandler
, protected TrainingAreaBase::Interface::Ftc::AdlerCalculateTrajectoryReplyHandler
, protected TrainingAreaBase::Interface::Ftc::SendExFireReadinessReplyHandler
, protected TrainingAreaBase::Interface::Ftc::SendExFiredReportReplyHandler
, protected TrainingAreaBase::Interface::Ftc::SendExConfirmationReportReplyHandler
{
Q_OBJECT
using BaseClass = ObjectBaseTmpl<Database::AesEffectCluster>;
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 ObjectEffectCluster(MainHandler *mainHandler,
QObject *parent = nullptr);
~ObjectEffectCluster();
bool isActivationTimeReached() const override
{
if (noTracjectoryRequired())
return BaseClass::isActivationTimeReached();
return BaseClass::isActivationTimeReached();
}
bool isFiringTimeReached() const override
{
return m_firingTimeReached;
}
qint32 flightDurationMsec() const override { return m_data.flightDuration; }
QString name() const { return m_data.name; }
bool isOnlyObjectNameModifiable() const override;
bool isObjectModifiable() const override; // is at least one parameter in the object modifyable
Core::FunctionReplyObject createObject(const Database::AesEffectCluster &data, const QVector<Database::AesEffectorData> &effectorDatas);
Core::FunctionReplyObject updateObject(const Database::AesEffectCluster &data, const QVector<Database::AesEffectorData> &effectorDatas) ;
Core::FunctionReplyObject removeObject() override;
Core::FunctionReplyObject checkObjectData(const Database::AesEffectCluster &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 EffectClusterPauseResumeRequest &req);
Core::FunctionReplyObject targetPositionsModify(const TargetPositionsModifyRequest &req);
Core::FunctionReplyObject trajectoryAdlerModify(const TrajectoryAdlerModifyRequest &req);
Core::FunctionReplyObject effectClusterModifyActor(const EffectClusterModifyActorRequest &req);
Core::FunctionReplyObject checkPreConditionsComplete();
Core::FunctionReplyObject checkPreConditions();
bool initialCalculations(QString *reasonOut = nullptr);
void abortPreStartTimer();
std::vector<Core::GeoPosition> 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 isEffectClusterMarkedValid() const { return m_temporaryData.annotationString.empty(); }
bool intersectWithCircle(double lonRad, double latRad, int radius);
void markAsExpired(bool fireLoadedRound);
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 EffectClusterDialog, a value of zero is an error!
// to skip trajectory, provied -1.
bool isOriginatorParticipantIdProvided() const { return m_data.participantId >= 0; } // !! >= !! For EffectClusterDialog, a value of zero is an error!
void setAirSupportSource(bool isAirSupportSource) { m_isAirSupportSource = isAirSupportSource; }
bool setEffectors(const QVector<Database::AesEffectorData> &effectors);
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);
//EffectorControl
bool sendFireCommand(const QString &identifier);
bool sendFireCommand(int participantId);
bool overrideEffectorStates(const AesEffectorState &newState);
void onSendReadinessToTab();
protected:
void setEffectState(AesEffectState newState) override;
void publishAiv();
protected Q_SLOTS:
void onPublishData() override;
void onDetonationLocationReply(const DetonationLocationReply &reply) override;
void onAdlerCalculateTrajectoryReply(const Core::FunctionReplyObject &reply) override;
void onSendExFireReadinessReply(const Core::FunctionReplyObject &reply) override;
void onSendExFiredReportReply(const Core::FunctionReplyObject &reply) override;
void onSendExConfirmationReportReply(const Core::FunctionReplyObject &reply) override;
void checkNextFire();
void checkAutomaton();
void onAutomatonFinished();
// void checkNextSingleExplosion();
//slots triggered by effectors
void onEffectorNotReadyToFire(int participantId, int aesItemId);
void onEffectorReadyToFire(int participantId, int aesItemId);
void onEffectorReceivedOrder(int participantId, int aesItemId);
void onEffectorReportedFired(int participantId, int aesItemId);
void onEffectorFired(int participantId, int aesItemId, const QVector<int> &chargeEffectIds);
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;
void sendCalculateTrajectoryAdlerRequest(const QVector<Database::AesEffectorData> &effectorData);
std::vector<Core::GeoPosition> generateGunToTargetForm(const std::vector<Core::RelativePositionRelativeTime> &trajectoryRelativeToOriginator,
int participantId);
std::vector<Core::GeoPosition> generateGunToTargetForm(QString *reasonOut = nullptr);
static std::vector<Core::RelativePositionRelativeTime> reduceSMArtTrajectoryToSingle(const std::vector<Core::RelativePositionRelativeTime> &trajectory); // E01562-294: Falsche Darstellung Flugkurve SMArt
void publishTrajectory(int explosionIndex);
ParticipantManagement::Participant getParticipantFromInternalName(const QString &name);
bool assignEffector(int participantId);
bool updateEffector(int participantId, const Database::AesEffectorData &data);
void freeEffectors();
//
QVector<Database::AesEffectorData> generateMHPs(const QVector<Database::AesEffectorData> &effectorData, const AreaEffectSimulation::AesExplosionGeometry &geometry);
// QVector<Database::AesEffectorData> generateTrajectoryInternal(const QVector<Database::AesEffectorData> &effectorData);
// to TAB Stuff
void onSendConfirmationReportToTab(int participantId);
//Internal stuff
void onChargeEffectChanged(int chargeEffectId);
// void publishDetonationAiv();
private:
AreaEffectSimulation::EffectorData fromDatabase(const Database::AesEffectorData &data);
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};
QTimer *m_fireHoldWaitforLateFire{nullptr};
QTimer *m_automatonTimer{nullptr};
int m_lastSentTriggeredExplosionNumber;
int m_chargeCountUntilLastStart;
EffectCluster m_EffectCluster;
AesItemView m_aiv; // AtEffectCluster
AesItemView m_aiv2; // AtEffectClusterSingleExplosions - SingleExplosions
bool m_aiv2HasChanged{true};
// Effects
Effects m_effects; // replacement for m_aiv2 & TriggeredImpacts
bool m_aivGunToTargetTrajectoryDirty{false};
bool m_isAirSupportSource{false};
FireAutomaton *m_fireAutomaton {nullptr};
QMap<int, QString> m_publisedTrajectoriesForDebug;
struct temporaryData
{
std::string annotationString;
std::vector<Core::GeoPosition2D> 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<Core::GeoPosition> gunToTargetLine;
// single
std::vector<Core::GeoPosition> 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<Core::GeoPosition> trajectoryGeometry;
TrajectoryTimeAware trajectoryTimeAware;
long flightDurationMSec{0}; // idealValue, intersection with building ist not considered
double peakHeightAbs{0};
};
int m_explosionIndex {0};
int m_lookoutChargeEffectStart {0};
int m_lookoutChargeEffectEnd {0};
int m_lookoutChargeEffectPaused {0};
rti::core::SampleIdentity m_cookie;
bool m_firingTimeReached {false};
bool m_firstTimeReady {true};
QSet<int> m_readytoFire;
QHash<QString, int> m_effectorNameToIdMap;
QHash<int, TracjectoryData> m_trajectoryCont; // key: explosionIndex (which is ExplosionNumber-1)
QHash<int, Database::AesEffectorData> m_effectorData; // key: explosionIndex (which is ExplosionNumber-1)
QMap<int,AesItemView> m_aivGunToTargetTrajectoryMap; // AtEffectClusterGunToTargetLine
QMap<int,int> m_firedPerEfector;
QSet<int> m_firstFireSend;
};
}