418 lines
17 KiB
C++
Executable File
418 lines
17 KiB
C++
Executable File
/**
|
|
* Handler for player data
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
* Created by hjl, SLSE, 2017-02-20
|
|
*/
|
|
|
|
#include "handler_vulcano.h"
|
|
#include "expand_polygon.h"
|
|
#include "isInCone.h"
|
|
#include "settings.h"
|
|
|
|
#include <LtsUtilsCore/dds_bitarray.h>
|
|
#include <LtsUtilsCore/qpoint3d.h>
|
|
#include <LtsCoordTrans/lts_coord_trans.h>
|
|
#include <CentralEffectSimulation/handler_participant.h>
|
|
|
|
#include <QPainterPath>
|
|
#include <QPointF>
|
|
#include <QPolygonF>
|
|
#include <QVector>
|
|
#include <QTimer>
|
|
#include <QTransform>
|
|
#include <QDateTime>
|
|
#include <QVariant>
|
|
|
|
|
|
namespace CesExplosions
|
|
{
|
|
|
|
HandlerVulcano::HandlerVulcano(const std::shared_ptr<CentralEffectSimulation::DdsHandler> &ddsHandler,
|
|
QObject *parent)
|
|
|
|
: QObject(parent)
|
|
, m_ddsHandler(ddsHandler)
|
|
, m_logger(CentralEffectSimulation::getLogger("Plugin.Explosions.HandlerVulcano"))
|
|
{
|
|
} // end of ctor()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HandlerVulcano::~HandlerVulcano()
|
|
{
|
|
} // end of dtor()
|
|
//-----------------------------------------------------------------------------
|
|
bool HandlerVulcano::init()
|
|
{
|
|
auto dtcAes = m_ddsHandler->dtcAes();
|
|
auto ftcAes = m_ddsHandler->ftcAes();
|
|
auto dtcPpm = m_ddsHandler->dtcPpm();
|
|
|
|
ftcAes->enableTargetPositionsModify();
|
|
|
|
dtcAes->enableExplosionCluster();
|
|
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionClusterChanged,
|
|
this, &HandlerVulcano::onExplosionClusterChanged);
|
|
dtcAes->enableTrajectoryTimeAware();
|
|
|
|
dtcPpm->enableDynamicParticipantData();
|
|
connect(dtcPpm.get(), &ParticipantManagement::Interface::Dtc::dynamicParticipantDataChanged,
|
|
this, &HandlerVulcano::onDynamicParticipantChanged);
|
|
|
|
float vulcanoViewAngleDeg = CesExplosions::Settings::vulcanoViewAngleDeg();
|
|
float vulcanoAttackAngleDeg = CesExplosions::Settings::vulcanoAttackAngleDeg();
|
|
float vulcanoAlphaDeg = CesExplosions::Settings::vulcanoMaximumDesignationAngleDeg();
|
|
|
|
DEBUG_LOG(m_logger) << "vulcanoViewAngleDeg=" << vulcanoViewAngleDeg
|
|
<< " vulcanoAttackAngleDeg=" << vulcanoAttackAngleDeg
|
|
<< " vulcanoAlphaDeg=" << vulcanoAlphaDeg;
|
|
|
|
|
|
return true;
|
|
} // end of init()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HandlerVulcano::onTargetPositionsModifyReply(const Core::FunctionReplyObject &replyObject)
|
|
{
|
|
if (replyObject.error())
|
|
WARNING_LOG(m_logger) << "Can not send TargetPositionsRequest: " << replyObject();
|
|
else
|
|
DEBUG_LOG(m_logger) << "TargetPositionsRequest for id " << replyObject.id() << " success\n"
|
|
<< " replyObject.str() = " << replyObject.str();
|
|
}
|
|
|
|
// ///////////////////////////////////
|
|
// A E S (AreaEffectSimulation)
|
|
// ///////////////////////////////////
|
|
|
|
void HandlerVulcano::onExplosionClusterChanged(const AreaEffectSimulation::ExplosionCluster &data, DataModification modType)
|
|
{
|
|
// order checks by effort:
|
|
if ((modType == DataModification::eRemoved)
|
|
|| (data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired)
|
|
|| (data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsActivated))
|
|
return;
|
|
|
|
if (!data.effectStart().is_set())
|
|
{
|
|
DEBUG_LOG(m_logger) << "No effectStart";
|
|
return;
|
|
}
|
|
|
|
// Kriterium fuer NASAT/MIDAS
|
|
if (m_ddsHandler->dtcPdm()->gearClassFromChargeTypeId(data.chargeStruct().chargeTypeId()) == ProjectDataManagement::GearClass_def::AirSupport)
|
|
return;
|
|
|
|
if (!m_ddsHandler->isLaserGuidable(data.chargeStruct()))
|
|
return;
|
|
|
|
if (!data.flightCurve().is_set() || !data.laserGuiding().is_set())
|
|
return;
|
|
const AreaEffectSimulation::FlightCurve &fcData = data.flightCurve().get();
|
|
const AreaEffectSimulation::LaserGuiding &lgData = data.laserGuiding().get();
|
|
|
|
DEBUG_LOG(m_logger) << "\n onExplosionClusterChanged id=" << data.id()
|
|
<< " AesEffectState=" << data.effectStateId()
|
|
<< " lcPrf=" << lgData.lcAttack()
|
|
<< " originalEffectDuration=" << data.originalEffectDuration()
|
|
<< " effectStart=" << data.effectStart();
|
|
|
|
DEBUG_LOG(m_logger) << "m_prfToAesId.insert(" << lgData.lcAttack() << ", " << data.id() << ")"
|
|
<< " tapTimepoint=" << lgData.tapTime()
|
|
<< " currentPartitionTime=" << m_ddsHandler->currentPartitionTime()
|
|
<< " Timer interval =" << lgData.tapTime() - m_ddsHandler->currentPartitionTime();
|
|
|
|
int aesId = data.id();
|
|
QTimer *timer = new QTimer(this);
|
|
timer->setSingleShot(true);
|
|
QObject::connect(timer, &QTimer::timeout,
|
|
this, &HandlerVulcano::onFiringTimeTimeout);
|
|
if (m_aesTimers.contains(data.id()))
|
|
{
|
|
m_aesTimers.value(data.id())->stop();
|
|
delete m_aesTimers.value(data.id());
|
|
m_aesTimers.remove(data.id());
|
|
}
|
|
m_aesTimers.insert(data.id(), timer);
|
|
timer->setProperty("aesId", aesId);
|
|
{
|
|
long long effectStart = data.effectStart().is_set() ? data.effectStart().get() : 0;
|
|
long long calculatedFlightDurationMSec = fcData.calculatedFlightDuration();
|
|
long long currentPartitionTime = m_ddsHandler->currentPartitionTime();
|
|
const int timerTimeout = effectStart - calculatedFlightDurationMSec - currentPartitionTime;
|
|
DEBUG_LOG(m_logger) << "Timer started mit Abflugzeit des Vulcano=" << timerTimeout;
|
|
if (timerTimeout >= 0)
|
|
timer->start(timerTimeout);
|
|
}
|
|
m_explosionCluster.insert(data.id(), data);
|
|
}
|
|
|
|
void HandlerVulcano::onFiringTimeTimeout()
|
|
{
|
|
DEBUG_LOG(m_logger) << "++++++++++++++ onFiringTimeTimeout";
|
|
QTimer *t = qobject_cast<QTimer *>(sender());
|
|
int aesId = t->property("aesId").toInt();
|
|
delete m_aesTimers.value(aesId);
|
|
m_aesTimers.remove(aesId);
|
|
|
|
DEBUG_LOG(m_logger) << "onTimerTimeout aesId=" << aesId;
|
|
const auto &myEC = m_explosionCluster.value(aesId);
|
|
if (myEC.id() != aesId)
|
|
return;
|
|
if (!myEC.flightCurve().is_set())
|
|
return;
|
|
const AreaEffectSimulation::FlightCurve &fcData = myEC.flightCurve().get();
|
|
|
|
//auto dtcPpm = m_ddsHandler->dtcPpm();
|
|
//auto participantId = dtcPpm.get()->getParticipantFromPrf(prf);
|
|
//auto dpd = dtcPpm.get()->dynamicParticipantData(participantId);
|
|
//DEBUG_LOG(m_logger) << "onTimerTimeout participantId=" << participantId;
|
|
QTimer *timer = new QTimer(this);
|
|
timer->setSingleShot(true);
|
|
QObject::connect(timer, &QTimer::timeout,
|
|
this, &HandlerVulcano::onTrajectoryTimeAwareTimeout);
|
|
|
|
if (m_aesTimers.contains(aesId))
|
|
{
|
|
m_aesTimers.value(aesId)->stop();
|
|
delete m_aesTimers.value(aesId);
|
|
m_aesTimers.remove(aesId);
|
|
}
|
|
m_aesTimers.insert(aesId, timer);
|
|
timer->setProperty("aesId", aesId);
|
|
auto dtcAes = m_ddsHandler->dtcAes();
|
|
const AreaEffectSimulation::TrajectoryTimeAware &trajectoryTimeAware = firstTrajectoryTimeAwareFromAesId(aesId);
|
|
if (trajectoryTimeAware.geometry().size() < 2)
|
|
{
|
|
DEBUG_LOG(m_logger) << "return";
|
|
return;
|
|
}
|
|
|
|
/* x | Tap (aka TapT)
|
|
// \ |
|
|
// \ |
|
|
// \ |
|
|
// \ |
|
|
// -------x | CloudBase (Tap_strich)
|
|
// \ |
|
|
// \ |
|
|
// ----------x | Impact
|
|
//
|
|
// ----------------| Surface
|
|
*/
|
|
|
|
const std::vector<Core::GeoPositionRelativeTime> &geometryRef = trajectoryTimeAware.geometry();
|
|
int calculatedFlightDurationMSec = fcData.calculatedFlightDuration();
|
|
// TAP' kann auch TAP sein -> Es ist immer der vorletzte Wert. = size() - 2.
|
|
int relTimeTap = geometryRef[geometryRef.size() - 2].relativeTime();
|
|
// Der letzte Wert ist immer der Einschlag = size() - 1.
|
|
int relTimeExplosion = geometryRef[geometryRef.size() - 1].relativeTime();
|
|
// Flugzeit zwischen TAP(') und Einschlag
|
|
int timeDiffTapToExplosion = relTimeExplosion - relTimeTap;
|
|
// Somit ist die absolute Zeit am TAP(') die Startzeit abzüglich der Flugzeit
|
|
int absTimeAtTap = myEC.effectStart().get() - timeDiffTapToExplosion;
|
|
// Und die ist in wieviel Sekunden?
|
|
int relTimeToTap = absTimeAtTap - m_ddsHandler->currentPartitionTime();
|
|
DEBUG_LOG(m_logger) << "timer->start timeout= " << relTimeToTap
|
|
<< " calculatedFlightDurationMSec= " << calculatedFlightDurationMSec;
|
|
if (relTimeToTap >= 0)
|
|
timer->start(relTimeToTap);
|
|
}
|
|
|
|
void HandlerVulcano::onTrajectoryTimeAwareTimeout()
|
|
{
|
|
QTimer *t = qobject_cast<QTimer *>(sender());
|
|
int aesId = t->property("aesId").toInt();
|
|
|
|
delete m_aesTimers.value(aesId);
|
|
m_aesTimers.remove(aesId);
|
|
DEBUG_LOG(m_logger) << "++++++++++++++ onTrajectoryTimeAwareTimeout aesId=" << aesId;
|
|
const auto &myEC = m_explosionCluster.value(aesId);
|
|
if (myEC.id() != aesId)
|
|
return;
|
|
if (!myEC.laserGuiding().is_set())
|
|
return;
|
|
const AreaEffectSimulation::LaserGuiding &lgData = myEC.laserGuiding().get();
|
|
|
|
const int prf = lgData.lcAttack();
|
|
|
|
const auto &dtcPpm = m_ddsHandler->dtcPpm();
|
|
const auto &dtcAes = m_ddsHandler->dtcAes();
|
|
auto participantId = dtcPpm->getParticipantFromPrf(prf);
|
|
auto dpd = dtcPpm->dynamicParticipantData(participantId);
|
|
const auto &designationsVec = dpd.designations();
|
|
DEBUG_LOG(m_logger) << "onTimerTimeout participantId=" << participantId;
|
|
|
|
for (const auto &designation : qAsConst(designationsVec))
|
|
{
|
|
if (designation.code() == prf && designation.outputPower() > 0)
|
|
{
|
|
const AreaEffectSimulation::TrajectoryTimeAware &trajectoryTimeAware = firstTrajectoryTimeAwareFromAesId(aesId);
|
|
const std::vector<Core::GeoPositionRelativeTime> &trajGeometry = trajectoryTimeAware.geometry();
|
|
|
|
DEBUG_LOG(m_logger) << "trajectoryTimeAware.geometry().size()=" << trajGeometry.size();
|
|
DEBUG_LOG(m_logger) << "Designation code=" << designation.code()
|
|
<< " prf=" << prf
|
|
<< " outputPower=" << designation.outputPower()
|
|
<< " designation.spotlocation=" << designation.spotLocation().lon() << "/" << designation.spotLocation().lat();
|
|
|
|
if (trajGeometry.size() < 2)
|
|
{
|
|
DEBUG_LOG(m_logger) << "return";
|
|
return;
|
|
}
|
|
|
|
const Core::GeoPosition topGeo = trajGeometry.at(trajGeometry.size() - 2);
|
|
const Core::GeoPosition bottomGeo = trajGeometry.at(trajGeometry.size() - 1);
|
|
const Core::GeoPosition2D pointToTestGeo = designation.spotLocation();
|
|
|
|
int zone = -1;
|
|
const QPointF &topPt = CentralEffectSimulation::geographic2Utm(topGeo, &zone);
|
|
const QPointF &bottomPt = CentralEffectSimulation::geographic2Utm(bottomGeo, &zone);
|
|
const QPointF &pointToTestPt = CentralEffectSimulation::geographic2Utm(pointToTestGeo, &zone);
|
|
|
|
const QVector3D top(topPt.x(), topPt.y(), topGeo.z());
|
|
DEBUG_LOG(m_logger) << "top=" << top.x() << "/" << top.y() << "/" << top.z() << " zone=" << zone;
|
|
|
|
const QVector3D bottom(bottomPt.x(), bottomPt.y(), bottomGeo.z());
|
|
DEBUG_LOG(m_logger) << "bottom=" << bottom.x() << "/" << bottom.y() << "/" << bottom.z() << " zone=" << zone;
|
|
|
|
const QVector3D pointToTest(pointToTestPt.x(), pointToTestPt.y(), designation.spotLocation().z());
|
|
DEBUG_LOG(m_logger) << "pointToTest=" << pointToTest.x() << "/" << pointToTest.y() << "/" << pointToTest.z() << " zone=" << zone;
|
|
|
|
float vulcanoViewAngleDeg = CesExplosions::Settings::vulcanoViewAngleDeg();
|
|
float vulcanoAttackAngleDeg = CesExplosions::Settings::vulcanoAttackAngleDeg();
|
|
|
|
DEBUG_LOG(m_logger) << "vulcanoViewAngleDeg=" << vulcanoViewAngleDeg
|
|
<< " vulcanoAttackAngleDeg=" << vulcanoAttackAngleDeg;
|
|
|
|
if (! isInCone(top, bottom, vulcanoAttackAngleDeg, pointToTest))
|
|
{
|
|
DEBUG_LOG(m_logger) << "Punkt ist ausserhalb des Attack Angles. ";
|
|
return;
|
|
}
|
|
|
|
const auto observerPos = dtcPpm->participantPosition(participantId).pos();
|
|
QPointF fePt = CentralEffectSimulation::geographic2Utm(trajGeometry.at(0), &zone);
|
|
QVector3D fe(fePt.x(), fePt.y(), 0);
|
|
|
|
QVector3D lp = pointToTest;
|
|
QPointF observerPt = CentralEffectSimulation::geographic2Utm(observerPos, &zone);
|
|
QVector3D observer(observerPt.x(), observerPt.y(), 0);
|
|
|
|
double angle = CesExplosions::Settings::vulcanoMaximumDesignationAngleDeg();
|
|
|
|
DEBUG_LOG(m_logger) << "isInAngle fe=" << fe.x() << "/" << fe.y()
|
|
<< ", lp=" << lp.x() << "/" << lp.y()
|
|
<< ", observer=" << observer.x() << "/" << observer.y()
|
|
<< ", angle=" << angle
|
|
<< ", zone=" << zone;
|
|
if (! isInCone(lp, fe, angle, observer))
|
|
{
|
|
DEBUG_LOG(m_logger) << "Keine neue Vulcano-Pos: Winkel ist zu gross. ";
|
|
return;
|
|
}
|
|
|
|
//designation.spotLocation()
|
|
Core::GeoPosition posVulcano = getTrajectoryPosFromTime(aesId, static_cast<long long>(m_ddsHandler->currentPartitionTime()));
|
|
|
|
|
|
AreaEffectSimulation::TargetPositionsModifyRequest posModify;
|
|
posModify.aesItemId(aesId);
|
|
posModify.positions().push_back(designation.spotLocation());
|
|
DEBUG_LOG(m_logger) << "targetPositionsModifyRequest z=" << posModify.positions().at(0).z()
|
|
<< " designation.z=" << designation.spotLocation().z()
|
|
<< " PosVulcano=" << posVulcano.lon() << "/" << posVulcano.lat();
|
|
m_ddsHandler->ftcAes()->targetPositionsModifyRequest(posModify, this);
|
|
return;
|
|
}
|
|
} // for
|
|
}
|
|
|
|
AreaEffectSimulation::TrajectoryTimeAware HandlerVulcano::firstTrajectoryTimeAwareFromAesId(int aesId)
|
|
{
|
|
// be aware of: [E01562-259] use originatorId, originatorTypeId (instead of aesItemId, aesTypeId)
|
|
const auto &dtcAes = m_ddsHandler->dtcAes();
|
|
const auto &trajectories = dtcAes->trajectoriesTimeAwareFromOriginatorIdAndType(aesId,
|
|
ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation);
|
|
if (trajectories.empty())
|
|
{
|
|
DEBUG_LOG(m_logger) << "no trajectories found for originatorId/aesItemId " << aesId;
|
|
return {};
|
|
}
|
|
Q_ASSERT_X(trajectories.size() == 1, __FUNCTION__, "For Vulcano there should be just one trajectory");
|
|
|
|
const auto &trajectoryTimeAwareValue = trajectories[0];
|
|
return trajectoryTimeAwareValue;
|
|
}
|
|
|
|
Core::GeoPosition HandlerVulcano::getTrajectoryPosFromTime(int aesId, long long timeParam)
|
|
{
|
|
// be aware of: [E01562-259] use originatorId, originatorTypeId (instead of aesItemId, aesTypeId)
|
|
const auto &trajectoryTimeAwareValue = firstTrajectoryTimeAwareFromAesId(aesId);
|
|
|
|
const qint64 relTime = timeParam - trajectoryTimeAwareValue.trajectoryStartTime();
|
|
|
|
//trajectoryTimeAwareValue = trajectoryTimeAware(aesId);
|
|
DEBUG_LOG(m_logger) << " getTrajectoryPosFromTime aesId=" << aesId
|
|
<< " trajectoryTimeAwareValue.id=" << trajectoryTimeAwareValue.id()
|
|
<< " time=" << timeParam
|
|
<< " trajectoryStartTime=" << trajectoryTimeAwareValue.trajectoryStartTime()
|
|
<< " relTime=" << relTime;
|
|
if (trajectoryTimeAwareValue.geometry().empty())
|
|
{
|
|
DEBUG_LOG(m_logger) << "getTrajectoryPosFromTime no pos available";
|
|
return {};
|
|
}
|
|
|
|
Core::GeoPosition result;
|
|
int i = 0;
|
|
for (const auto &point : qAsConst(trajectoryTimeAwareValue.geometry()))
|
|
{
|
|
DEBUG_LOG(m_logger) << "time=" << point.relativeTime();
|
|
if ( (point.relativeTime() > relTime) && (i > 0))
|
|
{
|
|
// Beachte: Geschwindigkeit wird _NICHT_ beruecksichtigt
|
|
const Core::GeoPositionRelativeTime &geoPosRelTime = trajectoryTimeAwareValue.geometry()[i-1];
|
|
// avoid expensive operations like following
|
|
// result.lat() = trajectoryTimeAwareValue.geometry()[i -1].lat();
|
|
// result.lon() = trajectoryTimeAwareValue.geometry()[i -1].lon();
|
|
// result.z() = trajectoryTimeAwareValue.geometry()[i -1].z();
|
|
// and take benefits from downcasting
|
|
result = geoPosRelTime; // implicit downcast :-)
|
|
DEBUG_LOG(m_logger) << "found i=" << i
|
|
<< " result=" << result.lat() << "/" << result.lon() << "/" << result.z();
|
|
return result;
|
|
}
|
|
++i;
|
|
} // for
|
|
result = trajectoryTimeAwareValue.geometry().back();
|
|
DEBUG_LOG(m_logger) << " return last point result="
|
|
<< " result=" << result.lat() << "/" << result.lon() << "/" << result.z();
|
|
return result;
|
|
|
|
}
|
|
|
|
void HandlerVulcano::onDynamicParticipantChanged(const ParticipantManagement::DynamicParticipantData &data, DataModification modType)
|
|
{
|
|
if (modType == DataModification::eRemoved)
|
|
return;
|
|
|
|
DEBUG_LOG(m_logger) << "onDynamicParticipantChanged"
|
|
<< data.participantId()
|
|
<< data.designations().size();
|
|
if (data.designations().size() > 0)
|
|
{
|
|
for (const ParticipantManagement::LaserDesignation &designation : qAsConst(data.designations()))
|
|
{
|
|
DEBUG_LOG(m_logger) << "gearId=" << designation.gearId()
|
|
<< " code=" << designation.code()
|
|
<< " z=" << designation.spotLocation().z()
|
|
<< " outputPower=" << designation.outputPower();
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace CesExplosions
|