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,997 @@
/**
* Handler for player data
* $Id$
* $HeadURL$
* $Author$
* Created by hjl, SLSE, 2017-02-20
*/
#include "handler_aes_data.h"
#include <CentralEffectSimulation/handler_participant.h>
#include <CentralEffectSimulation/handler_building_data.h>
#include <QPainterPath>
#include <QPointF>
#include <QPolygonF>
#include <QTimer>
#include <QTransform>
#include <LtsUtilsCore/dds_bitarray.h>
#include <LtsUtilsCore/qpoint3d.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#define TIMEDIFFVALID 3000
namespace CesBuildings
{
HandlerAesData::HandlerAesData(const std::shared_ptr<CentralEffectSimulation::DdsHandler> &ddsHandler,
const std::shared_ptr<CentralEffectSimulation::HandlerBuildingData> &buildingDataHandler,
QObject *parent)
: QObject(parent)
, m_ddsHandler(ddsHandler)
, m_handlerBuildingData(buildingDataHandler)
, m_logger(CentralEffectSimulation::getLogger("Plugin.Buildings.HandlerAesData"))
{
} // end of ctor()
//-----------------------------------------------------------------------------
HandlerAesData::~HandlerAesData()
{
} // end of dtor()
//-----------------------------------------------------------------------------
bool HandlerAesData::init()
{
auto dtcAes = m_ddsHandler->dtcAes();
dtcAes->enableChargeEffect();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::chargeEffectChanged,
this, &HandlerAesData::onChargeEffectChanged);
dtcAes->enableExplosionSingle();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionSingleChanged,
this, &HandlerAesData::onExplosionSingleChanged);
dtcAes->enableAirSupportImpact();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::airSupportImpactChanged,
this, &HandlerAesData::onAirSupportImpactChanged);
dtcAes->enableTriggeredImpacts();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::triggeredImpactsChanged,
this, &HandlerAesData::onTriggeredImpactsChanged);
dtcAes->waitForHistoricalDataAesItemView(1000);
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged,
this, &HandlerAesData::onAesItemViewChanged);
dtcAes->enableEffects();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::effectsChanged,
this, &HandlerAesData::onEffectsChanged, Qt::UniqueConnection);
dtcAes->enableBoobyTrap();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::boobyTrapChanged,
this, &HandlerAesData::onBoobyTrapChanged, Qt::UniqueConnection);
// Get 'static' data for all AesItemView
{
auto cb = [this](const AreaEffectSimulation::AesItemView & aiv)
{
onAesItemViewChanged(aiv, DataModification::eAdded);
};
if (!dtcAes->aesItemViews(cb))
return false;
}
return true;
} // end of init()
void HandlerAesData::onBoobyTrapChanged(const AreaEffectSimulation::BoobyTrap &data, DataModification modType)
{
DEBUG_LOG(m_logger) << "onBoobyTrapChanged"
<< " id=" << data.id()
<< " impactPosition=" << data.impactPosition()
<< " impactRadius=" << data.impactRadius()
<< " effectStateId=" << data.effectStateId();
if (data.isDeleted()
|| data.effectStateId() == AreaEffectSimulation::AesEffectState::EsRemoved
|| modType == DataModification::eRemoved)
return;
if (!data.triggered())
return;
INFO_LOG(m_logger) << "HandlerParticipantBoobyTrap::onBoobyTrapChanged"
<< " data.id=" << data.id()
<< " effectStateId=" << data.effectStateId();
if (!data.effectStart().is_set())
return;
if ((m_ddsHandler->currentPartitionTime() - (data.effectStart().get() + data.effectDuration())) > 2000)
{
INFO_LOG(m_logger) << "onBoobyTrap outdated";
return;
}
AreaEffectSimulation::AesItemView itemView;
AreaEffectSimulation::AesType aesType = AreaEffectSimulation::AesType_def::AtBoobyTrap;
m_ddsHandler->dtcAes()->aesItemViewFromEntityId(data.id(), aesType, itemView);
checkBuildingsHit(itemView);
}
//-----------------------------------------------------------------------------
// ///////////////////////////////////
// A E S (AreaEffectSimulation)
// ///////////////////////////////////
void HandlerAesData::onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType)
{
DEBUG_LOG(m_logger) << "onAesItemViewChanged"
<< " data.aesTypeId()=" << data.aesTypeId();
if (modType != DataModification::eAdded &&
modType != DataModification::eModified)
return;
DEBUG_LOG(m_logger) << "Got AesItemView: " << data.effectStateId();
if (data.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired)
{
INFO_LOG(m_logger) << "AesEffectState_def::EsExpired"
<< " currentPartitionTime=" << m_ddsHandler->currentPartitionTime()
<< " effectStart=" << data.effectStart()
<< " diff = " << m_ddsHandler->currentPartitionTime() - data.effectStart() ;
if (m_ddsHandler->currentPartitionTime() == 0)
return;
if ((data.aesTypeId() == AreaEffectSimulation::AesType_def::AtOffRouteMineSecondary)
|| (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtBoobyTrapSecondary))
{
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtOffRouteMineSecondary)
{
DEBUG_LOG(m_logger) << "AtOffRouteMineSecondary id=" << data.id() / 1000;
auto offRouteMine = m_ddsHandler->dtcAes()->offRouteMine((int)(data.id() / 1000));
if (!offRouteMine.effectStart().is_set())
{
DEBUG_LOG(m_logger) << "!offRouteMine.effectStart().is_set()";
return;
}
DEBUG_LOG(m_logger) << " currentPartitionTime=" << m_ddsHandler->currentPartitionTime()
<< " effectStart=" << offRouteMine.effectStart()
<< " effectDuration=" << offRouteMine.effectDuration()
<< " diff = " << m_ddsHandler->currentPartitionTime() - (offRouteMine.effectStart().get() + offRouteMine.effectDuration()) ;
if ((m_ddsHandler->currentPartitionTime() - (offRouteMine.effectStart().get() + offRouteMine.effectDuration())) < 2000)
{
DEBUG_LOG(m_logger) << "AtBoobyTrapSecondary Mine wird ausgeloeset.";
checkBuildingsHit(data);
}
else
{
DEBUG_LOG(m_logger) << "outdated return";
return;
}
}
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtBoobyTrapSecondary)
{
DEBUG_LOG(m_logger) << "AtBoobyTrapSecondary id=" << data.id() / 1000;
//auto offRouteMine = m_ddsHandler->dtcAes()->offRouteMine((int) (data.id() / 1000));
auto boobyTrap = m_ddsHandler->dtcAes()->boobyTrap((int)(data.id() / 1000));
if (!boobyTrap.effectStart().is_set())
{
DEBUG_LOG(m_logger) << "!offRouteMine.effectStart().is_set()";
return;
}
DEBUG_LOG(m_logger) << " currentPartitionTime=" << m_ddsHandler->currentPartitionTime()
<< " effectStart=" << boobyTrap.effectStart()
<< " effectDuration=" << boobyTrap.effectDuration()
<< " diff = " << m_ddsHandler->currentPartitionTime() - (boobyTrap.effectStart().get() + boobyTrap.effectDuration()) ;
if ((m_ddsHandler->currentPartitionTime() - (boobyTrap.effectStart().get() + boobyTrap.effectDuration())) < 2000)
{
DEBUG_LOG(m_logger) << "AtBoobyTrapSecondary Mine wird ausgeloeset.";
checkBuildingsHit(data);
}
else
{
DEBUG_LOG(m_logger) << "outdated return";
return;
}
}
DEBUG_LOG(m_logger) << "removeStates data";
removeStates(data);
DEBUG_LOG(m_logger) << "m_aesItemViewsUtm remove id" << data.aesItemId();
m_aesItemViewsUtm.remove(data.aesItemId());
}
}
}
void HandlerAesData::removeStates(const AreaEffectSimulation::AesItemView &data)
{
if (data.aesTypeId() != AreaEffectSimulation::AesType_def::AtExplosionArea)
return;
DEBUG_LOG(m_logger) << "Remove aesItemId=" << data.aesItemId();
// remove State for each building
for (CentralEffectSimulation::BitfieldStates &bfs : m_bitfieldStatesBld)
bfs.remove(data.aesItemId());
} // end removeStates
void HandlerAesData::calculateBuildingExplosion(const AreaEffectSimulation::ChargeStruct &chargeStruct,
const Core::GeoPosition2D& impactPosition,
const int id)
{
const double impactRadius = m_ddsHandler->chargeTypeRadius(chargeStruct);
DEBUG_LOG(m_logger) << "Handle ExplosionSingle (EsActivated)"
<< ", m_handlerBuildingData.getFootprints().size =" << m_handlerBuildingData->getFootprints().size();
QHash<int, CentralEffectSimulation::HandlerBuildingData::Footprint> footprints = m_handlerBuildingData->getFootprints();
// Bounding-Box-Check
// QMap<int, Footprint> m_handlerBuildingData.getFootprints();
for (const CentralEffectSimulation::HandlerBuildingData::Footprint &footprint : qAsConst(footprints))
{
const Core::GeoPosition2D impactPosition2D(impactPosition.lon(), impactPosition.lat());
if (inFootprint(impactPosition, footprint, impactRadius))
{
DEBUG_LOG(m_logger) << "ftc sendCESHitOnBuilding mseId = " << footprint.mseId
<< " chargeTypeId = " << chargeStruct.chargeTypeId()
<< " id = " << id
<< " currentPartitionTime = " << m_ddsHandler->currentPartitionTime()
<< " impactPosition2D.lon = " << impactPosition2D.lon()
<< " impactPosition2D.lat = " << impactPosition2D.lat();
Buildings::SendCesHitOnBuildingRequest requestBld(
footprint.mseId,
chargeStruct.chargeTypeId(),
m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(chargeStruct.chargeTypeId()).destructiveFactor(),
m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(chargeStruct.chargeTypeId()).penetrationFactor(),
id,
m_ddsHandler->currentPartitionTime(),
Core::GeoPosition(impactPosition2D.lon(), impactPosition2D.lat(), 0.0),
QStringLiteral("properties").toStdString());
m_ddsHandler->ftcBld()->sendCesHitOnBuildingRequest(requestBld, this);
}
else
DEBUG_LOG(m_logger) << "Not sendCESHitOnBuilding mseId = " << footprint.mseId;
DEBUG_LOG(m_logger) << "boundingBoxImpact.contains(footprint)";
} // for
}
void HandlerAesData::startTimer(int id, int msec) {
auto *timer = new QTimer(this);
timer->setProperty("chargeEffectId", id);
// Timeout-Slot mit echtem SLOT
connect(timer, &QTimer::timeout, this, &HandlerAesData::onStartTimeReached);
timer->start(msec);
DEBUG_LOG(m_logger) << "Timer " << id << " gestartet fuer " << msec << " ms.";
}
void HandlerAesData::onStartTimeReached()
{
if (!sender())
return;
bool bOk;
int chargeEffectId = sender()->property("chargeEffectId").toInt(&bOk);
if (!bOk)
return;
DEBUG_LOG(m_logger) << "Timer " << chargeEffectId << " ausgeloest!";
// Timer stoppen und loeschen
AreaEffectSimulation::ChargeEffect data = m_chargeEffects.value(chargeEffectId);
AreaEffectSimulation::ChargeStruct chargeStruct(data.chargeTypeId(),data.igniterTypeId(), data.igniterFunctionId());
calculateBuildingExplosion(chargeStruct, data.position(), data.id());
sender()->deleteLater();
m_chargeEffects.remove(chargeEffectId);
}
void HandlerAesData::onChargeEffectChanged(const AreaEffectSimulation::ChargeEffect &data, DataModification modType)
{
DEBUG_LOG(m_logger) << "HandlerExplosions::onChargeEffectChanged";
if (modType == DataModification::eRemoved)
return;
const long long effectStart = data.effectStart();
DEBUG_LOG(m_logger) << "HandlerExplosions::onChargeEffectChanged"
<< ", effectStart=" << effectStart
<< ", currentPartitionTime=" << m_ddsHandler->currentPartitionTime();
// Startzeit in der Vergangenheit: aktuelle Zeit - 3 Sekunden
if ( effectStart < m_ddsHandler->currentPartitionTime() - TIMEDIFFVALID )
{
DEBUG_LOG(m_logger) << "Startzeit in der Vergangenheit: aktuelle Zeit - 3 Sekunden";
return;
}
// Startzeit entspricht der aktuellen Zeit
if ( effectStart > m_ddsHandler->currentPartitionTime() - TIMEDIFFVALID
&& effectStart < m_ddsHandler->currentPartitionTime() + data.effectDuration() )
{
DEBUG_LOG(m_logger) << "Startzeit entspricht der aktuellen Zeit";
AreaEffectSimulation::ChargeStruct chargeStruct;
chargeStruct.chargeTypeId(data.chargeTypeId());
chargeStruct.igniterTypeId(data.igniterTypeId());
calculateBuildingExplosion(chargeStruct, data.position(), data.id());
}
// Startzeit in der Zukunft:
else if (effectStart > m_ddsHandler->currentPartitionTime() + data.effectDuration())
{
// Start mit Timer
DEBUG_LOG(m_logger) << "Startzeit in der Zukunft"
<< ", id :" << data.id()
<< ", duration=" << effectStart - m_ddsHandler->currentPartitionTime();
m_chargeEffects.insert(data.id(), data);
startTimer(data.id(), effectStart - m_ddsHandler->currentPartitionTime());
}
DEBUG_LOG(m_logger) << "END onChargeEffectChanged";
}
void HandlerAesData::onExplosionSingleChanged(const AreaEffectSimulation::ExplosionSingle &data, DataModification modType)
{
bool handleExplosion = false;
if (modType != DataModification::eAdded &&
modType != DataModification::eModified)
{
m_explosionSingleState.remove(data.id());
return;
}
if (data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsRemoved)
{
DEBUG_LOG(m_logger) << "Remove SingleExplosion";
m_explosionSingleState.remove(data.id());
return;
}
// Only handle transitions from prepared or fired to activated
if ((m_explosionSingleState.value(data.id()) == AreaEffectSimulation::AesEffectState_def::EsPrepared ||
m_explosionSingleState.value(data.id()) == AreaEffectSimulation::AesEffectState_def::EsFired) &&
data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsActivated)
{
DEBUG_LOG(m_logger) << "Handle SingleExplosion";
handleExplosion = true;
}
m_explosionSingleState.insert(data.id(), data.effectStateId());
DEBUG_LOG(m_logger) << "Got ExplosionSingle";
if (handleExplosion)
{
calculateBuildingExplosion(data.chargeStruct(), data.impactPosition(), data.id());
}
} // onExplosionSingleChanged
void HandlerAesData::onAirSupportImpactChanged(const AreaEffectSimulation::AirSupportImpact &data, DataModification modType)
{
DEBUG_LOG(m_logger) << "Got onAirSupportImpactChanged()";
AreaEffectSimulation::ExplosionSingle esData;
esData.id(data.id());
esData.chargeStruct().chargeTypeId(data.chargeStruct().chargeTypeId());
esData.impactPosition(data.impactPosition());
esData.effectStateId(data.effectStateId());
onExplosionSingleChanged(esData, modType);
}
void HandlerAesData::onTriggeredImpactsChanged(const AreaEffectSimulation::TriggeredImpacts &data, DataModification modType)
{
if (modType != DataModification::eAdded &&
modType != DataModification::eModified)
return;
DEBUG_LOG(m_logger) << "Got TriggeredImpacts";
preCalculateImpacts(data);
}
void HandlerAesData::onEffectsChanged(const AreaEffectSimulation::Effects &effects, DataModification modType)
{
TRACE_LOG(m_logger) << " Got Effects " << effects;
if (modType == DataModification::eRemoved)
return;
if (effects.aesTypeId().underlying() != AreaEffectSimulation::AesType_def::AtExplosionClusterSingleExplosions)
return;
DEBUG_LOG(m_logger) << " Got typechecked effects " << effects;
// calculate for every building of our exercise:
const QHash<int, CentralEffectSimulation::HandlerBuildingData::Footprint> &footprints = m_handlerBuildingData->getFootprints();
for (const CentralEffectSimulation::HandlerBuildingData::Footprint &footprint : qAsConst(footprints))
{
calculateImpacts(effects, footprint);
}
}
// ///////////////////////////////////
// I S M (InfrastructureManagement)
// ///////////////////////////////////
// ///////////////////////////////////
// Calculations:
// ///////////////////////////////////
bool HandlerAesData::inBoundingBox(const Core::GeoPosition2D& impactPosition,
const CentralEffectSimulation::HandlerBuildingData::Footprint &footprint,
const double impactRadius) const
{
const QPointF impactPos(CentralEffectSimulation::geographic2Utm(impactPosition, footprint.zone));
const QPolygonF polyBldgRadius({QPointF(footprint.boundingBoxPolyUtm.bottomLeft().rx() - impactRadius,
footprint.boundingBoxPolyUtm.bottomLeft().ry() + impactRadius),
QPointF(footprint.boundingBoxPolyUtm.topRight().rx() + impactRadius,
footprint.boundingBoxPolyUtm.bottomLeft().ry() + impactRadius),
QPointF(footprint.boundingBoxPolyUtm.topRight().rx() + impactRadius,
footprint.boundingBoxPolyUtm.topRight().ry() - impactRadius),
QPointF(footprint.boundingBoxPolyUtm.bottomLeft().rx() - impactRadius,
footprint.boundingBoxPolyUtm.topRight().ry() - impactRadius)});
return (polyBldgRadius.containsPoint(impactPos, Qt::OddEvenFill));
}
bool HandlerAesData::inFootprint(const Core::GeoPosition2D &geoPosition,
const CentralEffectSimulation::HandlerBuildingData::Footprint &footPrint,
const double radius) const
{
if ( !inBoundingBox( geoPosition, footPrint, radius))
return false;
const QPointF impact(CentralEffectSimulation::geographic2Utm(geoPosition, footPrint.zone));
// NT: Folgendes raubt zu viele Zeilen beim tail. Daher nehme ich es raus.
// if (m_logger.isDebugEnabled())
// {
// const QPointF utmBuilding(CentralEffectSimulation::geographic2Utm(footPrint.geoPosition, footPrint.zone));
// DEBUG_LOG(m_logger) << "Impact (x/y)=" << CentralEffectSimulation::debug(impact - utmBuilding);
// for (int i = 0; i < footPrint.polyUtm.length(); i++)
// DEBUG_LOG(m_logger) << " poly (x/y)=" << CentralEffectSimulation::debug(footPrint.polyUtm[i] - utmBuilding);
// }
// Check, if hit direct on building
if (footPrint.polyUtm.containsPoint(impact, Qt::OddEvenFill))
return true;
// Check, if hit in munition-radius
QPainterPath ppFp;
ppFp.addPolygon(footPrint.polyUtm);
QPainterPath ppCircle;
ppCircle.addEllipse(impact, radius, radius);
return ppFp.intersects(ppCircle);
}
void HandlerAesData::preCalculateImpacts(const AreaEffectSimulation::TriggeredImpacts &data)
{
if (data.id() <= 0)
return;
DEBUG_LOG(m_logger) << "preCalculateImpacts";
// Find AES ItemView and set Bounding-Box of Aes-Field
const int aesItemId = data.aesItemId();
// Search corresponding itemView. If not available no impact to participant.
// Use dtcAes since sender (AES) publishes TriggeredImpacts and AesItemView
//
AreaEffectSimulation::AesItemView aesItemView;
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> dtcAes = m_ddsHandler->dtcAes();
if (!dtcAes->aesItemViewFromEntityId(aesItemId, data.aesTypeId(), aesItemView))
{
static long msgCnt = 0;
WARNING_LOG(m_logger) << "TriggeredExplosions.aesItemId=" << data.aesItemId() << " No aesItemView available"
<< " (cnt=" << ++msgCnt << ")";
return;
}
// Maybe ExlosionClusters Trajectory is hitting a building?
// Vulnerability should be calculated
const QHash<int, CentralEffectSimulation::HandlerBuildingData::Footprint> &footprints = m_handlerBuildingData->getFootprints();
for (const CentralEffectSimulation::HandlerBuildingData::Footprint &footprint : qAsConst(footprints))
{
calculateImpacts(data, aesItemView, footprint);
} // for
}
void HandlerAesData::onSendCesHitOnBuildingReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
WARNING_LOG(m_logger) << "Can not send hit on building to building service: " << replyObject()
<< ", m_handlerBuildingData.getFootprints().size()" << m_handlerBuildingData->getFootprints().size();
else
DEBUG_LOG(m_logger) << "onSendCesHitOnBuildingReply for id " << replyObject.id() << " success\n"
<< " replyObject.str() = " << replyObject.str();
}
void HandlerAesData::onSendCesHit3DonBuildingReply(const Core::FunctionReplyObject &replyObject)
{
DEBUG_LOG(m_logger) << "m_handlerBuildingData.getFootprints() size " << m_handlerBuildingData->getFootprints().size();
if (replyObject.error())
WARNING_LOG(m_logger) << "Can not send hit3D on building to building service: " << replyObject()
<< ", m_handlerBuildingData.getFootprints().size()" << m_handlerBuildingData->getFootprints().size();
else
DEBUG_LOG(m_logger) << "onSendCesHit3DonBuildingReply for id " << replyObject.id() << " success\n"
<< " replyObject.str() = " << replyObject.str();
}
void HandlerAesData::onSendCesHitInformationReply(const Core::FunctionReplyObject &reply)
{
if (reply.error())
WARNING_LOG(m_logger) << "onSendCesHitInformationReply for id " << reply.id() << " failed: " << reply.str();
else
DEBUG_LOG(m_logger) << "onSendCesHitInformationReply for id " << reply.id() << " successful";
}
void HandlerAesData::onSetBuildingStatesReply(const Core::FunctionReplyObject &reply)
{
if (reply.error())
WARNING_LOG(m_logger) << "onSetBuildingStatesReply for id " << reply.id() << " failed: " << reply.str();
else
DEBUG_LOG(m_logger) << "onSetBuildingStatesReply for id " << reply.id() << " successful";
}
// calculate impacts for one building
void HandlerAesData::calculateImpacts(const AreaEffectSimulation::TriggeredImpacts &data, const AreaEffectSimulation::AesItemView &aesItemView,
const CentralEffectSimulation::HandlerBuildingData::Footprint &footprint)
{
const int aesItemId = data.aesItemId();
bool bIsExplosionCluster = false;
ProjectDataManagement::IgniterFunctionBase ignFuncBase = ProjectDataManagement::IgniterFunctionBase::IfbImpact;
// check for correct AES type artillery only
static QVector<AreaEffectSimulation::AesType_def::type> s_relevantTypes =
{
AreaEffectSimulation::AesType_def::AtExplosionArea,
AreaEffectSimulation::AesType_def::AtExplosionSingle,
AreaEffectSimulation::AesType_def::AtExplosionAreaSingleExplosions,
};
if (data.aesTypeId().underlying() == AreaEffectSimulation::AesType_def::AtExplosionClusterSingleExplosions)
{
bIsExplosionCluster = true;
ignFuncBase = igniterFunctionBaseExplosionCluster(aesItemId); // assume that fuseType does not differ within a single ExplosionArea
DEBUG_LOG(m_logger) << "Calculate, bIsExplosionCluster = " << bIsExplosionCluster
<< ", ignFuncBase = " << ignFuncBase;
}
else if (s_relevantTypes.contains(data.aesTypeId().underlying()))
DEBUG_LOG(m_logger) << "Calculate";
else
{
DEBUG_LOG(m_logger) << "No Calculation";
return;
}
if (aesItemId != aesItemView.aesItemId())
{
WARNING_LOG(m_logger) << "mismatch: "
<< ", TriggeredExplosions.aesItemId=" << data.aesItemId()
<< ", AesItemView.aesItemId=" << aesItemView.aesItemId();
return;
}
DEBUG_LOG(m_logger) << "calculateImpacts mseId=" << footprint.mseId << " aesItemId " << aesItemId
<< ", bIsExplosionCluster = " << bIsExplosionCluster;
// get position(s) of impact(s):
CentralEffectSimulation::BitfieldStates &bitfieldStates = m_bitfieldStatesBld[footprint.mseId];
QSet<int> &bitfieldState = bitfieldStates[aesItemId];
for (int i : qAsConst(bitfieldState))
DEBUG_LOG(m_logger) << "bitfieldState for " << aesItemId << ": " << i;
for (int32_t bitFieldIdx = 0; bitFieldIdx < data.impactCount(); ++bitFieldIdx)
{
// no break, because every bit can be a hit
if (LtsUtils::BitArray::testBit(data.bitfield(), bitFieldIdx))
continue;
// get position of impact to corresponding bit
if (aesItemView.form().size() <= size_t(bitFieldIdx))
continue;
const Core::GeoPosition &impactPosition = aesItemView.form()[bitFieldIdx];
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " impactPosition: lat/lon" << impactPosition.lat() << "/" << impactPosition.lon() << " z=" << impactPosition.z()
<< " UTM=" << CentralEffectSimulation::debug(CentralEffectSimulation::geographic2Utm(impactPosition));
// check, if impact-pos + munition-radius touch the building
// get radius of munition
const double impactRadius = m_ddsHandler->chargeTypeRadius(aesItemView.chargeStruct());
if (inFootprint(impactPosition, footprint, impactRadius))
{
DEBUG_LOG(m_logger) << "In Footprint of mseId = " << footprint.mseId;
if (!bitfieldState.contains(bitFieldIdx))
{
DEBUG_LOG(m_logger) << "m_bitfieldStates.insert(aesItemId=" << aesItemId << ", bitfieldState)" << " bitFieldIdx=" << bitFieldIdx;
bitfieldState.insert(bitFieldIdx);
DEBUG_LOG(m_logger) << "sendCesHit mseId = " << footprint.mseId
<< ", aesItemId=" << aesItemId
<< ", bitFieldIdx= " << bitFieldIdx
<< ", ignFuncBase = " << ignFuncBase;
if (! qFuzzyIsNull(impactPosition.z()))
{
// since HandlerBuildingModel::handleDetonationLocationRequest has already
// calculated to fuseType, the impactPosition is the detonationLocation:
Core::GeoPosition detonationLocation = impactPosition;
//
double detonationDistance = 0;
switch (ignFuncBase.underlying())
{
case ProjectDataManagement::IgniterFunctionBase::IfbProximity:
detonationDistance = -5;
break;
case ProjectDataManagement::IgniterFunctionBase::IfbImpact:
detonationDistance = 0;
break;
case ProjectDataManagement::IgniterFunctionBase::IfbDelay:
detonationDistance = 5;
break;
default:
// empty
break;
} // switch
Buildings::SendCesHit3DonBuildingRequest requestBld(
footprint.mseId,
aesItemView.chargeStruct().chargeTypeId(),
m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(aesItemView.chargeStruct().chargeTypeId()).destructiveFactor(),
m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(aesItemView.chargeStruct().chargeTypeId()).penetrationFactor(),
aesItemId,
m_ddsHandler->currentPartitionTime(),
impactPosition,
detonationLocation,
detonationDistance,
QStringLiteral("properties").toStdString());
DEBUG_LOG(m_logger) << "sendCesHit3DonBuildingRequest for aesItemId=" << aesItemId
<< " bitFieldIdx= " << bitFieldIdx
<< ", requestBld= " << requestBld;
m_ddsHandler->ftcBld()->sendCesHit3DonBuildingRequest(requestBld, this);
}
else
{
// z-Value is 0 -> old / ! AtExplosionClusterSingleExplosions
Buildings::SendCesHitOnBuildingRequest requestBld(
footprint.mseId,
aesItemView.chargeStruct().chargeTypeId(),
m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(aesItemView.chargeStruct().chargeTypeId()).destructiveFactor(),
m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(aesItemView.chargeStruct().chargeTypeId()).penetrationFactor(),
aesItemId,
m_ddsHandler->currentPartitionTime(),
Core::GeoPosition(impactPosition.lon(), impactPosition.lat(), 0.0),
QStringLiteral("properties").toStdString());
DEBUG_LOG(m_logger) << "sendCesHitOnBuildingRequest for aesItemId=" << aesItemId
<< " bitFieldIdx= " << bitFieldIdx
<< ", impactPosition= " << impactPosition;
m_ddsHandler->ftcBld()->sendCesHitOnBuildingRequest(requestBld, this);
} // if (impactPosition.z() > 0)
}
else
DEBUG_LOG(m_logger) << "NOT in Footprint of mseId = " << footprint.mseId;
// if (inFootprint(impactPosition
} // foreach Impact
} // for (int32_t bitFieldId
}
void HandlerAesData::calculateImpacts(const AreaEffectSimulation::Effects &effects, const CentralEffectSimulation::HandlerBuildingData::Footprint &footprint)
{
if (effects.aesTypeId().underlying() != AreaEffectSimulation::AesType_def::AtExplosionClusterSingleExplosions)
return;
const int aesItemId = effects.aesItemId();
DEBUG_LOG(m_logger) << "calculateImpacts mseId=" << footprint.mseId << " data.aesItemId() " << aesItemId;
const AreaEffectSimulation::ExplosionCluster &ec = m_ddsHandler->dtcAes()->explosionCluster(effects.aesItemId());
if (ec.id() != aesItemId)
return;
const ProjectDataManagement::IgniterFunctionBase ignFuncBase = igniterFunctionBaseExplosionCluster(aesItemId);
const double impactRadius = m_ddsHandler->chargeTypeRadius(ec.chargeStruct());
CentralEffectSimulation::BitfieldStates &bitfieldStates = m_bitfieldStatesBld[footprint.mseId];
QSet<int> &bitfieldState = bitfieldStates[aesItemId];
const std::vector<AreaEffectSimulation::SingleEffect> &singleEffectsRef = effects.singleEffects();
int indexBitFieldIdx = -1;
for (const AreaEffectSimulation::SingleEffect &singleEffect : qAsConst(singleEffectsRef))
{
++indexBitFieldIdx;
// just SesTriggered are relevant:
if (singleEffect.singleEffectState() != AreaEffectSimulation::AesSingleEffectState_def::SesTriggered)
continue;
// already processed?
if (bitfieldState.contains(indexBitFieldIdx))
continue;
// is building in range?
const Core::GeoPosition &impactPositionGeo = singleEffect.position();
if (!inFootprint(impactPositionGeo, footprint, impactRadius))
continue;
// Hier könnte man zunächst zwei-dimensional prüfen, ob ein anderes Bldg eine Verdeckung erzeugt zw.
// a) Explosionsort (impactPosition) und
// b) Grundriss (footprint) des Kandidaten
// Diese Verdeckung würde die Wirkung auf den Kandidaten reduzieren.
// 2DpoylgonCentroid https://stackoverflow.com/questions/2792443/finding-the-centroid-of-a-polygon
const LtsCoordTrans::LtsCoordinate impactPos(impactPositionGeo);
const QVector<int> &buildingIdsInBetween = findBuildingsBetween(impactPos, footprint);
QString mseIdAsStr;
for (auto mseId : buildingIdsInBetween)
mseIdAsStr += QString(QStringLiteral("_coveredByMseId_%1;")).arg(mseId);
DEBUG_LOG(m_logger) << "findBuildingsBetween returns mseIdAsStr =" << mseIdAsStr;
const ProjectDataManagement::BuildingChargeEffect &bldgChargeEffect = m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(ec.chargeStruct().chargeTypeId());
if (bldgChargeEffect.id() <= 0)
ERROR_LOG(m_logger) << "getBuildingChargeEffectByChargeTypeId failed, chargeTypeId not found: =" << ec.chargeStruct().chargeTypeId();
qint32 destructiveFactor = bldgChargeEffect.destructiveFactor();
qint32 penetrationFactor = bldgChargeEffect.penetrationFactor();
if ((destructiveFactor <= 0) || (penetrationFactor <= 0))
WARNING_LOG(m_logger) << "getBuildingChargeEffectByChargeTypeId failed, destructiveFactor or penetrationFactor invalid"
<< ", destructiveFactor = " << destructiveFactor
<< ", penetrationFactor = " << penetrationFactor;
if (!buildingIdsInBetween.empty())
{
destructiveFactor /= 2;
penetrationFactor /= 2;
}
QString properties = QStringLiteral("properties");
properties += mseIdAsStr;
const AreaEffectSimulation::AesZPlacement_def::type zPlacement = singleEffect.zPlacement().underlying();
if (zPlacement == AreaEffectSimulation::AesZPlacement_def::ZpDraped)
{
// z-Value is 0 -> old:
Buildings::SendCesHitOnBuildingRequest requestBld(
footprint.mseId,
ec.chargeStruct().chargeTypeId(),
destructiveFactor,
penetrationFactor,
aesItemId,
m_ddsHandler->currentPartitionTime(),
Core::GeoPosition(impactPositionGeo.lon(), impactPositionGeo.lat(), 0.0),
properties.toStdString());
DEBUG_LOG(m_logger) << "sendCesHitOnBuildingRequest for aesItemId=" << aesItemId
<< " bitFieldIdx= " << indexBitFieldIdx
<< ", impactPositionGeo= " << impactPositionGeo
<< ", chargeTypeId= " << ec.chargeStruct().chargeTypeId()
<< ", destructiveFactor= " << destructiveFactor
<< ", penetrationFactor= " << penetrationFactor;
m_ddsHandler->ftcBld()->sendCesHitOnBuildingRequest(requestBld, this);
bitfieldState.insert(indexBitFieldIdx);
}
else if (zPlacement == AreaEffectSimulation::AesZPlacement_def::ZpAbsolute)
{
// since HandlerBuildingModel::handleDetonationLocationRequest has already
// calculated to ignFuncBase, the impactPosition is the detonationLocation:
Core::GeoPosition detonationLocation = impactPositionGeo;
double detonationDistance = 0;
switch (ignFuncBase.underlying())
{
case ProjectDataManagement::IgniterFunctionBase::IfbProximity:
detonationDistance = -5;
break;
case ProjectDataManagement::IgniterFunctionBase::IfbImpact:
detonationDistance = 0;
break;
case ProjectDataManagement::IgniterFunctionBase::IfbDelay:
detonationDistance = 5;
break;
default:
// empty
break;
} // switch
Buildings::SendCesHit3DonBuildingRequest requestBld(
footprint.mseId,
ec.chargeStruct().chargeTypeId(),
destructiveFactor,
penetrationFactor,
aesItemId,
m_ddsHandler->currentPartitionTime(),
impactPositionGeo,
detonationLocation,
detonationDistance,
properties.toStdString());
DEBUG_LOG(m_logger) << "SendCesHit3DonBuildingRequest for aesItemId=" << aesItemId
<< " bitFieldIdx= " << indexBitFieldIdx
<< ", impactPosition= " << impactPositionGeo
<< ", zPlacement= " << zPlacement
<< ", chargeTypeId= " << ec.chargeStruct().chargeTypeId()
<< ", destructiveFactor= " << destructiveFactor
<< ", penetrationFactor= " << penetrationFactor;
m_ddsHandler->ftcBld()->sendCesHit3DonBuildingRequest(requestBld, this);
bitfieldState.insert(indexBitFieldIdx);
} // if
} // for
}
void HandlerAesData::checkBuildingsHit(const AreaEffectSimulation::AesItemView &data)
{
const int exerciseId = m_ddsHandler->getExerciseId();
QPolygonF polyGeo;
DEBUG_LOG(m_logger) << "checkBuildingsHit " << ", aesItemId=" << data.aesItemId();
DEBUG_LOG(m_logger) << "AtBoobyTrapSecondary data.form().size()=" << data.form().size();
// BoobyTrap
polyGeo.reserve(data.form().size());
for (const auto &pointGeo : qAsConst(data.form()))
{
QPointF point;
point.setX(pointGeo.lon());
point.setY(pointGeo.lat());
DEBUG_LOG(m_logger) << "point= " << point.x() << "/" << point.y();
polyGeo.append(point);
}
bool completeEnclosed = false;
bool isGroundLevel = true;
const QVector<int> &buildingIdsInside = m_ddsHandler->dtcIsm()->getBuildingByPolygon(exerciseId, polyGeo, completeEnclosed, isGroundLevel);
DEBUG_LOG(m_logger) << "buildingsInside.size=" << buildingIdsInside.size()
<< " exerciseId=" << exerciseId;
// only include buildings within the radius
for (const int buildingId : qAsConst(buildingIdsInside))
{
DEBUG_LOG(m_logger) << " BuildingId=" << buildingId;
Buildings::Building tmpBldg;
tmpBldg.buildingId(buildingId);
Core::GeoPosition impactPosition;
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtBoobyTrapSecondary)
{
impactPosition.lat(m_ddsHandler->dtcAes()->boobyTrap(data.aesItemId()).impactPosition().lat());
impactPosition.lon(m_ddsHandler->dtcAes()->boobyTrap(data.aesItemId()).impactPosition().lon());
}
else if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtOffRouteMineSecondary)
{
int zone = -1;
LtsCoordTrans::UtmCoordinate bottomLeft = LtsCoordTrans::UtmCoordinate::fromGeographic(data.boundingBoxBottomLeft().lon(), data.boundingBoxBottomLeft().lat(), zone);
LtsCoordTrans::UtmCoordinate topRight = LtsCoordTrans::UtmCoordinate::fromGeographic(data.boundingBoxTopRight().lon(), data.boundingBoxTopRight().lat(), zone);
LtsCoordTrans::UtmCoordinate impactPosPoint(topRight.zone, ((topRight.x + bottomLeft.x) / 2), ((topRight.y + bottomLeft.y) / 2), true);
impactPosPoint.zone = topRight.zone;
QPointF pointGeo = impactPosPoint.geographic();
impactPosition.lon(pointGeo.x());
impactPosition.lat(pointGeo.y());
DEBUG_LOG(m_logger) << "impactPosition lon/lat=" << impactPosition.lon() << "/" << impactPosition.lat()
<< " impactPosPoint.zone" << impactPosPoint.zone
<< " impactPosPoint=" << impactPosPoint.x << "/" << impactPosPoint.y
<< " topRight=" << topRight.x << "/" << topRight.y
<< " bottomLeft" << bottomLeft.x << "/" << bottomLeft.y;
}
Buildings::SendCesHitOnBuildingRequest requestBld(
tmpBldg.buildingId(),
data.chargeStruct().chargeTypeId(),
m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(data.chargeStruct().chargeTypeId()).destructiveFactor(),
m_ddsHandler->dtcPdm()->getBuildingChargeEffectByChargeTypeId(data.chargeStruct().chargeTypeId()).penetrationFactor(),
data.aesItemId(),
m_ddsHandler->currentPartitionTime(),
impactPosition,
QStringLiteral("properties").toStdString());
DEBUG_LOG(m_logger) << "sendCesHitOnBuildingRequest for aesItemId=" << data.aesItemId()
<< ", impactPosition= " << impactPosition;
m_ddsHandler->ftcBld()->sendCesHitOnBuildingRequest(requestBld, this);
}
}
QVector<int> HandlerAesData::findBuildingsBetween(const LtsCoordTrans::LtsCoordinate &impactPosition,
const CentralEffectSimulation::HandlerBuildingData::Footprint &targetFootprint)
{
QVector<int> buildingIdsInBetween;
// use zone of impactPosition
const LtsCoordTrans::UtmCoordinate &impactPositionUTM = impactPosition.utmCoordinate();
const int zone = impactPositionUTM.zone;
const LtsCoordTrans::LtsCoordinate targetFootprintPos =
LtsCoordTrans::LtsCoordinate::fromUtm(targetFootprint.polyUtm.first(), targetFootprint.zone, 0.0);
DEBUG_LOG(m_logger) << "findBuildingsBetween" << " START impactPosition " << impactPositionUTM.mgrs()
<< ", maybe effected mseId is = " << targetFootprint.mseId
<< " with targetFootprintPos = " << targetFootprintPos.utmCoordinate().mgrs();
// line between a) impactPosition and b) targetFootprintPos
QPolygonF impactToFootprintLineMetric;
impactToFootprintLineMetric.reserve(2);
// // a) impactPosition
// impactToFootprintLineMetric.append(impactPosition.utmCoordinateAsPointF(zone));
// // b) targetFootprintPos
// impactToFootprintLineMetric.append(targetFootprintPos.utmCoordinateAsPointF(zone));
// a) impactPosition
impactToFootprintLineMetric.append(impactPosition.toUtm<QPointF>());
// b) targetFootprintPos
impactToFootprintLineMetric.append(targetFootprintPos.toUtm<QPointF>(zone));
// check all exercise specific footprints except our:
const QHash<int, CentralEffectSimulation::HandlerBuildingData::Footprint> &allFootprintsOfEx = m_handlerBuildingData->getFootprints();
buildingIdsInBetween.reserve(allFootprintsOfEx.size());
for (const CentralEffectSimulation::HandlerBuildingData::Footprint &footprintToCheck : qAsConst(allFootprintsOfEx))
{
// do not check ourself
if (footprintToCheck.mseId == targetFootprint.mseId)
continue;
// check intersection of
// line between a) impactPosition and b) targetFootprintPos
// and footprint
// QPolygonF is your friend :-)
#if QT_VERSION < QT_VERSION_CHECK(5,10,0)
const bool intersects = impactToFootprintLineMetric.intersected(footprintToCheck.polyUtm).size() > 0;
#else
const bool intersects = impactToFootprintLineMetric.intersects(footprintToCheck.polyUtm);
#endif
DEBUG_LOG(m_logger) << "findBuildingsBetween"
<< " to mseId " << footprintToCheck.mseId
<< " with curFpTargetPos " << impactPosition.utmCoordinate().mgrs()
<< " -> intersects = " << intersects;
if (intersects)
{
DEBUG_LOG(m_logger) << "findBuildingsBetween"
<< " regarding impactPosition " << impactPositionUTM.mgrs()
<< " and mseId " << targetFootprint.mseId
<< " is concealed/covered by footpriont of mseId = " << footprintToCheck.mseId;
buildingIdsInBetween.append(footprintToCheck.mseId);
}
} // for
return buildingIdsInBetween;
}
} // namespace CesBuildings