Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+211
@@ -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
|
||||
+71
@@ -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
|
||||
+47
@@ -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
|
||||
+28
@@ -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
|
||||
+190
@@ -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
|
||||
+40
@@ -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
|
||||
+64
@@ -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
|
||||
+22
@@ -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
|
||||
+87
@@ -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
|
||||
+40
@@ -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
|
||||
+102
@@ -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
|
||||
+37
@@ -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
|
||||
Reference in new issue
Block a user