283 lines
12 KiB
C++
Executable File
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
|