Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

@@ -0,0 +1,211 @@
/**
* Base Class for Effector Firing Strategy
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2025-05-11
*/
#include "abstract_firing_strategy.h"
#include "object_effector.h"
#include "main_handler.h"
#include "handler_charge_effect.h"
#include <ParticipantManagement/ppm.hpp>
#include "Trajectory/trajectory_transform.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsUtilsCore/qpoint3d.h>
#include <QJsonDocument>
#include <QRandomGenerator>
namespace AreaEffectSimulation
{
AbstractFiringStrategy::AbstractFiringStrategy(ObjectEffector *parent)
: QObject(parent)
, m_effector(parent)
, m_logger(getLogger("FiringStrategy"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
AbstractFiringStrategy::~AbstractFiringStrategy()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void AbstractFiringStrategy::calculateDispersionTrajectory()
{
DEBUG_LOG(m_logger) << "calculateDispersionTrajectory";
// 1. Neuen Zielpunkt berechnen
const auto &intendendTrajectory = m_effector->intendendTrajectory();
// sanity check:
if (intendendTrajectory.size() < 2)
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " size of intendendTrajectory not sufficiant, size=" << intendendTrajectory.size();
return;
}
LtsUtils::QPoint3D newTarget = intendendTrajectory.last().position;
const int scatterLength = qRound(m_effector->effectorData().mhpScatterLenght());
const int randomValueX = QRandomGenerator::global()->bounded(-scatterLength, scatterLength + 1);
const int scatterWidth = qRound(m_effector->effectorData().mhpScatterWidth());
const int randomValueY = QRandomGenerator::global()->bounded(-scatterWidth, scatterWidth + 1);
newTarget.setX(newTarget.x() + randomValueX);
newTarget.setY(newTarget.y() + randomValueY);
// 2. Neue Trajektorie berechnen
m_transformedTrajectory = TrajectoryTransform::transformToNewTarget(intendendTrajectory, newTarget);
}
std::vector<Core::GeoPositionRelativeTime> AbstractFiringStrategy::buildTrajectoryGeometry(const QVector<TrajectoryPoint> &relativeTrajectory)
{
// sanity check:
if (relativeTrajectory.size() < 2)
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " size of relativeTrajectory not sufficiant, size=" << relativeTrajectory.size();
return {};
}
// setup some vars of effector, which is used as launch position:
Core::GeoPosition effectorPositionGeo = participantPosition(m_effector->participantId());
const LtsCoordTrans::UtmCoordinate &launchUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(effectorPositionGeo.lon(), effectorPositionGeo.lat());
std::vector<Core::GeoPositionRelativeTime> geometry;
geometry.reserve(relativeTrajectory.size());
// Aehnlich wie Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/aes_handler.cpp Zeile 365
for (const TrajectoryPoint &trajPntRelPos : qAsConst(relativeTrajectory))
{
DEBUG_LOG(m_logger) << "Flugbahnpunkt: trajPntRelPos :" << trajPntRelPos; // benefit from operator<<:
// // geographic offers nice feature to add an offset:
// const QPointF &trajectoryAbsGeo = launchUtm.geographic(trajPntRelPos);
// but lets stay with trajectoryAbsUtm to enable DEBUG_LOG
const QPointF &trajPntRelPosUtm = trajPntRelPos.position.toPointF(); // still relativ to launch
const LtsCoordTrans::UtmCoordinate trajPntAbsUtm(launchUtm.zone,
launchUtm.x + trajPntRelPosUtm.x(),
launchUtm.y + trajPntRelPosUtm.y());
const QPointF &trajPntAbsGeo = trajPntAbsUtm.geographic();
// Position des Participants mit der m_participantId
const double zValue = effectorPositionGeo.z() + trajPntRelPos.position.z(); // trajPntRelPos.position.z() ist die Hoehe
DEBUG_LOG(m_logger) << "trajPntAbsUtm :" << trajPntAbsUtm << ", trajPntAbsGeo :" << trajPntAbsGeo;
geometry.emplace_back(Core::GeoPositionRelativeTime(trajPntAbsGeo.x(), trajPntAbsGeo.y(), zValue, trajPntRelPos.time));
}
return geometry;
}
TrajectoryTimeAware AbstractFiringStrategy::buildTrajectoryTimeAware(const std::vector<Core::GeoPositionRelativeTime> &trajectoryGeometry, qint64 trajectoryStartTime, int index)
{
// sanity check:
if (trajectoryGeometry.size() < 2)
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " size of trajectoryGeometry not sufficiant, size=" << trajectoryGeometry.size();
return {};
}
TrajectoryTimeAware trajectory;
trajectory.trajectoryStartTime(trajectoryStartTime);
trajectory.index(index); // see SituationMap : charge_effect_to_map ::updateSingleTrajectory()
// [E01562-259] use originatorId, originatorTypeId (instead of aesItemId, aesTypeId)
trajectory.originatorId(m_effector->aesItemId());
trajectory.originatorTypeId(ProjectDataManagement::StateChangeOriginator::ChargeEffect);
trajectory.geometry(trajectoryGeometry);
return trajectory;
}
int AbstractFiringStrategy::sendChargeEffect(const TrajectoryTimeAware &trajectoryTimeAware, const QJsonObject &properties)
{
DEBUG_LOG(m_logger) << "sendChargeEffect";
const auto &trajGeometry = trajectoryTimeAware.geometry();
// sanity check:
if (trajGeometry.size() < 2)
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " size of trajectoryTimeAware.geometry not sufficiant, size=" << trajGeometry.size();
return -1;
}
const qint64 trajectoryStartTime = trajectoryTimeAware.trajectoryStartTime();
ChargeEffectModifyRequest chargeEffect;
chargeEffect.id(0);
chargeEffect.effectDuration(3000);
chargeEffect.name("Schuss");
chargeEffect.chargeTypeId(m_effector->effectorData().chargeStruct().chargeTypeId());
chargeEffect.effectStart(trajectoryStartTime + trajectoryTimeAware.geometry().back().relativeTime()); // Einschlag Zeit
chargeEffect.igniterTypeId(m_effector->effectorData().chargeStruct().igniterTypeId());
chargeEffect.gearTypeId(gearTypeId(m_effector->effectorData().chargeStruct().chargeTypeId(), m_effector->participantId()));
chargeEffect.originatorId(m_effector->aesItemId());
chargeEffect.originatorTypeId(ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation);
chargeEffect.properties(QJsonDocument(properties).toJson().toStdString());
const Core::GeoPosition &impactPosition = trajGeometry.back();
chargeEffect.position(impactPosition); // Impact Position
chargeEffect.trajectoryTimeAware(trajectoryTimeAware);
const auto &reply = m_effector->mainHandler()->chargeEffectHandler()->createChargeEffect(chargeEffect);
DEBUG_LOG(m_logger) << "reply.id=" << reply.id() << ", str:" << reply.str() << ", uniqueId:" << reply.uniqueId();
return reply.id();
}
Core::GeoPosition AbstractFiringStrategy::participantPosition(int participantId)
{
m_effector->mainHandler()->dds()->ppmDtc()->enableParticipantPosition(true);
const ParticipantManagement::ParticipantPosition &pos = m_effector->mainHandler()->dds()->ppmDtc()->participantPosition(participantId);
return pos.pos();
}
int32_t AbstractFiringStrategy::gearTypeId(int chargeTypeId, int participantId)
{
DEBUG_LOG(m_logger) << "getGearTypeId"
<< ", chargeTypeId:" << chargeTypeId << ", participantId:" << participantId;
m_effector->mainHandler()->dds()->ppmDtc()->enableDynamicParticipantData(true);
m_effector->mainHandler()->dds()->ppmDtc()->waitForHistoricalDataDynamicParticipantData(1000);
const ParticipantManagement::DynamicParticipantData &dynamicParticipantData = m_effector->mainHandler()->dds()->ppmDtc()->dynamicParticipantData(participantId);
DEBUG_LOG(m_logger) << "gearSize " << dynamicParticipantData.gear().size();
const auto& participantData_gear = dynamicParticipantData.gear();
for (const auto &gear : qAsConst(participantData_gear))
{
DEBUG_LOG(m_logger) << "gear.chargeTypeId(): " << gear.chargeTypeId() << ", chargeTypeId:" << chargeTypeId;
if (gear.chargeTypeId() == chargeTypeId)
{
DEBUG_LOG(m_logger) << "getGearTypeId return: " << gear.gearTypeId();
if (gear.gearTypeId() != 0)
return gear.gearTypeId();
else
return 701; // MALTE XKCD
}
}
return 701; // MALTE XKCD
}
qint64 AbstractFiringStrategy::getStartTime() const
{
const auto trajectoryAndEffectStartTime = m_effector->mainHandler()->dds()->currentPartitionTimeMSec() + 1000;
return trajectoryAndEffectStartTime;
}
} // namespace AreaEffectSimulation
@@ -0,0 +1,71 @@
/**
* Base Class for Effector Firing Strategy
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-11
*/
/*
* Diese Implementierung des Strategie- und Factory-Patterns ist ein Schritt, die Logik der Erzeugung der Trajektorien und der ChargeEffects von dem Effektor zu trennen.
* Aus zeitlichen Gründen ist die SW-technische Abhängigkeit Strategie und Effektor bidirektional.
*
* Bei zukünftigen Implementierungen sollte diese Abhängigkeit nur in einer Richtung bestehen.
* Oder anders gesagt: Die Strategie sollte möglichst UNABHÄNGIG (von ihren Vater) sein.
*
* Vorteile waeren:
* - Strategie kann unabhängig erstellt, gestestet und vielfältiger/flexibler ein gesetzt werden.
* - Es ist besser erkennbar, WAS die Strategie genau macht, da Ein- und Ausgangsdaten klar definiert sind.
*/
#pragma once
#include <AreaEffectSimulation/logger.h>
#include <QObject>
#include <Core/core.hpp>
#include <QJsonObject>
namespace Core
{
class GeoPosition;
}
namespace AreaEffectSimulation
{
class ObjectEffector;
class TrajectoryPoint;
class TrajectoryTimeAware;
class AbstractFiringStrategy : public QObject
{
Q_OBJECT
public:
explicit AbstractFiringStrategy(ObjectEffector *parent);
virtual ~AbstractFiringStrategy();
virtual void doFiring() = 0;
Q_SIGNALS:
// Has to be emitted in doFiring() implementation!
void signalFired(int participantId, int aesItemId, const QVector<int> &chargeEffectIds);
protected:
// methods:
void calculateDispersionTrajectory();
std::vector<Core::GeoPositionRelativeTime> buildTrajectoryGeometry(const QVector<TrajectoryPoint> &trajectory);
TrajectoryTimeAware buildTrajectoryTimeAware(const std::vector<Core::GeoPositionRelativeTime> &trajectoryGeometry, qint64 curTime, int index = 0);
int sendChargeEffect(const TrajectoryTimeAware &trajectoryTimeAware, const QJsonObject &properties = QJsonObject());
Core::GeoPosition participantPosition(int participantId);
int32_t gearTypeId(int chargeTypeId, int participantId);
qint64 getStartTime() const;
// attributes:
ObjectEffector *m_effector {nullptr};
QVector<TrajectoryPoint> m_transformedTrajectory;
LOGGER_T &m_logger;
}; // class ObjectEffector
} // namespace AreaEffectSimulation
@@ -0,0 +1,47 @@
/**
* Default Effector Firing Strategy (RRohr)
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-11
*/
#include "default_firing_strategy.h"
#include "object_effector.h"
namespace AreaEffectSimulation
{
DefaultFiringStrategy::DefaultFiringStrategy(ObjectEffector *effector)
: BaseClass(effector)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
DEBUG_LOG(m_logger) << "using DefaultFiringStrategy";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
DefaultFiringStrategy::~DefaultFiringStrategy()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void DefaultFiringStrategy::doFiring()
{
calculateDispersionTrajectory();
const qint64 trajectoryStartTime = getStartTime();
const auto &geometry = buildTrajectoryGeometry(m_transformedTrajectory);
const auto &trajTimeAware = buildTrajectoryTimeAware(geometry, trajectoryStartTime);
const int chargeEffectId = sendChargeEffect(trajTimeAware);
emit signalFired(m_effector->participantId(), m_effector->aesItemId(), {chargeEffectId});
DEBUG_LOG(m_logger) << "created ChargeEffectId: " << chargeEffectId;
}
} //namespace AreaEffectSimulation
@@ -0,0 +1,28 @@
/**
* Default Effector Firing Strategy (RRohr)
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-11
*/
#pragma once
#include "abstract_firing_strategy.h"
namespace AreaEffectSimulation
{
class DefaultFiringStrategy final : public AbstractFiringStrategy
{
using BaseClass = AbstractFiringStrategy;
Q_OBJECT
public:
explicit DefaultFiringStrategy(ObjectEffector *effector);
virtual ~DefaultFiringStrategy();
void doFiring() override;
}; // class DefaultFiringStrategy
} // namespace AreaEffectSimulation
@@ -0,0 +1,190 @@
/**
* Effector Firing Strategy for DM72
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-21
*/
#include "dm72_firing_strategy.h"
#include "main_handler.h"
#include "object_effector.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <TrainingAreaBase/Plugins/AdlerJson/telegrams.h>
#include <Trajectory/trajectory_transform.h>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QTransform>
namespace AreaEffectSimulation
{
Dm72FiringStrategy::Dm72FiringStrategy(ObjectEffector *effector)
: AbstractFiringStrategy(effector)
{
init();
DEBUG_LOG(m_logger) << "using Dm72FiringStrategy";
}
void Dm72FiringStrategy::doFiring()
{
m_transformedTrajectory = TrajectoryTransform::transformToNewTarget(m_effector->intendendTrajectory(), m_impactPoints.at(m_impactPointIndex++));
if (m_impactPointIndex >= m_impactPoints.size())
m_impactPointIndex = 0;
const qint64 trajectoryStartTime = getStartTime();
const auto &geometry = buildTrajectoryGeometry(m_transformedTrajectory);
const auto &trajTimeAware = buildTrajectoryTimeAware(geometry, trajectoryStartTime);
QJsonObject props{{QStringLiteral("effectDurationMsec"), m_effectDurationMsec}};
const int chargeEffectId = sendChargeEffect(trajTimeAware, props);
emit signalFired(m_effector->participantId(), m_effector->aesItemId(), {chargeEffectId});
}
void Dm72FiringStrategy::init()
{
using namespace AdlerJson;
using namespace LtsCoordTrans;
m_impactPointIndex = 0;
m_impactPoints.clear();
bool bOk;
WirkmittelFeuerkommandoRakete wirkmittel;
const QJsonObject properties = QJsonDocument::fromJson(QByteArray::fromStdString(m_effector->effectorData().properties())).object();
const Core::GeoPosition geoStartPos = participantPosition(m_effector->participantId());
const UtmCoordinate utmStartPos = UtmCoordinate::fromGeographic(geoStartPos.lon(), geoStartPos.lat());
wirkmittelFeuerkommandoRaketeFromJson(properties, wirkmittel, bOk);
using UtmWithHeight = std::pair<UtmCoordinate, int>;
m_effectDurationMsec = getFeuerkommandoRaketeEffectDurationMsec(properties);
QVector<UtmWithHeight> tempUtmTreffpunkte;
tempUtmTreffpunkte.reserve(wirkmittel.geschoss.anzahl);
for (const Treffpunkt &treffpunkt : qAsConst(wirkmittel.treffpunkte))
{
const UtmCoordinate utmTreffpunkt = UtmCoordinate::fromGeographic(treffpunkt.koordinate.koordinate.longitude * deg2rad(),
treffpunkt.koordinate.koordinate.latitude * deg2rad(),
utmStartPos.zone);
for (int anzahlCounter = 1; anzahlCounter <= treffpunkt.anzahl; ++anzahlCounter)
tempUtmTreffpunkte.append({utmTreffpunkt, treffpunkt.koordinate.hoehe});
}
const QVector<QPointF> mineLayingCentroidDeviations = mineLayingCentroids();
int deviationIndex = 0;
for (const UtmWithHeight &tempUtmTreffpunkt : tempUtmTreffpunkte)
{
const QPointF &deviation = mineLayingCentroidDeviations.at(deviationIndex++);
const LtsUtils::QPoint3D impactPoint(tempUtmTreffpunkt.first.x - utmStartPos.x + deviation.x(),
tempUtmTreffpunkt.first.y - utmStartPos.y + deviation.y(),
tempUtmTreffpunkt.second);
m_impactPoints.append(impactPoint);
if (deviationIndex >= mineLayingCentroidDeviations.size())
deviationIndex = 0;
}
DEBUG_LOG(m_logger) << "Got " << m_impactPoints.size() << " impact points";
}
QVector<QPointF> Dm72FiringStrategy::mineLayingCentroids() const
{
using namespace LtsCoordTrans;
const int chargeTypeId = m_effector->effectorData().chargeStruct().chargeTypeId();
const ProjectDataManagement::ChargeType &chargeType = m_effector->mainHandler()->dds()->pdmDtc()->chargeType(chargeTypeId);
const QJsonObject chargeTypeProperties = QJsonDocument::fromJson(QByteArray::fromStdString(chargeType.properties())).object();
const QJsonObject mineLaying = chargeTypeProperties.value(QStringLiteral("mineLaying")).toObject();
const QJsonArray mineLayingPattern = mineLaying.value(QStringLiteral("pattern")).toArray();
/* QMap is used here because it sort its entries by key.
* Since each pattern has an id and we want the pattern sorted by id, adding them to a QMap
* automatically sorts the pattern.
*/
QMap<int, QPointF> centroidsMap;
const UtmCoordinate mhpUtm = UtmCoordinate::fromGeographic(m_effector->effectorData().mhpPosition().lon(),
m_effector->effectorData().mhpPosition().lat());
const double rotationAngle = angleToMhpUtm(mhpUtm.toPoint());
QTransform rotation;
rotation.rotate(rotationAngle);
for (const QJsonValue &patternValue: mineLayingPattern)
{
const QJsonObject patternObject = patternValue.toObject();
const int patternId = patternObject.value(QStringLiteral("id")).toInt();
const QJsonArray patternPositionArray = patternObject.value(QStringLiteral("positions")).toArray();
int sumNorthing = 0;
int sumEasting = 0;
for (const QJsonValue &positionValue : patternPositionArray)
{
const QJsonObject positionObject = positionValue.toObject();
const int northing = positionObject.value(QStringLiteral("northing")).toInt();
const int easting = positionObject.value(QStringLiteral("easting")).toInt();
sumNorthing += northing;
sumEasting += easting;
}
QPointF centroid(static_cast<double>(sumEasting) / patternPositionArray.size(), static_cast<double>(sumNorthing) / patternPositionArray.size());
centroid = rotation.map(centroid);
centroidsMap.insert(patternId, centroid);
}
if (centroidsMap.isEmpty())
{
WARNING_LOG(m_logger) << "Could not read mine laying patterns from DM72 ammunition properties for creation of DM72 trajectories.";
return { QPointF(0.0, 0.0) }; // We have to return at least one QPointF(0.0, 0.0) to ensure later DM72 functionality
}
int vectorIndex = 0;
QVector<QPointF> mineLayingCentroids;
mineLayingCentroids.reserve(centroidsMap.size());
for (auto it = centroidsMap.constBegin(); it != centroidsMap.constEnd(); ++it)
mineLayingCentroids.insert(vectorIndex++, it.value()); // insert with index ensures sorting
return mineLayingCentroids;
}
double Dm72FiringStrategy::angleToMhpUtm(const QPointF &mhpUtm) const
{
const ParticipantManagement::ParticipantPosition &ownPosition = m_effector->mainHandler()->dds()->ppmDtc()->participantPosition(m_effector->participantId());
const LtsCoordTrans::UtmCoordinate ownPositionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(ownPosition.pos().lon(), ownPosition.pos().lat());
const double angle = QLineF(mhpUtm, ownPositionUtm.toPoint()).angle();
double northAngle = 90 - angle;
northAngle = std::fmod(northAngle, 360.0);
if (northAngle < 0.0)
northAngle += 360.0;
DEBUG_LOG(m_logger) << "angleToMhpUtm:"
<< "mhpUtm=" << mhpUtm
<< ", ownPositionUtm=" << ownPositionUtm.toPoint()
<< ", angle=" << angle
<< ", northAngle=" << northAngle;
return northAngle;
}
} // namespace AreaEffectSimulation
@@ -0,0 +1,40 @@
/**
* Effector Firing Strategy for DM72
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-21
*/
#pragma once
#include "abstract_firing_strategy.h"
namespace LtsUtils
{
class QPoint3D;
}
namespace AreaEffectSimulation
{
class Dm72FiringStrategy : public AbstractFiringStrategy
{
Q_OBJECT
public:
explicit Dm72FiringStrategy(ObjectEffector *effector);
void doFiring() override final;
private: // methods
void init();
QVector<QPointF> mineLayingCentroids() const;
double angleToMhpUtm(const QPointF &mhpUtm) const;
private: // attributes
QVector<LtsUtils::QPoint3D> m_impactPoints;
int m_impactPointIndex {0};
int m_effectDurationMsec = 3 * 60 * 60 * 1000; // default 3h
}; // class Dm72FiringStrategy
} // namespace AreaEffectSimulation
@@ -0,0 +1,64 @@
/**
* Namespace and function for creating a Firing Strategy
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-11
*/
#include "firing_strategy_factory.h"
#include "default_firing_strategy.h"
#include "dm72_firing_strategy.h"
#include "gmlrs_firing_strategy.h"
#include "smart_firing_strategy.h"
#include "main_handler.h"
#include "object_effector.h"
namespace AreaEffectSimulation
{
namespace FiringStrategyFactory
{
AbstractFiringStrategy *createFiringStrategy(ObjectEffector *parent)
{
LOGGER_T &logger = getLogger("FiringStrategyFactory");
TRACE_LOG(logger) << LOGFUNCTION << " START";
if (!parent)
return nullptr;
const ChargeStruct &cs = parent->effectorData().chargeStruct();
DEBUG_LOG(logger) << LOGFUNCTION << " ChargeStruct = " << cs;
// GMLRS
if (parent->mainHandler()->dds()->pdmDtc()->isGmlrs(cs.chargeTypeId()))
{
TRACE_LOG(logger) << LOGFUNCTION << " END";
return new GmlrsFiringStrategy(parent);
}
// SMArt
const bool bIsSmart = parent->mainHandler()->dds()->pdmDtc()->isSmart(cs.chargeTypeId(), cs.igniterTypeId(), cs.igniterFunctionId());
if (bIsSmart)
{
TRACE_LOG(logger) << LOGFUNCTION << " END";
return new SmartFiringStrategy(parent);
}
// DM72
if (parent->mainHandler()->dds()->pdmDtc()->isDm72(cs.chargeTypeId()))
{
TRACE_LOG(logger) << LOGFUNCTION << " END";
return new Dm72FiringStrategy(parent);
}
TRACE_LOG(logger) << LOGFUNCTION << " END";
return new DefaultFiringStrategy(parent);
}
} //namespace FiringFactory
} //namespace AreaEffectSimulation
@@ -0,0 +1,22 @@
/**
* Namespace and function for creating a Firing Strategy
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-11
*/
#pragma once
namespace AreaEffectSimulation
{
class AbstractFiringStrategy;
class ObjectEffector;
namespace FiringStrategyFactory
{
AbstractFiringStrategy *createFiringStrategy(ObjectEffector *parent);
} //namespace FiringFactory
} //namespace AreaEffectSimulation
@@ -0,0 +1,87 @@
/**
* Effector Firing Strategy for GMLRS
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-12
*/
#include "gmlrs_firing_strategy.h"
#include "object_effector.h"
#include <TrainingAreaBase/Plugins/AdlerJson/telegrams.h>
#include <Trajectory/trajectory_transform.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <QJsonDocument>
#include <QJsonObject>
namespace AreaEffectSimulation
{
GmlrsFiringStrategy::GmlrsFiringStrategy(ObjectEffector *effector)
: BaseClass(effector)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
init();
DEBUG_LOG(m_logger) << "using GmlrsFiringStrategy";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
GmlrsFiringStrategy::~GmlrsFiringStrategy()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void GmlrsFiringStrategy::doFiring()
{
m_transformedTrajectory = TrajectoryTransform::transformToNewTarget(m_effector->intendendTrajectory(), m_impactPoints.at(m_impactPointIndex++));
if (m_impactPointIndex >= m_impactPoints.size())
m_impactPointIndex = 0;
const qint64 trajectoryStartTime = getStartTime();
const auto &geometry = buildTrajectoryGeometry(m_transformedTrajectory);
const auto &trajTimeAware = buildTrajectoryTimeAware(geometry, trajectoryStartTime);
const int chargeEffectId = sendChargeEffect(trajTimeAware);
emit signalFired(m_effector->participantId(), m_effector->aesItemId(), {chargeEffectId});
}
void GmlrsFiringStrategy::init()
{
using namespace AdlerJson;
m_impactPointIndex = 0;
m_impactPoints.clear();
const QJsonObject &properties = QJsonDocument::fromJson(QByteArray::fromStdString(m_effector->effectorData().properties())).object();
const Core::GeoPosition &geoStartPos = participantPosition(m_effector->participantId());
const LtsCoordTrans::UtmCoordinate &utmStartPos = LtsCoordTrans::UtmCoordinate::fromGeographic(geoStartPos.lon(), geoStartPos.lat());
bool bOk = false;
WirkmittelFeuerkommandoRakete wirkmittel;
wirkmittelFeuerkommandoRaketeFromJson(properties, wirkmittel, bOk);
for (const Treffpunkt &treffpunkt : qAsConst(wirkmittel.treffpunkte))
{
const LtsCoordTrans::UtmCoordinate utmTreffpunkt =
LtsCoordTrans::UtmCoordinate::fromGeographic(treffpunkt.koordinate.koordinate.longitude * LtsCoordTrans::deg2rad(),
treffpunkt.koordinate.koordinate.latitude * LtsCoordTrans::deg2rad());
const LtsUtils::QPoint3D relImpactPoint(utmTreffpunkt.x - utmStartPos.x,
utmTreffpunkt.y - utmStartPos.y,
treffpunkt.koordinate.hoehe);
for (int anzahlCounter = 1; anzahlCounter <= treffpunkt.anzahl; ++anzahlCounter)
m_impactPoints.append(relImpactPoint);
}
DEBUG_LOG(m_logger) << "Got " << m_impactPoints.size() << " impact points";
}
} // namespace AreaEffectSimulation
@@ -0,0 +1,40 @@
/**
* Effector Firing Strategy for GMLRS
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-12
*/
#pragma once
#include "abstract_firing_strategy.h"
#include <LtsUtilsCore/qpoint3d.h>
namespace Core
{
class RelativePosition;
}
namespace AreaEffectSimulation
{
class GmlrsFiringStrategy final : public AbstractFiringStrategy
{
using BaseClass = AbstractFiringStrategy;
Q_OBJECT
public:
explicit GmlrsFiringStrategy(ObjectEffector *effector);
virtual ~GmlrsFiringStrategy();
void doFiring() override;
private: // methods
void init();
private: // attributes
QVector<LtsUtils::QPoint3D> m_impactPoints;
int m_impactPointIndex {0};
}; // class GmlrsFiringStrategy
} // namespace AreaEffectSimulation
@@ -0,0 +1,102 @@
/**
* Effector Firing Strategy for SMart
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, ESVG, 2026-05-20
*/
#include "smart_firing_strategy.h"
#include "object_effector.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <TrainingAreaBase/Plugins/AdlerJson/telegrams.h>
#include <TrajectorySplitter/trajectory_splitter.h>
#include <QJsonDocument>
#include <QJsonObject>
namespace AreaEffectSimulation
{
SmartFiringStrategy::SmartFiringStrategy(ObjectEffector *effector)
: BaseClass(effector)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
DEBUG_LOG(m_logger) << "using SmartFiringStrategy";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
SmartFiringStrategy::~SmartFiringStrategy()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void SmartFiringStrategy::doFiring()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
// by default, following is done
// - calculateDispersionTrajectory
// - calc new target
// - calc matching trajectory
// (intendendTrajectory -> m_transformedTrajectory)
// - sendChargeEffect
// - emit signalFired
// for SMArt, calculateDispersionTrajectory is not necessary
// But we have to split the tow-in-one trajectory:
const qint64 trajectoryStartTime = getStartTime();
//
// first check, if trajectory is a tow-in-one trajectory:
//
// convert from intendendTrajectory (relativ) to TRAJECTORY_TYPE (absolute)
// The TrajectorySplitter needs TRAJECTORY_TYPE
// using TRAJECTORY_TYPE = std::vector<Core::GeoPositionRelativeTime>;
const auto &intendendTrajectory = m_effector->intendendTrajectory();
const auto &trajGeometry = buildTrajectoryGeometry(intendendTrajectory);
// split and avoid copying:
const TrajectorySplitter<std::vector<Core::GeoPositionRelativeTime>> splitter(trajGeometry);
const auto &trajGeo1 = splitter.getTrajectory1();
const auto &trajGeo2 = splitter.getTrajectory2();
DEBUG_LOG(m_logger) << "splitter: " << " intendendTrajectory.size = " << intendendTrajectory.size();
DEBUG_LOG(m_logger) << "splitter: " << " trajGeometry.size = " << trajGeometry.size();
DEBUG_LOG(m_logger) << "splitter: " << " rawTrajectorySize = " << splitter.rawTrajectorySize();
DEBUG_LOG(m_logger) << "splitter: " << " trajGeo1 = " << trajGeo1.size();
DEBUG_LOG(m_logger) << "splitter: " << " trajGeo2 = " << trajGeo2.size();
//
// and second, process both Trajectories/Impacts
//
// ToDo: Check, how oftem signalFired should be fired?
QVector<int> chargeEffectIds;
if (trajGeo1.size() > 2)
{
const auto &trajTimeAware = buildTrajectoryTimeAware(trajGeo1, trajectoryStartTime, 0);
const int chargeEffectId = sendChargeEffect(trajTimeAware);
chargeEffectIds << chargeEffectId;
DEBUG_LOG(m_logger) << "Trajectory1 - created ChargeEffectId: " << chargeEffectId;
}
if (trajGeo2.size() > 2)
{
const auto &trajTimeAware = buildTrajectoryTimeAware(trajGeo2, trajectoryStartTime, 1);
const int chargeEffectId = sendChargeEffect(trajTimeAware);
chargeEffectIds << chargeEffectId;
DEBUG_LOG(m_logger) << "Trajectory2 - created ChargeEffectId: " << chargeEffectId;
}
if (chargeEffectIds.size())
emit signalFired(m_effector->participantId(), m_effector->aesItemId(), chargeEffectIds);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
} // namespace AreaEffectSimulation
@@ -0,0 +1,37 @@
/**
* Effector Firing Strategy for SMart
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, ESVG, 2026-05-20
*/
#pragma once
#include "abstract_firing_strategy.h"
namespace Core
{
class RelativePosition;
}
namespace AreaEffectSimulation
{
class SmartFiringStrategy final : public AbstractFiringStrategy
{
using BaseClass = AbstractFiringStrategy;
Q_OBJECT
public:
explicit SmartFiringStrategy(ObjectEffector *effector);
virtual ~SmartFiringStrategy();
void doFiring() override;
private: // methods
private: // attributes
}; // class SmartFiringStrategy
} // namespace AreaEffectSimulation