Files
qt5-legatus-legacy/Lts/Services/CentralEffectSimulation/Plugins/MineLayingArtillery/handler_mine_laying_artillery.cpp
T
2026-08-13 18:33:10 +02:00

283 lines
12 KiB
C++
Executable File

/**
* Handler for Mine Laying Artillery
* $Id$
* $HeadURL$
* $Author$
* Created by Nino Pinzer, ESZS, 12.03.2026
*/
#include "handler_mine_laying_artillery.h"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QTimer>
#include <QTransform>
#include <qrandom.h>
namespace MineLayingArtillery
{
HandlerMineLayingArtillery::HandlerMineLayingArtillery(const std::shared_ptr<CentralEffectSimulation::DdsHandler> &ddsHandler, QObject *parent)
: QObject(parent)
, m_effectClusterTimer(new QTimer(this))
, m_ddsHandler(ddsHandler)
, m_logger(CentralEffectSimulation::getLogger("Plugin.MineLayingArtillery.HandlerMineLayingArtillery"))
{
INFO_LOG(m_logger) << "HandlerMineLayingArtillery::HandlerMineLayingArtillery";
m_effectClusterTimer->setInterval(500);
}
HandlerMineLayingArtillery::~HandlerMineLayingArtillery() { INFO_LOG(m_logger) << "HandlerMineLayingArtillery::~HandlerMineLayingArtillery"; }
bool HandlerMineLayingArtillery::init()
{
INFO_LOG(m_logger) << "HandlerMineLayingArtillery::init";
m_dtcAes = m_ddsHandler->dtcAes();
m_ftcAes = m_ddsHandler->ftcAes();
m_dtcPdm = m_ddsHandler->dtcPdm();
m_dtcPdm->enableChargeType();
m_dtcAes->enableChargeEffect();
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::chargeEffectChanged, this, &HandlerMineLayingArtillery::onChargeEffectChanged);
connect(m_dtcPdm.get(), &ProjectDataManagement::Interface::Dtc::chargeTypeChanged, this, &HandlerMineLayingArtillery::onChargeTypeChanged);
connect(m_effectClusterTimer, &QTimer::timeout, this, &HandlerMineLayingArtillery::onEffectClusterTimerTimeout);
const QMap<int, ProjectDataManagement::ChargeType> &chargeTypes = m_dtcPdm->chargeTypes();
for (const auto &chargeType : chargeTypes)
onChargeTypeChanged(chargeType, DataModification::eAdded);
return true;
}
double HandlerMineLayingArtillery::calculateAngle(const AreaEffectSimulation::TrajectoryTimeAware &trajectory)
{
Core::GeoPositionRelativeTime trajectoryGeometryFirst = trajectory.geometry().front();
Core::GeoPositionRelativeTime trajectoryGeometryLast = trajectory.geometry().back();
const LtsCoordTrans::UtmCoordinate &trajectoryGeometryFirstUtm
= LtsCoordTrans::UtmCoordinate::fromGeographic(QPointF(trajectoryGeometryFirst.lon(), trajectoryGeometryFirst.lat()));
const LtsCoordTrans::UtmCoordinate &trajectoryGeometryLastUtm
= LtsCoordTrans::UtmCoordinate::fromGeographic(QPointF(trajectoryGeometryLast.lon(), trajectoryGeometryLast.lat()), trajectoryGeometryFirstUtm.zone);
QLineF lineFromStartToImpact
= QLineF(QPointF(trajectoryGeometryFirstUtm.x, trajectoryGeometryFirstUtm.y), QPointF(trajectoryGeometryLastUtm.x, trajectoryGeometryLastUtm.y));
return lineFromStartToImpact.angle();
}
std::vector<AreaEffectSimulation::MineData> HandlerMineLayingArtillery::calculateImpacts(const QJsonObject &mineLayingPattern,
const Core::GeoPosition2D &impactPositon,
double angle,
qint64 time,
int patternCounter)
{
QJsonArray patternsArray = mineLayingPattern[QStringLiteral("pattern")].toArray();
if (patternsArray.isEmpty() || patternCounter < 0 || patternCounter >= patternsArray.size())
return std::vector<AreaEffectSimulation::MineData>();
const double appearanceTimeOffset = mineLayingPattern[QStringLiteral("appearanceTimeOffset")].toDouble();
const double appearanceJitter = mineLayingPattern[QStringLiteral("appearanceJitter")].toDouble();
const double armingTimeOffset = mineLayingPattern[QStringLiteral("armingTimeOffset")].toDouble();
const double armingJitter = mineLayingPattern[QStringLiteral("armingJitter")].toDouble();
DEBUG_LOG(m_logger) << "[calculateImpacts] ## \n\tappearanceTimeOffset: " << appearanceTimeOffset << "\n\tappearanceJitter: " << appearanceJitter << "\n\tarmingTimeOffset: " << armingTimeOffset<<"\n\ttime: "<<time;
const LtsCoordTrans::UtmCoordinate impactPostionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(QPointF(impactPositon.lon(), impactPositon.lat()));
QJsonObject patternObj = patternsArray[patternCounter].toObject();
QJsonArray positionsArray = patternObj[QStringLiteral("positions")].toArray();
std::vector<AreaEffectSimulation::MineData> transformedImpacts;
transformedImpacts.reserve(positionsArray.size());
for (const QJsonValue &posValue : positionsArray)
{
AreaEffectSimulation::MineData mineData;
QJsonObject posObj = posValue.toObject();
double easting = posObj[QStringLiteral("easting")].toDouble();
double northing = posObj[QStringLiteral("northing")].toDouble();
const double newAngle = static_cast<int>(angle + 90.0) % 360;
QTransform t;
t.rotate(-(newAngle));
QPointF positionRot = t.map(QPointF(easting, northing));
QPointF transformedImpactPosition = impactPostionUtm.geographic(positionRot.x(), positionRot.y());
const double appearenceTime = time + appearanceTimeOffset + QRandomGenerator::global()->bounded(0, appearanceJitter * 2) - appearanceJitter;
const double armingTime = time + armingTimeOffset + QRandomGenerator::global()->bounded(0, armingJitter * 2) - armingJitter;
DEBUG_LOG(m_logger) <<"[calculateImpacts] patternCount: "<<patternCounter<< " appearenceTime: " << appearenceTime << " armingTime: " << armingTime;
mineData.position(Core::GeoPosition2D(transformedImpactPosition.x(), transformedImpactPosition.y()));
mineData.appearenceTime(appearenceTime);
mineData.armingTime(armingTime);
transformedImpacts.push_back(mineData);
}
DEBUG_LOG(m_logger)<<"[calculateImpacts] ### pattern patterCount: "<< patternCounter <<" patternsArray.size(): "<<patternsArray.size();
return transformedImpacts;
}
void HandlerMineLayingArtillery::onChargeTypeChanged(const ProjectDataManagement::ChargeType &data, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
case DataModification::eModified:
{
if (!m_dtcPdm->isMineLaying(data.id(), 0, 0))
return;
QJsonObject mineLayingPattern = m_dtcPdm->mineLayingPattern(data.id(), 0, 0);
m_mineLayingChargeTypes.insert(data.id(), mineLayingPattern);
return;
}
case DataModification::eRemoved:
m_mineLayingChargeTypes.remove(data.id());
return;
}
}
void HandlerMineLayingArtillery::onChargeEffectModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
ERROR_LOG(m_logger)<<"[CES ChargeEffectModifyReply]: "<<replyObject.str();
}
void HandlerMineLayingArtillery::onEffectClusterTimerTimeout()
{
if (m_effectClusterQue.isEmpty())
return;
const AreaEffectSimulation::ChargeEffect chargeEffect = m_effectClusterQue.dequeue();
const int chargeTypeId = chargeEffect.chargeTypeId();
QByteArray jsonData = QByteArray::fromStdString(chargeEffect.properties());
QJsonDocument doc = QJsonDocument::fromJson(jsonData);
bool alreadyUsed = false;
if (!doc.isNull() && doc.isObject())
{
QJsonObject obj = doc.object();
alreadyUsed = obj[QStringLiteral("used")].toBool();
}
if (!m_mineLayingChargeTypes.contains(chargeTypeId) || alreadyUsed)
return;
const int chargeEffectId = chargeEffect.id();
const QJsonObject &mineLayingProperties = m_mineLayingChargeTypes[chargeTypeId];
const int mineChargeTypeId = mineLayingProperties[QStringLiteral("mineChargeTypeId")].toInt();
AreaEffectSimulation::EffectCluster ec = m_dtcAes->effectCluster(chargeEffect.originatorId());
DEBUG_LOG(m_logger)<<" [onChargeEffectChanged] effectClusterId: "<<ec.id()<<" mineChargeTypeId: "<<mineChargeTypeId;
if (mineChargeTypeId <= 0)
{
ERROR_LOG(m_logger)<<" [onChargeEffectChanged] mineChargeTypeId invalid!!! chargeEffectId: "<<chargeEffectId;
return;
}
for (const auto &trajectory : m_dtcAes->trajectoryTimeAwares())
{
if (trajectory.originatorId() != chargeEffectId && trajectory.originatorTypeId().underlying() != ProjectDataManagement::StateChangeOriginator::ChargeEffect)
continue;
if (!m_impactCounters.contains(chargeEffect.originatorId()))
m_impactCounters[chargeEffect.originatorId()] = 0;
const int impactCounter = m_impactCounters.value(chargeEffect.originatorId());
const double angle = calculateAngle(trajectory);
const qint64 timeFromLastTrajectoryPointAbsolute = chargeEffect.effectStart() + trajectory.geometry().back().relativeTime();
const int patternCount = mineLayingProperties.value(QStringLiteral("pattern")).toArray().size();
const int patternCounter = patternCounterByImpactCount(ec, patternCount, impactCounter);
const int effectorIndex = effectorIndexByImpactCount(ec, impactCounter);
const Core::GeoPosition mhpPositionGeo = ec.effectors().at(effectorIndex).mhpPosition();
const Core::GeoPosition2D mhpPositionGeo2D(mhpPositionGeo.lon(), mhpPositionGeo.lat());
const std::vector<AreaEffectSimulation::MineData> mineImpacts
= calculateImpacts(mineLayingProperties, mhpPositionGeo2D, angle, timeFromLastTrajectoryPointAbsolute, patternCounter);
AreaEffectSimulation::MineAreaReferenceModifyRequest request;
request.chargeTypeId(mineChargeTypeId);
request.referenceAesItemId(chargeEffect.originatorId());
request.mineData(mineImpacts);
QJsonObject chargeEffectPropsObj = QJsonDocument::fromJson(QByteArray::fromStdString(chargeEffect.properties())).object();
request.effectDurationMsec(chargeEffectPropsObj[QStringLiteral("effectDurationMsec")].toInt(3 * 60 * 60 * 1000));
const int ret = m_ftcAes->mineAreaReferenceModifyRequest(request);
DEBUG_LOG(m_logger)<<"[onChargeEffectChanged] ****** mineAreaReferenceModifyRequest \n\tret id: "<< ret
<< "\n\tchargeTypeId: " << chargeTypeId
<< "\n\tchargeEffectId: " << chargeEffectId << "\n\ttrajectoryId: " << trajectory.id()
<< "\n\tangle: " << angle << "\n\ttimeFromLastTrajectoryPointAbsolute: " << timeFromLastTrajectoryPointAbsolute;
AreaEffectSimulation::ChargeEffectModifyRequest chargeEffectRequest;
chargeEffectRequest.chargeTypeId(chargeEffect.chargeTypeId());
chargeEffectRequest.effectDuration(chargeEffect.effectDuration());
chargeEffectRequest.effectStart(chargeEffect.effectStart());
chargeEffectRequest.gearTypeId(chargeEffect.gearTypeId());
chargeEffectRequest.id(chargeEffect.id());
chargeEffectRequest.igniterTypeId(chargeEffect.igniterTypeId());
chargeEffectRequest.originatorId(chargeEffect.originatorId());
chargeEffectRequest.originatorTypeId(chargeEffect.originatorTypeId());
chargeEffectRequest.position(chargeEffect.position());
chargeEffectPropsObj.insert(QStringLiteral("used"), QJsonValue::fromVariant(true));
chargeEffectRequest.properties(QJsonDocument(chargeEffectPropsObj).toJson().toStdString());
m_ftcAes->chargeEffectModifyRequest(chargeEffectRequest);
++m_impactCounters[chargeEffect.originatorId()];
break;
}
if (m_effectClusterQue.isEmpty())
m_effectClusterTimer->stop();
}
int HandlerMineLayingArtillery::patternCounterByImpactCount(const AreaEffectSimulation::EffectCluster &effectCluster, int patternCount, int impactCounter) const
{
return impactCounter / effectCluster.effectors().size() % patternCount;
}
int HandlerMineLayingArtillery::effectorIndexByImpactCount(const AreaEffectSimulation::EffectCluster &effectCluster, int impactCounter) const
{
return impactCounter % effectCluster.effectors().size();
}
void HandlerMineLayingArtillery::onChargeEffectChanged(const AreaEffectSimulation::ChargeEffect &data, DataModification modType)
{
Q_UNUSED(modType);
if (data.state() != AreaEffectSimulation::AesEffectState::EsActivated)
return;
if (data.originatorTypeId() != ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation)
return;
// since there are some mines missing when adding them too fast, they are queued
m_effectClusterQue.enqueue(data);
if (!m_effectClusterTimer->isActive())
m_effectClusterTimer->start();
}
} // namespace MineLayingArtillery