1823 lines
83 KiB
C++
Executable File
1823 lines
83 KiB
C++
Executable File
/**
|
|
* Handler for player data
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
* Created by hjl, SLSE, 2017-02-20
|
|
*/
|
|
|
|
#include "handler_explosions.h"
|
|
#include "expand_polygon.h"
|
|
|
|
#include <LtsUtilsCore/dds_bitarray.h>
|
|
#include <LtsUtilsCore/qpoint3d.h>
|
|
#include <LtsCoordTrans/lts_coord_trans.h>
|
|
|
|
#include <LtsElevation/lts_elevation_manager.h>
|
|
#include <CentralEffectSimulation/handler_participant.h>
|
|
#include <LtsCoordinate/lts_coordinate.h>
|
|
|
|
|
|
#include <QPointF>
|
|
#include <QVector>
|
|
#include <QSet>
|
|
#include <QDateTime>
|
|
#include <QTimer>
|
|
#include <QElapsedTimer>
|
|
|
|
using namespace CentralEffectSimulation;
|
|
using namespace AreaEffectSimulation;
|
|
using namespace LtsCoordTrans;
|
|
|
|
namespace CesExplosions
|
|
{
|
|
|
|
HandlerExplosions::HandlerExplosions(const std::shared_ptr<CentralEffectSimulation::DdsHandler> &ddsHandler,
|
|
const std::shared_ptr<CentralEffectSimulation::HandlerParticipant> &participantHandler,
|
|
const std::shared_ptr<CentralEffectSimulation::ProxyBuilding> &proxyBuilding,
|
|
QObject *parent)
|
|
|
|
: QObject(parent)
|
|
, m_ddsHandler(ddsHandler)
|
|
, m_handlerParticipant(participantHandler)
|
|
, m_proxyBuilding(proxyBuilding)
|
|
, m_logger(CentralEffectSimulation::getLogger("Plugin.Explosions.HandlerExplosions"))
|
|
{
|
|
} // end of ctor()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HandlerExplosions::~HandlerExplosions()
|
|
{
|
|
} // end of dtor()
|
|
//-----------------------------------------------------------------------------
|
|
bool HandlerExplosions::init()
|
|
{
|
|
DEBUG_LOG(m_logger) << "HandlerExplosions::init()";
|
|
auto dtcAes = m_ddsHandler->dtcAes();
|
|
auto ftcTab = m_ddsHandler->ftcTab();
|
|
auto dtcCes = m_ddsHandler->dtsCes();
|
|
|
|
ftcTab->enableSendCesHit();
|
|
dtcCes->enableParticipantEvent();
|
|
|
|
dtcAes->enableAesItemView();
|
|
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged,
|
|
this, &HandlerExplosions::onAesItemViewChanged);
|
|
|
|
dtcAes->enableTriggeredImpacts();
|
|
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::triggeredImpactsChanged,
|
|
this, &HandlerExplosions::onTriggeredImpactsChanged);
|
|
|
|
dtcAes->enableExplosionSingle();
|
|
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionSingleChanged,
|
|
this, &HandlerExplosions::onExplosionSingleChanged);
|
|
dtcAes->enableEffects();
|
|
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::effectsChanged,
|
|
this, &HandlerExplosions::onEffectsChanged, Qt::UniqueConnection);
|
|
|
|
connect(m_handlerParticipant.get(), &CentralEffectSimulation::HandlerParticipant::newOutdoorPosition,
|
|
this, &HandlerExplosions::onNewParticipantCesPosition);
|
|
connect(m_handlerParticipant.get(), &CentralEffectSimulation::HandlerParticipant::newIndoorPosition,
|
|
this, &HandlerExplosions::onNewParticipantCesPosition);
|
|
|
|
connect(m_handlerParticipant.get(), &CentralEffectSimulation::HandlerParticipant::restoreRadioReadinessChanged,
|
|
this, &HandlerExplosions::onRestoreRadioReadinessChanged);
|
|
|
|
dtcAes->enableChargeEffect();
|
|
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::chargeEffectChanged,
|
|
this, &HandlerExplosions::onChargeEffectChanged);
|
|
|
|
return true;
|
|
} // end of init()
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void HandlerExplosions::onSendCesHitReply(const Core::FunctionReplyObject &replyObject)
|
|
{
|
|
if (replyObject.error())
|
|
WARNING_LOG(m_logger) << "Can not send CesHitRequest: " << replyObject();
|
|
else
|
|
DEBUG_LOG(m_logger) << "onSendCesHitReply for id " << replyObject.id() << " success\n"
|
|
<< " replyObject.str() = " << replyObject.str();
|
|
}
|
|
|
|
// ///////////////////////////////////
|
|
// A E S (AreaEffectSimulation)
|
|
// ///////////////////////////////////
|
|
|
|
void HandlerExplosions::onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType)
|
|
{
|
|
if (modType == DataModification::eRemoved)
|
|
return;
|
|
|
|
// NASAT, MIDAS, AirSupport
|
|
// Der explizite HandlerAirSupport wird gestrichen.
|
|
// Der HandlerExplosions übernimmt die Aufgaben mit, da quasi identisch!!
|
|
// if (m_ddsHandler->dtcPdm()->gearClassFromChargeTypeId(data.chargeTypeId()) == ProjectDataManagement::GearClass_def::AirSupport)
|
|
// return;
|
|
|
|
static const QVector<AreaEffectSimulation::AesType> validAesTypes = {AesType_def::AtEffectCluster, AesType_def::AtExplosionAreaSingleExplosions, AesType_def::AtExplosionSingle, AesType_def::AtExplosionCluster};
|
|
if (!validAesTypes.contains(data.aesTypeId()))
|
|
return;
|
|
|
|
const AreaEffectSimulation::AesType aesTypeId = data.aesTypeId();
|
|
const int aesItemId = data.aesItemId();
|
|
const int chargeTypeId = data.chargeStruct().chargeTypeId();
|
|
const ProjectDataManagement::GearClass &gearClass = m_ddsHandler->dtcPdm()->gearClassFromChargeTypeId(chargeTypeId);
|
|
const double impactRadius = m_ddsHandler->chargeTypeRadius(data.chargeStruct());
|
|
bool isCentral = false;
|
|
|
|
DEBUG_LOG(m_logger) << "onAesItemViewChanged 1 "
|
|
<< ", aesItemId=" << aesItemId
|
|
<< ", aesTypeId=" << aesTypeId
|
|
<< ", chargeTypeId=" << chargeTypeId
|
|
<< ", gearClass=" << gearClass
|
|
<< ", impactRadius=" << impactRadius
|
|
<< ", effectStateId=" << data.effectStateId()
|
|
<< ", chargeStruct=" << data.chargeStruct();
|
|
|
|
switch (aesTypeId.underlying())
|
|
{
|
|
case AreaEffectSimulation::AesType_def::AtExplosionSingle:
|
|
{
|
|
const AreaEffectSimulation::ExplosionSingle &explosionSingle = m_ddsHandler->dtcAes()->explosionSingle(aesItemId);
|
|
isCentral = explosionSingle.isCentral();
|
|
DEBUG_LOG(m_logger) << "ExplosionSingle";
|
|
break;
|
|
}
|
|
case AreaEffectSimulation::AesType::AtExplosionArea:
|
|
case AreaEffectSimulation::AesType::AtExplosionAreaSingleExplosions:
|
|
{
|
|
const AreaEffectSimulation::ExplosionArea &explosionArea = m_ddsHandler->dtcAes()->explosionArea(aesItemId);
|
|
isCentral = explosionArea.isCentral();
|
|
DEBUG_LOG(m_logger) << "explosionArea";
|
|
break;
|
|
}
|
|
case AreaEffectSimulation::AesType::AtExplosionCluster:
|
|
{
|
|
const AreaEffectSimulation::ExplosionCluster &explosionCluster = m_ddsHandler->dtcAes()->explosionCluster(aesItemId);
|
|
isCentral = explosionCluster.isCentral() || m_ddsHandler->isLaserGuidable(explosionCluster.chargeStruct());
|
|
DEBUG_LOG(m_logger) << "explosionCluster";
|
|
break;
|
|
}
|
|
case AreaEffectSimulation::AesType::AtEffectCluster:
|
|
{
|
|
isCentral = true;
|
|
DEBUG_LOG(m_logger) << "effectCluster";
|
|
break;
|
|
}
|
|
default:
|
|
DEBUG_LOG(m_logger) << "default, data.aesTypeId=" << aesTypeId;
|
|
} // switch
|
|
|
|
// skip SMART
|
|
if (m_ddsHandler->isSmartChargeType(data.chargeStruct()))
|
|
{
|
|
DEBUG_LOG(m_logger) << " S M A R T return onAesItemViewChanged";
|
|
return;
|
|
} // if
|
|
|
|
if (!isCentral)
|
|
return; // <-------------------------- return
|
|
|
|
//
|
|
// just for isCentral == true
|
|
//
|
|
|
|
DEBUG_LOG(m_logger) << "onAesItemViewChanged 2 aesItemId=" << aesItemId
|
|
<< " isCentral=" << isCentral;
|
|
|
|
const bool isSupplyTruck = false;
|
|
static CentralEffectSimulation::AesItemViewUtm prevData_empty_justForSignatur;
|
|
// hazardDistance and buffer will be done later (prepared, activated)
|
|
CentralEffectSimulation::AesItemViewUtm aesItemViewUtm = CentralEffectSimulation::AesItemViewUtm::create(data,
|
|
prevData_empty_justForSignatur,
|
|
impactRadius,
|
|
isSupplyTruck);
|
|
// default for useHazardDistance, hazardDistance
|
|
|
|
DEBUG_LOG(m_logger) << "onAesItemViewChanged 3"
|
|
<< ", aesItemId=" << aesItemId
|
|
<< ", aesTypeId=" << aesTypeId
|
|
<< ", chargeTypeId=" << chargeTypeId
|
|
<< ", gearClass=" << gearClass
|
|
<< ", isCentral=" << isCentral
|
|
<< ", impactRadius=" << impactRadius
|
|
<< ", effectStateId=" << data.effectStateId();
|
|
|
|
switch (data.effectStateId().underlying())
|
|
{
|
|
case AreaEffectSimulation::AesEffectState_def::EsUnknown:
|
|
case AreaEffectSimulation::AesEffectState_def::EsError:
|
|
case AreaEffectSimulation::AesEffectState_def::EsExpired:
|
|
case AreaEffectSimulation::AesEffectState_def::EsRemoved:
|
|
{
|
|
DEBUG_LOG(m_logger) << " remove aesItemId=" << data.aesItemId();
|
|
// do some cleanup:
|
|
m_handledImpactsStorage.removeAesItem(aesItemId);
|
|
Q_FALLTHROUGH();
|
|
}
|
|
case AreaEffectSimulation::AesEffectState_def::EsPaused:
|
|
{
|
|
// Man kann den m_enteredHazardDistanceStore nutzen.
|
|
// Und allen darin für dieses area/aesItemView gespeicherten participants
|
|
// ein sendLeaveHazardDistance senden...
|
|
|
|
const QVector<int> &allParticipantIdsInArea = m_enteredHazardDistanceStore.allParticipantIdsOfArea(aesItemId);
|
|
|
|
if (m_logger.isDebugEnabled())
|
|
qCritical() << "allParticipantIdsInArea = " << allParticipantIdsInArea;
|
|
|
|
const int remaining = 1; // ICD GUEZSB60_ICD-0052_Zentrale_Tne.doc Wirkfeldtyp Gefahrenbereich Steilfeuer
|
|
const auto &participantPositions = m_handlerParticipant->positions();
|
|
for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &pPos : qAsConst(participantPositions))
|
|
{
|
|
if (!allParticipantIdsInArea.contains(pPos.participantId()))
|
|
continue;
|
|
sendLeaveHazardDistance(pPos.participantId(), aesItemId, remaining);
|
|
} // for
|
|
|
|
// jetzt sollte m_enteredHazardDistanceStore eigentlich bereinigt sein:
|
|
const int remaingPartIdsInArea = m_enteredHazardDistanceStore.allParticipantIdsOfArea(aesItemId).size();
|
|
DEBUG_LOG(m_logger) << " remaingParticipantIdsInArea=" << remaingPartIdsInArea;
|
|
Q_ASSERT(remaingPartIdsInArea == 0);
|
|
|
|
m_enteredHazardDistanceStore.removeArea(aesItemId);
|
|
|
|
|
|
// cleanup:
|
|
if (data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsPaused)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Expired, remove aesItemId=" << aesItemId
|
|
<< ", m_aesItemViewsUtm.remove(" << aesItemId << ")";
|
|
m_aesItemViewsUtm.remove(aesItemId);
|
|
m_aesItemViewsImpactsUtm.remove(aesItemId);
|
|
#ifdef ORIGINATOR_AT_SENDCESHITREQUEST
|
|
m_aesItemId2partIdMap.remove(aesItemId);
|
|
#endif // ORIGINATOR_AT_SENDCESHITREQUEST
|
|
}
|
|
break;
|
|
}
|
|
case AreaEffectSimulation::AesEffectState_def::EsPrepared:
|
|
case AreaEffectSimulation::AesEffectState_def::EsFired:
|
|
case AreaEffectSimulation::AesEffectState_def::EsActivated:
|
|
{
|
|
DEBUG_LOG(m_logger) << "Got AesItemView: " << data.effectStateId()
|
|
<< " aesItemId=" << aesItemId
|
|
<< " isCentral=" << isCentral;
|
|
|
|
DEBUG_LOG(m_logger) << "insert AesItemView: " << data.effectStateId()
|
|
<< " aesItemViewUtm.effectStateId=" << aesItemViewUtm.effectStateId
|
|
<< " chargeTypeId" << aesItemViewUtm.chargeStruct.chargeTypeId();
|
|
|
|
// pre-calculate 'hazardDistanceForm' only in case of -> EsPrepared and EsActivated
|
|
// race condition: what comes first?
|
|
// a) Aiv changes to EsActivated or
|
|
// b) Effects ?
|
|
// So be prepared and calculate hazardDistanceForm already at EsPrepared
|
|
if (aesTypeId == AreaEffectSimulation::AesType_def::AtExplosionCluster || aesTypeId == AreaEffectSimulation::AesType_def::AtEffectCluster)
|
|
{
|
|
// useHazardDistance = (aesTypeId == AreaEffectSimulation::AesType_def::AtExplosionCluster);
|
|
|
|
const double hazardDistance = m_ddsHandler->dtcPdm()->hazardDistance(data.chargeStruct().chargeTypeId(),
|
|
data.chargeStruct().igniterTypeId(),
|
|
data.chargeStruct().igniterFunctionId());
|
|
|
|
DEBUG_LOG(m_logger) << "+++++++++++ ExplosionCluster: hazardDistance=" << hazardDistance
|
|
<< ", impactRadius =" << impactRadius;
|
|
aesItemViewUtm.hazardDistance_meter = hazardDistance;
|
|
aesItemViewUtm.hazardDistanceForm = ExpandPolygon::bufferUtmPolygon(aesItemViewUtm.form, aesItemViewUtm.zone, hazardDistance);
|
|
}
|
|
|
|
if ((aesTypeId == AreaEffectSimulation::AesType_def::AtExplosionClusterSingleExplosions) ||
|
|
(aesTypeId == AreaEffectSimulation::AesType_def::AtExplosionAreaSingleExplosions))
|
|
m_aesItemViewsImpactsUtm.insert(aesItemId, aesItemViewUtm);
|
|
else
|
|
{
|
|
m_aesItemViewsUtm.insert(aesItemId, aesItemViewUtm);
|
|
DEBUG_LOG(m_logger) << "m_aesItemViewsUtm.insert aesItemId=" << aesItemId;
|
|
}
|
|
|
|
//
|
|
// checkPartEnterLeaveArea
|
|
//
|
|
if ((aesItemViewUtm.effectStateId == AreaEffectSimulation::AesEffectState::EsActivated) &&
|
|
aesItemViewUtm.hasHazardDistance())
|
|
{
|
|
// const reference (but if required we can benefit from implicit data sharing)
|
|
// make m_aesItemViewsUtm const to be able to initialze CONST iterator
|
|
const QHash<int, CentralEffectSimulation::AesItemViewUtm> &m_aesItemViewsUtmConstRef = m_aesItemViewsUtm;
|
|
const AES_ITEM_VIEW_UTM_CONST_ITER &curAivIt = m_aesItemViewsUtmConstRef.find(aesItemId);
|
|
if (curAivIt != m_aesItemViewsUtm.constEnd())
|
|
{
|
|
const auto &participantPositions = m_handlerParticipant->positions();
|
|
const int numberOfParametrizedObjects = checkPartEnterLeaveArea(m_aesItemViewsUtm.constBegin(), m_aesItemViewsUtm.constEnd(), // parametrize a single item
|
|
participantPositions.constBegin(), participantPositions.constEnd());
|
|
|
|
DEBUG_LOG(m_logger) << "++++++++ checkPartEnterLeaveArea checkPartEnterLeaveAreaNumber=" << numberOfParametrizedObjects;
|
|
}
|
|
} // if (aesItemViewUtm.effectStart == AreaEffectSimulation::AesEffectState::EsActivated)
|
|
break;
|
|
}
|
|
case AreaEffectSimulation::AesEffectState_def::EsCreated:
|
|
case AreaEffectSimulation::AesEffectState_def::EsReceived:
|
|
break;
|
|
} // switch
|
|
} // END onAesItemViewChanged
|
|
|
|
void HandlerExplosions::onTriggeredImpactsChanged(const AreaEffectSimulation::TriggeredImpacts &data, DataModification modType)
|
|
{
|
|
DEBUG_LOG(m_logger) << "onTriggeredImpactsChanged";
|
|
if (modType == DataModification::eRemoved)
|
|
return;
|
|
|
|
DEBUG_LOG(m_logger) << "Got TriggeredImpacts: " << data
|
|
<< " bitfield().size=" << data.bitfield().size();
|
|
switch (data.aesTypeId().underlying())
|
|
{
|
|
case AreaEffectSimulation::AesType_def::AtExplosionArea:
|
|
case AreaEffectSimulation::AesType_def::AtExplosionSingle:
|
|
case AreaEffectSimulation::AesType_def::AtExplosionAreaSingleExplosions:
|
|
case AreaEffectSimulation::AesType_def::AtExplosionClusterSingleExplosions:
|
|
// explosion at the position of participant
|
|
calculateImpacts(data);
|
|
break;
|
|
default:
|
|
//nothing
|
|
break;
|
|
} // switch
|
|
} // END onTriggeredImpactsChanged
|
|
|
|
void HandlerExplosions::calculateImpact(const AreaEffectSimulation::ChargeEffect &data)
|
|
{
|
|
int zone = -1;
|
|
// Now call calculateImpacts() (createEvent if in circle...)
|
|
const auto &participantPositions = m_handlerParticipant->positions();
|
|
|
|
// GeoPosition to UTM
|
|
const QPointF impactPositionUtm = CentralEffectSimulation::geographic2Utm(data.position(), zone);
|
|
AreaEffectSimulation::ChargeStruct chargeStruct(data.chargeTypeId(),data.igniterTypeId(), data.igniterFunctionId());
|
|
const double impactRadius = m_ddsHandler->chargeTypeRadius(chargeStruct);
|
|
|
|
for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &pPos : qAsConst(participantPositions))
|
|
{
|
|
if (m_handlerParticipant->isOutdated(pPos)) // Participant Position is not valid Todo Magic Number
|
|
continue;
|
|
DEBUG_LOG(m_logger) << "pPos.participantId=" << pPos.participantId();
|
|
if (pPos.vulnerabilityState == ProjectDataManagement::VulnerabilityState_def::CatastrophicKill)
|
|
continue;
|
|
DEBUG_LOG(m_logger) << "pPos.vulnerabilityState=" << pPos.vulnerabilityState;
|
|
if (pPos.insideBuilding())
|
|
continue;
|
|
|
|
const QPointF participantPosUtm = CentralEffectSimulation::geographic2Utm(pPos.pos(), zone);
|
|
|
|
int equipNo = m_ddsHandler->dtcPpm()->equipmentNumberByParticipantId(pPos.participantId());
|
|
ParticipantManagement::DynamicParticipantData dynPart = m_ddsHandler->dtcPpm()->dynamicParticipantData(pPos.participantId());
|
|
ParticipantManagement::Participant originator = m_ddsHandler->dtcPpm()->participant(pPos.participantId());
|
|
|
|
int remainEffectPercent = 100; // TODO hier ist keine Gebäudewand dazwischen der Effekt wirkt komplett auf den Teilnehmer. Ist 100 richtig oder 0?
|
|
|
|
if (inCircle(impactPositionUtm, impactRadius, participantPosUtm))
|
|
{
|
|
DEBUG_LOG(m_logger) << "Teilnehmer getroffen => Send CesHitRequest ";
|
|
|
|
TrainingAreaBase::TabPosition tabPosition;
|
|
TrainingAreaBase::SendCesHitRequest requestTab;
|
|
|
|
tabPosition.position().lat(data.position().lat());
|
|
tabPosition.position().lon(data.position().lon());
|
|
tabPosition.position().z(data.position().z());
|
|
requestTab.equipmentNumber(equipNo);
|
|
requestTab.participantId(pPos.participantId());
|
|
requestTab.chargeTypeId(data.chargeTypeId());
|
|
//default
|
|
requestTab.aesId(0);
|
|
requestTab.eventType(ProjectDataManagement::EventType_def::FireEvent);
|
|
//
|
|
if (data.originatorTypeId() == ProjectDataManagement::StateChangeOriginator::Participant)
|
|
{
|
|
// if we get the equipmentNumber from the participant we will send this (needed by tab)
|
|
// if not fall back to participantId, not negated yet as it's not tested and probalbly handled by the TNE
|
|
const ParticipantManagement::Participant &part = m_ddsHandler->dtcPpm()->participant(data.originatorId());
|
|
if (part.equipmentNumber.is_set())
|
|
requestTab.originatorEquipmentNumber(part.equipmentNumber.get());
|
|
else
|
|
{
|
|
WARNING_LOG(m_logger) << "Sending CesHitRequest without EquipNo, using negative ParticipantID : " << data.originatorId();
|
|
requestTab.originatorEquipmentNumber(-data.originatorId());
|
|
}
|
|
}
|
|
else if (data.originatorTypeId() == ProjectDataManagement::StateChangeOriginator::Party)
|
|
{
|
|
requestTab.originatorEquipmentNumber(data.originatorId());
|
|
}
|
|
else if (data.originatorTypeId() == ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation)
|
|
{
|
|
requestTab.aesId(data.originatorId());
|
|
requestTab.eventType(ProjectDataManagement::EventType_def::ExplosionCluster);
|
|
}
|
|
requestTab.remainEffectPercent(remainEffectPercent);
|
|
requestTab.position(tabPosition);
|
|
requestTab.effectTime(data.effectStart() / 1000);
|
|
ParticipantManagement::Participant originator = m_ddsHandler->dtcPpm()->participant(data.originatorId());
|
|
requestTab.partyId(originator.partyId());
|
|
requestTab.mineId(0);
|
|
requestTab.partyId(originator.partyId());
|
|
|
|
DEBUG_LOG(m_logger) << " participantId=" << pPos.participantId()
|
|
<< ", chargeTypeId=" << data.chargeTypeId()
|
|
<< ", m_sendCesHitRequestQueue.size()=" << m_sendCesHitRequestQueue.size()
|
|
<< ", duration=" << data.effectDuration()
|
|
<< " \n" << requestTab;
|
|
|
|
DEBUG_LOG(m_logger) << "ftcTab->SendCesHitRequest: " << requestTab;
|
|
m_ddsHandler->ftcTab()->sendCesHitRequest(requestTab, this);
|
|
|
|
}
|
|
} // for
|
|
}
|
|
|
|
void HandlerExplosions::startTimer(int id, int msec) {
|
|
auto *timer = new QTimer(this);
|
|
|
|
timer->setProperty("chargeEffectId", id);
|
|
// Timeout-Slot mit echtem SLOT
|
|
connect(timer, &QTimer::timeout, this, &HandlerExplosions::onStartTimeReached);
|
|
timer->start(msec);
|
|
DEBUG_LOG(m_logger) << "Timer " << id << " gestartet fuer " << msec << " ms.";
|
|
}
|
|
|
|
void HandlerExplosions::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);
|
|
calculateImpact(data);
|
|
sender()->deleteLater();
|
|
m_chargeEffects.remove(chargeEffectId);
|
|
}
|
|
|
|
|
|
void HandlerExplosions::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() - 3000 )
|
|
{
|
|
DEBUG_LOG(m_logger) << "Startzeit in der Vergangenheit: aktuelle Zeit - 3 Sekunden";
|
|
return;
|
|
}
|
|
// Startzeit entspricht der aktuellen Zeit
|
|
if ( effectStart > m_ddsHandler->currentPartitionTime() - 3000
|
|
&& effectStart < m_ddsHandler->currentPartitionTime() + data.effectDuration() )
|
|
{
|
|
DEBUG_LOG(m_logger) << "Startzeit entspricht der aktuellen Zeit";
|
|
calculateImpact(data);
|
|
}
|
|
// 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 HandlerExplosions::onExplosionSingleChanged(const AreaEffectSimulation::ExplosionSingle &data, DataModification modType)
|
|
{
|
|
DEBUG_LOG(m_logger) << "\n onExplosionSingleChanged id=" << data.id()
|
|
<< " AesEffectState=" << data.effectStateId();
|
|
if (modType == DataModification::eRemoved)
|
|
return;
|
|
|
|
DEBUG_LOG(m_logger) << "onExplosionSingleChanged 1";
|
|
|
|
if (data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsActivated)
|
|
return;
|
|
|
|
DEBUG_LOG(m_logger) << "onExplosionSingleChanged"
|
|
<< " isCentral=" << data.isCentral();
|
|
|
|
if (!data.isCentral())
|
|
return;
|
|
|
|
DEBUG_LOG(m_logger) << "onExplosionSingle Activated isCentral=" << data.isCentral()
|
|
<< " id=" << data.id();
|
|
|
|
// get access to the aesItemViewUtm storing the data for bbox check:
|
|
CentralEffectSimulation::AesItemViewUtm aesItemViewUtm;
|
|
if (m_aesItemViewsUtm.contains(data.id()))
|
|
aesItemViewUtm = m_aesItemViewsUtm.value(data.id());
|
|
else if (m_aesItemViewsImpactsUtm.contains(data.id()))
|
|
aesItemViewUtm = m_aesItemViewsImpactsUtm.value(data.id());
|
|
else
|
|
ERROR_LOG(m_logger) << "Kein AesItemViewUtm ";
|
|
|
|
// Now call calculateImpacts() (createEvent if in circle...)
|
|
const auto &participantPositions = m_handlerParticipant->positions();
|
|
|
|
// GeoPosition to UTM
|
|
QVector<QPointF> triggeredEffectsUtmPos;
|
|
triggeredEffectsUtmPos.reserve(1);
|
|
const QPointF &posUtm = CentralEffectSimulation::geographic2Utm(data.impactPosition(), aesItemViewUtm.zone);
|
|
triggeredEffectsUtmPos.push_back(posUtm);
|
|
|
|
if (m_ddsHandler->isSmartChargeType(data.chargeStruct()))
|
|
{
|
|
DEBUG_LOG(m_logger) << " S M A R T "
|
|
<< " aesItemId=" << aesItemViewUtm.aesItemId
|
|
<< " chargeTypeId=" << data.chargeStruct().chargeTypeId()
|
|
<< " partyId=" << aesItemViewUtm.partyId
|
|
<< " sampleTime=" << data.sampleTime();
|
|
calculateSmartExplosion(aesItemViewUtm.aesItemId, data.chargeStruct(), aesItemViewUtm.partyId, data.impactPosition(), data.sampleTime());
|
|
return;
|
|
}
|
|
|
|
for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &pPos : qAsConst(participantPositions))
|
|
{
|
|
if (m_handlerParticipant->isOutdated(pPos)) // Participant Position is not valid Todo Magic Number
|
|
continue;
|
|
DEBUG_LOG(m_logger) << "pPos.participantId=" << pPos.participantId();
|
|
if (pPos.vulnerabilityState == ProjectDataManagement::VulnerabilityState_def::CatastrophicKill)
|
|
continue;
|
|
DEBUG_LOG(m_logger) << "pPos.vulnerabilityState=" << pPos.vulnerabilityState;
|
|
if (pPos.insideBuilding())
|
|
continue;
|
|
DEBUG_LOG(m_logger) << "pPos.insideBuilding=" << pPos.insideBuilding();
|
|
|
|
if (!inBoundingBoxCheck(aesItemViewUtm, pPos))
|
|
continue;
|
|
|
|
DEBUG_LOG(m_logger) << "calculateImpacts zone=" << aesItemViewUtm.zone;
|
|
const Core::GeoPosition impactPosition3D(data.impactPosition().lon(), data.impactPosition().lat(), 0.0);
|
|
|
|
Options options;
|
|
calculateImpactsEffect_outdated(aesItemViewUtm,
|
|
{impactPosition3D}, triggeredEffectsUtmPos,
|
|
pPos, options);
|
|
} // for
|
|
|
|
}
|
|
|
|
void HandlerExplosions::onEffectsChanged(const AreaEffectSimulation::Effects &effects, DataModification modType)
|
|
{
|
|
QElapsedTimer timer;
|
|
if (m_logger.isInfoEnabled())
|
|
timer.start();
|
|
|
|
// The topic Effects is the successor of TriggeredImpacts & AesItemView (to avoid race condition)
|
|
|
|
Q_UNUSED(modType);
|
|
|
|
// Until now (04/2024) AreaEffectSimulation::Effects is used at ExplosionCluster only:
|
|
if (effects.aesTypeId().underlying() != AreaEffectSimulation::AesType_def::AtExplosionClusterSingleExplosions)
|
|
return;
|
|
|
|
// older then 10sec? ignore
|
|
if (effects.sampleTime() < m_ddsHandler->currentPartitionTime() - 10 * 1000)
|
|
return;
|
|
|
|
const int aesItemId = effects.aesItemId();
|
|
AreaEffectSimulation::AesItem aesItem;
|
|
bool itemAvailable = m_ddsHandler->dtcAes()->aesItemFromId(effects.aesItemId(), aesItem);
|
|
if ( itemAvailable)
|
|
{
|
|
if (m_ddsHandler->isSmartChargeType(aesItem.chargeStruct()))
|
|
{
|
|
DEBUG_LOG(m_logger) << "Calculate SMART";
|
|
calculateSmartEffect(effects);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DEBUG_LOG(m_logger) << "No aesItem found for effect aesItemId=" << effects.aesItemId();
|
|
}
|
|
|
|
// get access to the aesItemViewUtm storing the data for bbox check:
|
|
const CentralEffectSimulation::AesItemViewUtm &aesItemViewUtm = m_aesItemViewsUtm.value(aesItemId);
|
|
|
|
// Is the m_aesItemViewsUtm filled with data _BEFORE_ onEffectsChanged is called?
|
|
// AesItem will be created _BEOFRE_ effects (Topic Effects) could be published:
|
|
if (aesItemViewUtm.aesItemId != aesItemId)
|
|
{
|
|
DEBUG_LOG(m_logger) << "aesItemViewUtm (with extent) not found for aesItemId =" << aesItemId
|
|
<< " perhaps the field is not central, expired or SMART return onEffectsChanged";
|
|
return;
|
|
}
|
|
|
|
//
|
|
// assume that areas with (isCentral = false) are not stored at m_aesItemViewsUtm
|
|
//
|
|
|
|
const int chargeTypeId = aesItemViewUtm.chargeStruct.chargeTypeId();
|
|
const ProjectDataManagement::GearClass &gearClass = m_ddsHandler->dtcPdm()->gearClassFromChargeTypeId(chargeTypeId);
|
|
|
|
DEBUG_LOG(m_logger) << "onEffectsChanged, aesItemId=" << aesItemId
|
|
<< ", chargeTypeId = " << chargeTypeId
|
|
<< ", gearClass = " << gearClass;
|
|
|
|
// determine relevant positions:
|
|
const std::vector<AreaEffectSimulation::SingleEffect> &singleEffectsRef = effects.singleEffects();
|
|
|
|
QVector<LtsCoordinate> triggeredEffectsLtsCoord;
|
|
triggeredEffectsLtsCoord.reserve(singleEffectsRef.size());
|
|
|
|
int curSeIndex = -1;
|
|
for (const AreaEffectSimulation::SingleEffect &se : qAsConst(singleEffectsRef))
|
|
{
|
|
++curSeIndex;
|
|
if (se.singleEffectState() != AreaEffectSimulation::AesSingleEffectState_def::SesTriggered)
|
|
continue;
|
|
|
|
// compare current triggered idx with triggered idx of the past
|
|
if (m_handledImpactsStorage.isIndexProcessed(aesItemId, curSeIndex))
|
|
continue;
|
|
|
|
// okay, the current idx is triggered now and was not triggered before -> changing
|
|
triggeredEffectsLtsCoord.push_back(LtsCoordinate(se.position())); // ZpDraped and ZpAbsolute should have a valid zValue
|
|
|
|
// update storage ('remember')
|
|
m_handledImpactsStorage.setIndexProcessed(aesItemId, curSeIndex);
|
|
} // for const AreaEffectSimulation::SingleEffect &se
|
|
|
|
// now triggeredEffectsLtsCoord stores the (new) GeoPositions to be calculated:
|
|
|
|
DEBUG_LOG(m_logger) << "Effects of aesItemId =" << aesItemId
|
|
<< ", number of new SingleEffects (in this timeslot) = " << triggeredEffectsLtsCoord.size();
|
|
|
|
// Now call calculateImpacts() (createEvent if in circle...)
|
|
const auto &participantPositions = m_handlerParticipant->positions();
|
|
for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &pPos : qAsConst(participantPositions))
|
|
{
|
|
if (pPos.vulnerabilityState == ProjectDataManagement::VulnerabilityState_def::CatastrophicKill)
|
|
continue;
|
|
if (m_handlerParticipant->isOutdated(pPos)) // Participant Position is not valid Todo Magic Number
|
|
continue;
|
|
DEBUG_LOG(m_logger) << "pPos.participantId=" << pPos.participantId()
|
|
<< ", vulnerabilityState=" << pPos.vulnerabilityState;
|
|
if (pPos.insideBuilding())
|
|
continue;
|
|
DEBUG_LOG(m_logger) << "pPos.insideBuilding=" << pPos.insideBuilding();
|
|
|
|
// skip inBoundingBoxCheck for ExplosionCluster since impacts could be OUTSIDE of bbox (intersection with bldg)
|
|
// if (!inBoundingBoxCheck(aesItemViewUtm, pPos))
|
|
// continue;
|
|
|
|
DEBUG_LOG(m_logger) << "calculateImpactsEffect for participantId " << pPos.participantId()
|
|
<< " Pos=" << pPos.ltsCoordinate().toString() << " ....";
|
|
// both: BLDG_LOS, ELEVATION_LOS
|
|
Options options = EXECUTE_BLDG_LOS_CALC |
|
|
EXECUTE_ELEVATION_LOS_CALC;
|
|
calculateImpactsEffect(aesItemViewUtm, triggeredEffectsLtsCoord, pPos, options);
|
|
|
|
DEBUG_LOG(m_logger) << "calculateImpactsEffect for participantId " << pPos.participantId() << " DONE";
|
|
} // for const CentralEffectSimulation::HandlerParticipant::CesParticipantData &pPos
|
|
|
|
INFO_LOG(m_logger) << " onEffectsChanged took [ms] " << timer.elapsed();
|
|
}
|
|
|
|
// [GUEZE-2257]3D Wirksim - Gefaehrdungsabstand Zentral
|
|
void HandlerExplosions::onNewParticipantCesPosition(const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantData)
|
|
{
|
|
if (m_aesItemViewsUtm.empty())
|
|
{
|
|
TRACE_LOG(m_logger) << "++++++++ onNewParticipantOutdoorPosition participantId=" << participantData.participantId()
|
|
<< ", m_aesItemViewsUtm.size=" << m_aesItemViewsUtm.size();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// checkPartEnterLeaveArea
|
|
//
|
|
// const reference (but if required we can benefit from implicit data sharing)
|
|
const auto &participantPositions = m_handlerParticipant->positions();
|
|
const HandlerParticipant::POSITIONS_CONST_ITER curPartIt = participantPositions.find(participantData.participantId());
|
|
if (curPartIt == participantPositions.constEnd())
|
|
return;
|
|
|
|
const int numberOfParametrizedObjects = checkPartEnterLeaveArea(m_aesItemViewsUtm.constBegin(), m_aesItemViewsUtm.constEnd(),
|
|
curPartIt, curPartIt + 1); // parametrize a single item
|
|
|
|
DEBUG_LOG(m_logger) << "++++++++ checkPartEnterLeaveArea checkPartEnterLeaveAreaNumber=" << numberOfParametrizedObjects;
|
|
}
|
|
|
|
int HandlerExplosions::checkPartEnterLeaveArea(AES_ITEM_VIEW_UTM_CONST_ITER currentAreaIt,
|
|
const AES_ITEM_VIEW_UTM_CONST_ITER &endingAreaIt,
|
|
HandlerParticipant::POSITIONS_CONST_ITER currentParticipantIt,
|
|
const HandlerParticipant::POSITIONS_CONST_ITER &endingParticipantIt)
|
|
{
|
|
DEBUG_LOG(m_logger) << "checkPartEnterLeaveArea...";
|
|
|
|
int numberOfParametrizedObjects = 0;
|
|
|
|
// outer loop (aesitemViewsUtm)
|
|
for (; currentAreaIt != endingAreaIt; ++currentAreaIt)
|
|
{
|
|
const CentralEffectSimulation::AesItemViewUtm &curAivUtmArea = *currentAreaIt;
|
|
|
|
// check aiv:
|
|
if (curAivUtmArea.effectStateId != AreaEffectSimulation::AesEffectState_def::EsActivated)
|
|
continue;
|
|
if (!curAivUtmArea.hasHazardDistance())
|
|
continue;
|
|
|
|
int zoneInOut = curAivUtmArea.zone;
|
|
|
|
// inner loop (participants)
|
|
for (; currentParticipantIt != endingParticipantIt; ++currentParticipantIt)
|
|
{
|
|
++numberOfParametrizedObjects;
|
|
|
|
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantData = *currentParticipantIt;
|
|
const int participantId = participantData.participantId();
|
|
const Core::GeoPosition &participantGeoPos = participantData.pos();
|
|
|
|
if (LtsCoordTrans::LtsCoordinate::valueIsNullOrNan(participantGeoPos.lon()) ||
|
|
LtsCoordTrans::LtsCoordinate::valueIsNullOrNan(participantGeoPos.lat()))
|
|
continue;
|
|
|
|
if (m_handlerParticipant->isOutdated(participantData)) // Participant Position is not valid Todo Magic Number
|
|
continue;
|
|
|
|
// participant insideBuilding should also be 'effected' regarding hazardDistance
|
|
// process following type of positions:
|
|
// - outdoor positions (based on GPS or IndoorPositioningSystem)
|
|
// - indoor positions (basedIndoorPositioningSystem) - Not implemented yet
|
|
// see also : https://devhub.defence-elec.de/confluence/display/ORGSL/Wirkung+CES
|
|
//
|
|
// if (participantData.insideBuilding())
|
|
// {
|
|
// DEBUG_LOG(m_logger) << "Participant:" << participantData.participantId() << " inside Building";
|
|
// continue;
|
|
// }
|
|
|
|
const QPointF &posUtm = participantData.ltsCoordinate().toUtm<QPointF>(zoneInOut);
|
|
DEBUG_LOG(m_logger) << "Check, if participant with id = " << participantId
|
|
<< " is inside hazardDistanceForm: "
|
|
<< ", hasHazardDistance=" << curAivUtmArea.hasHazardDistance()
|
|
<< ", hazardDistance=" << curAivUtmArea.hazardDistance_meter
|
|
<< ", posUtm=" << posUtm
|
|
<< ", zone=" << zoneInOut;
|
|
|
|
if (m_logger.isDebugEnabled())
|
|
{
|
|
for (int i = 0; i < curAivUtmArea.form.size(); ++i)
|
|
DEBUG_LOG(m_logger) << "form[" << i << "]=" << curAivUtmArea.form[i].x() << "/" << curAivUtmArea.form[i].y();
|
|
if (m_logger.isTraceEnabled())
|
|
for (int i = 0; i < curAivUtmArea.hazardDistanceForm.size(); ++i)
|
|
TRACE_LOG(m_logger) << "hazardDistanceForm[" << i << "]=" << curAivUtmArea.hazardDistanceForm[i].x() << "/" << curAivUtmArea.hazardDistanceForm[i].y();
|
|
}
|
|
|
|
// Laut ICD GUEZSB60_ICD-0052_Zentrale_Tne.doc Wirkfeldtyp Gefahrenbereich Steilfeuer
|
|
// ist der Gefahrenbereich/Gefährdungsabstand
|
|
// der a) INNERHALB des hazardDistance, aber b) AUSSERHALB des eigentlichen Wirkfeldes.
|
|
// also: bRealInHazardDistance = participantIsInsideHazardDistance && !participantIsInsideForm
|
|
|
|
// assume hazardDistanceForm is bigger than aiv :-)
|
|
const bool participantIsInsideHazardDistance = curAivUtmArea.hazardDistanceForm.containsPoint(posUtm, Qt::FillRule::OddEvenFill);
|
|
const bool participantIsInsideForm = participantIsInsideHazardDistance &&
|
|
curAivUtmArea.form.containsPoint(posUtm, Qt::FillRule::OddEvenFill);
|
|
|
|
const bool bRealInHazardDistance = participantIsInsideHazardDistance && !participantIsInsideForm;
|
|
const bool bWasRealInHazardDistance = m_enteredHazardDistanceStore.hasParticipantAlreadyEnteredArea(participantId, curAivUtmArea.aesItemId);
|
|
|
|
DEBUG_LOG(m_logger) << "participantId = " << participantId
|
|
<< ", participantIsInsideHazardDistance = " << participantIsInsideHazardDistance
|
|
<< ", participantIsInsideForm = " << participantIsInsideForm
|
|
<< ", bRealInHazardDistance = " << bRealInHazardDistance
|
|
<< ", bWasRealInHazardDistanc = " << bWasRealInHazardDistance;
|
|
|
|
|
|
// N. Thoden, 19.11.2024
|
|
// Ein Übergang von ganz innen nach ganz aussen oder umgekehrt, scheint nicht definiert zu sein.
|
|
// Mit Hilfe der Verwendung von b___Real___InHazardDistance würde keine Modifikation erkannt werden...
|
|
|
|
// does state changes?
|
|
if (bWasRealInHazardDistance != bRealInHazardDistance)
|
|
{
|
|
// now IN?
|
|
if (bRealInHazardDistance)
|
|
{
|
|
// part is ENTERING
|
|
DEBUG_LOG(m_logger) << "Send HazardDistanceMessage to participant (ENTERING)...";
|
|
sendEnterHazardDistance(participantId, curAivUtmArea.aesItemId);
|
|
|
|
}
|
|
else
|
|
{
|
|
// logical consequence: part is LEAVING
|
|
int remaining = 2; // ICD GUEZSB60_ICD-0052_Zentrale_Tne.doc Wirkfeldtyp Gefahrenbereich Steilfeuer
|
|
// 1 = verlassen in Richtung Wirkfeld (nach innen)
|
|
// 2 = verlassen nach aussen
|
|
// 3 = Info
|
|
// 0 = betreten
|
|
|
|
// Die Meldung wird einmalig bei Betreten des Gefahrbereichs und bei Verlassen gesendet.
|
|
// Die Meldung erfolgt fuer jeden Gefahrbereich, in dem sich der TN zu einem Zeitpunkt befindet
|
|
// (sich ueberlappende Gefahrbereiche sind moeglich). Die Warnmeldung auf dem CAN-Bus erfolgt im 30 Sekunden-Takt,
|
|
// solange sich der TN in einem oder mehreren Gefahrbereich(en) befindet. Betritt ein TN den Wirkbereich selber,
|
|
// kommt eine Verlassensmeldung fuer das betreffende Wirkfeld ("Verlassen nach innen").
|
|
// Erst wenn der TN alle Gefahrbereiche - entweder nach innen in Richtung Wirkfeld selber oder nach aussen - verlassen hat,
|
|
// unterbleiben die zyklischen Warnmeldungen auf dem CAN-Bus.
|
|
// Bei Wirkung = 3 (Info) wird nur einmalig eine Warnmeldung ausgegeben.
|
|
// Wird eine TNE stromlos (Restart) oder verliert zwischenzeitlich die Funkbereitschaft,
|
|
// kommen fuer alle betretenen Gefahrbereich neue Betreten-Meldungen von der Zentrale (inkl. Wirkbeginn + Wirkdauer).
|
|
|
|
if (participantIsInsideForm)
|
|
remaining = 1;
|
|
DEBUG_LOG(m_logger) << "Send HazardDistanceMessage to participant (LEAVING)... remaining =" << remaining;
|
|
sendLeaveHazardDistance(participantData.participantId(), curAivUtmArea.aesItemId, remaining);
|
|
}
|
|
} // if (bWasRealInHazardDistance != bRealInHazardDistance)
|
|
|
|
} // for
|
|
} // for
|
|
|
|
return numberOfParametrizedObjects;
|
|
}
|
|
|
|
|
|
void HandlerExplosions::onRestoreRadioReadinessChanged(const int participantId)
|
|
{
|
|
DEBUG_LOG(m_logger) << "HandlerExplosions::onRestoreRadioReadinessChanged participantId=" << participantId;
|
|
m_enteredHazardDistanceStore.removeParticipant(participantId);
|
|
}
|
|
|
|
// return true, if participant is inside of the bounding Box
|
|
bool HandlerExplosions::inBoundingBoxCheck(const CentralEffectSimulation::AesItemViewUtm &itemViewUtm,
|
|
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition)
|
|
{
|
|
DEBUG_LOG(m_logger) << "inBoundingBoxCheck 1"
|
|
<< ", aesItemId=" << itemViewUtm.aesItemId
|
|
<< ", effectStateId=" << itemViewUtm.effectStateId;
|
|
if ((itemViewUtm.effectStateId != AreaEffectSimulation::AesEffectState_def::EsActivated) &&
|
|
(itemViewUtm.effectStateId != AreaEffectSimulation::AesEffectState_def::EsPrepared) &&
|
|
(itemViewUtm.effectStateId != AreaEffectSimulation::AesEffectState_def::EsFired))
|
|
return false;
|
|
DEBUG_LOG(m_logger) << "inBoundingBoxCheck 2";
|
|
|
|
// QRectF(topLeft, bottomRight) -> no need to normalize, QRectF:contains() handles it correct
|
|
|
|
const QRectF boundingBoxImpact(itemViewUtm.boundingBoxBottomLeft,
|
|
itemViewUtm.boundingBoxTopRight);
|
|
|
|
const QPointF participantPosUtmPoint = CentralEffectSimulation::geographic2Utm(participantPosition.pos(), itemViewUtm.zone);
|
|
if (boundingBoxImpact.contains(participantPosUtmPoint))
|
|
{
|
|
DEBUG_LOG(m_logger) << "inBoundingBoxCheck Paricipant " << participantPosition.participantId()
|
|
<< " inside AesItemView:" << itemViewUtm.aesItemId;
|
|
return true;
|
|
}
|
|
DEBUG_LOG(m_logger) << "inBoundingBoxCheck Paricipant " << participantPosition.participantId()
|
|
<< " outside AesItemView:" << itemViewUtm.aesItemId;
|
|
return false;
|
|
} // END inBoundingBoxCheck
|
|
|
|
// [GUEZE-3526] SMART Munition ueberpruefen
|
|
// Nach der Umstellung auf 3D-WS wird der Einschlag von SMART aus dem DDS-Topic AES::Effects genommen.
|
|
void HandlerExplosions::calculateSmartEffect(const AreaEffectSimulation::Effects &effects)
|
|
{
|
|
DEBUG_LOG(m_logger) << "calculateSmartEffect";
|
|
|
|
AreaEffectSimulation::AesItem aesItem;
|
|
bool itemAvailable = m_ddsHandler->dtcAes()->aesItemFromId(effects.aesItemId(), aesItem);
|
|
if ( itemAvailable)
|
|
{
|
|
for (const AreaEffectSimulation::SingleEffect &se : qAsConst(effects.singleEffects()))
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " ---> singleEffect=" << se
|
|
<< ", aesItem= " << aesItem;
|
|
calculateSmartExplosion(effects.aesItemId(),
|
|
aesItem.chargeStruct(),
|
|
aesItem.partyId.is_set() ? aesItem.partyId.get() : 0,
|
|
Core::GeoPosition(se.position().lon(), se.position().lat(), 0),
|
|
effects.sampleTime());
|
|
} // for
|
|
} // if
|
|
else
|
|
{
|
|
WARNING_LOG(m_logger) << "No aesItem found for effect aesItemId=" << effects.aesItemId();
|
|
} // else
|
|
} // END calculateSmartEffect
|
|
|
|
void HandlerExplosions::calculateSmartExplosion(const int32_t aesItemId,
|
|
const AreaEffectSimulation::ChargeStruct &chargeStruct,
|
|
int32_t partyId,
|
|
const Core::GeoPosition2D &impactPosition,
|
|
qint64 impactTime)
|
|
{
|
|
DEBUG_LOG(m_logger) << "calculateSmartExplosion "
|
|
<< " partyId=" << partyId;
|
|
if (partyId == 0)
|
|
return;
|
|
|
|
int impactPosZone = -1;
|
|
const QPointF impactPos(CentralEffectSimulation::geographic2Utm(impactPosition, &impactPosZone));
|
|
|
|
QVector <ParticipantDistance> vehiclesInSmartExplosion;
|
|
|
|
DEBUG_LOG(m_logger) << "calculateSmartExplosion chargeTypeId=" << chargeStruct.chargeTypeId();
|
|
// Insert all Vehicles inside of explosion-Radius in list vehiclesInSmartExplosion
|
|
const double searchRadius = m_ddsHandler->searchRadius(chargeStruct); // where the smart munition should search for targets
|
|
const double impactRadius = m_ddsHandler->chargeTypeRadius(chargeStruct); // the real explosion area radius
|
|
DEBUG_LOG(m_logger) << "Handle ExplosionSingle (EsActivated)";
|
|
bool bHumanFound = false;
|
|
|
|
const auto &participantPositions = m_handlerParticipant->positions();
|
|
for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition : qAsConst(participantPositions))
|
|
{
|
|
if (m_handlerParticipant->isOutdated(participantPosition)) // Participant Position is not valid Todo Magic Number
|
|
continue;
|
|
|
|
const QPointF participantPosUtmPoint = CentralEffectSimulation::geographic2Utm(participantPosition.pos(), impactPosZone);
|
|
|
|
ParticipantManagement::TypeClass typeClass = m_handlerParticipant->typeClassFromTypeId(participantPosition.participantTypeId());
|
|
DEBUG_LOG(m_logger) << "participant id=" << participantPosition.participantId()
|
|
<< " participantTypeId=" << participantPosition.participantTypeId()
|
|
<< " partyType =" << participantPosition.partyId()
|
|
<< " typeClass=" << typeClass;
|
|
|
|
// Only send, if Any Participant is in Smart-Range (save Tetra load)
|
|
if (!bHumanFound && typeClass == ParticipantManagement::TypeClass_def::Human)
|
|
bHumanFound = inCircle(impactPos, impactRadius, participantPosUtmPoint);
|
|
|
|
if (typeClass != ParticipantManagement::TypeClass_def::GroundVehicle &&
|
|
typeClass != ParticipantManagement::TypeClass_def::VehicleWithGear &&
|
|
typeClass != ParticipantManagement::TypeClass_def::WeaponSystem)
|
|
continue;
|
|
|
|
DEBUG_LOG(m_logger) << "participantPosition.timestamp =" << participantPosition.sampleTime()
|
|
<< " effectTime = " << m_ddsHandler->currentPartitionTime();
|
|
|
|
DEBUG_LOG(m_logger) << "Vehicle => calculate SMART participantId=" << participantPosition.participantId()
|
|
<< " ImpactPos=" << CentralEffectSimulation::debug(impactPos)
|
|
<< " searchRadius=" << searchRadius
|
|
<< " participantPosition=" << CentralEffectSimulation::debug(participantPosUtmPoint);
|
|
if (inCircle(impactPos, searchRadius, participantPosUtmPoint))
|
|
{
|
|
ParticipantDistance oneEntry;
|
|
oneEntry.squareDistance = CentralEffectSimulation::getSquareDistance(impactPos, participantPosUtmPoint);
|
|
oneEntry.participantPosition = participantPosition;
|
|
DEBUG_LOG(m_logger) << "squareDistance = " << oneEntry.squareDistance
|
|
<< " append participantId=" << participantPosition.participantId();
|
|
vehiclesInSmartExplosion.append(std::move(oneEntry));
|
|
}
|
|
}
|
|
|
|
CentralEffectSimulation::AesItemViewUtm itemViewUtm = m_aesItemViewsImpactsUtm.value(aesItemId);
|
|
itemViewUtm.chargeStruct = chargeStruct; // SingleExplosion: no Entry
|
|
itemViewUtm.aesItemId = aesItemId;
|
|
DEBUG_LOG(m_logger) << "aesItemViewUtm itemView.chargeTypeId =" << itemViewUtm.chargeStruct.chargeTypeId();
|
|
if (vehiclesInSmartExplosion.size() == 0)
|
|
{
|
|
DEBUG_LOG(m_logger) << "size=0";
|
|
if (bHumanFound)
|
|
{
|
|
CentralEffectSimulation::HandlerParticipant::CesParticipantData pos;
|
|
pos.pos(Core::GeoPosition(impactPosition.lon(), impactPosition.lat(), 0));
|
|
// send single fire to all if at least one participant is inside circle
|
|
// REGG-4160 no SMART-reachtion in GUEZ
|
|
// sendCesHitRequest(itemView, pos, impactTime);
|
|
}
|
|
}
|
|
else if (vehiclesInSmartExplosion.size() == 1)
|
|
{
|
|
DEBUG_LOG(m_logger) << "size=1";
|
|
// send two single fire to the same vehicle
|
|
sendCesHitRequest( itemViewUtm, vehiclesInSmartExplosion.first().participantPosition, impactTime , 100, ProjectDataManagement::EventType_def::HitEffectSimulation); // 100 Percent
|
|
sendCesHitRequest( itemViewUtm, vehiclesInSmartExplosion.first().participantPosition, impactTime + 1000, 100, ProjectDataManagement::EventType_def::HitEffectSimulation); // 100 Percent
|
|
sendParticipantEvent(itemViewUtm, vehiclesInSmartExplosion.first().participantPosition, ProjectDataManagement::EventType_def::HitEffectSimulation);
|
|
sendParticipantEvent(itemViewUtm, vehiclesInSmartExplosion.first().participantPosition, ProjectDataManagement::EventType_def::HitEffectSimulation);
|
|
}
|
|
else
|
|
{
|
|
DEBUG_LOG(m_logger) << "size=" << vehiclesInSmartExplosion.size();
|
|
// send single fire to the two nearest vehicles
|
|
|
|
// sort the list by the distance
|
|
const auto mapSortLessThan = [](const ParticipantDistance & s1, const ParticipantDistance & s2) -> bool
|
|
{
|
|
return s1.squareDistance < s2.squareDistance;
|
|
};
|
|
std::sort(vehiclesInSmartExplosion.begin(), vehiclesInSmartExplosion.end(), mapSortLessThan);
|
|
|
|
sendCesHitRequest( itemViewUtm, vehiclesInSmartExplosion.at(0).participantPosition, impactTime, 100, ProjectDataManagement::EventType_def::HitEffectSimulation); // 100 Percent
|
|
sendCesHitRequest( itemViewUtm, vehiclesInSmartExplosion.at(1).participantPosition, impactTime, 100, ProjectDataManagement::EventType_def::HitEffectSimulation); // 100 Percent
|
|
sendParticipantEvent(itemViewUtm, vehiclesInSmartExplosion.at(0).participantPosition, ProjectDataManagement::EventType_def::HitEffectSimulation);
|
|
sendParticipantEvent(itemViewUtm, vehiclesInSmartExplosion.at(1).participantPosition, ProjectDataManagement::EventType_def::HitEffectSimulation);
|
|
|
|
}
|
|
} // calculateSmartExplosion
|
|
|
|
|
|
// Check for all participants wheter he is in the explosion area
|
|
void HandlerExplosions::calculateImpacts(const AreaEffectSimulation::TriggeredImpacts &data)
|
|
{
|
|
DEBUG_LOG(m_logger) << "calculateImpacts m_aesItemViewsImpactsUtm.size " << m_aesItemViewsImpactsUtm.size();
|
|
|
|
// older then 10sec? ignore
|
|
if (data.sampleTime() < m_ddsHandler->currentPartitionTime() - 10 * 1000)
|
|
return;
|
|
|
|
if (!m_aesItemViewsImpactsUtm.contains(data.aesItemId()))
|
|
{
|
|
DEBUG_LOG(m_logger) << "calculateImpacts no aesItemId " << data.aesItemId();
|
|
return;
|
|
}
|
|
|
|
//CentralEffectSimulation::AesItemViewUtm aesItemViewUtm = m_aesItemViewsImpactsUtm.value(static_cast<int>(data.aesItemId()));
|
|
CentralEffectSimulation::AesItemViewUtm aesItemViewUtm;
|
|
if ((data.aesTypeId().underlying() == AreaEffectSimulation::AesType_def::AtExplosionClusterSingleExplosions)
|
|
|| (data.aesTypeId().underlying() == AreaEffectSimulation::AesType_def::AtExplosionAreaSingleExplosions))
|
|
aesItemViewUtm = m_aesItemViewsImpactsUtm.value(static_cast<int>(data.aesItemId()));
|
|
else
|
|
aesItemViewUtm = m_aesItemViewsUtm.value(static_cast<int>(data.aesItemId()));
|
|
DEBUG_LOG(m_logger) << "m_aesItemViewsUtm.insert aesItemId=" << aesItemViewUtm.aesItemId;
|
|
|
|
const bool bIsSmartChargeType = m_ddsHandler->isSmartChargeType(aesItemViewUtm.chargeStruct);
|
|
DEBUG_LOG(m_logger) << "calculateImpacts, isSMart: " << bIsSmartChargeType;
|
|
|
|
|
|
DEBUG_LOG(m_logger) << "XXXXXXXXXXXX calculateImpacts1 " << data.aesItemId();
|
|
|
|
QVector<int> bitsToCheck;
|
|
for (int bitFieldIdx = 0; bitFieldIdx < data.impactCount(); ++bitFieldIdx)
|
|
{
|
|
// not yet triggered (bit is still 1)
|
|
if (LtsUtils::BitArray::testBit(data.bitfield(), bitFieldIdx))
|
|
continue;
|
|
|
|
if (m_handledImpactsStorage.isIndexProcessed(data.aesItemId(), bitFieldIdx))
|
|
continue;
|
|
|
|
bitsToCheck += bitFieldIdx;
|
|
DEBUG_LOG(m_logger) << "bitsToCheck+=" << bitFieldIdx;
|
|
|
|
// update storage ('remember')
|
|
m_handledImpactsStorage.setIndexProcessed(data.aesItemId(), bitFieldIdx);
|
|
|
|
if (bIsSmartChargeType)
|
|
{
|
|
DEBUG_LOG(m_logger) << "XXXXXXXXXXXX SMART";
|
|
calculateSmartExplosion(aesItemViewUtm.aesItemId, aesItemViewUtm.chargeStruct, aesItemViewUtm.partyId, aesItemViewUtm.formGeo[bitFieldIdx], m_ddsHandler->currentPartitionTime());
|
|
}
|
|
}
|
|
|
|
if (bIsSmartChargeType)
|
|
return;
|
|
|
|
const auto &participantPositions = m_handlerParticipant->positions();
|
|
for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &pPos : qAsConst(participantPositions))
|
|
{
|
|
if (m_handlerParticipant->isOutdated(pPos)) // Participant Position is not valid Todo Magic Number
|
|
continue;
|
|
DEBUG_LOG(m_logger) << "pPos.participantId=" << pPos.participantId();
|
|
if (pPos.vulnerabilityState == ProjectDataManagement::VulnerabilityState_def::CatastrophicKill)
|
|
continue;
|
|
DEBUG_LOG(m_logger) << "pPos.vulnerabilityState=" << pPos.vulnerabilityState;
|
|
if (pPos.insideBuilding())
|
|
continue;
|
|
DEBUG_LOG(m_logger) << "pPos.insideBuilding=" << pPos.insideBuilding();
|
|
DEBUG_LOG(m_logger) << "calculateImpacts";
|
|
calculateImpacts(aesItemViewUtm, bitsToCheck, pPos);
|
|
}
|
|
|
|
} // END calculateImpacts
|
|
|
|
// calculate all impacts of the field for a participant
|
|
void HandlerExplosions::calculateImpacts(const CentralEffectSimulation::AesItemViewUtm &itemView,
|
|
const QVector<int> &impactsToCheck,
|
|
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition)
|
|
{
|
|
DEBUG_LOG(m_logger) << "calculateImpacts";
|
|
|
|
// get radius of munition
|
|
const double impactRadius = m_ddsHandler->chargeTypeRadius(itemView.chargeStruct);
|
|
const QPointF &participantPosUtmPoint = CentralEffectSimulation::geographic2Utm(participantPosition.pos(), itemView.zone);
|
|
|
|
DEBUG_LOG(m_logger) << "XXXXXXXXXXXX calculateImpacts2a impactsToCheck.size " << impactsToCheck.size();
|
|
|
|
for (int bitFieldIdx : qAsConst(impactsToCheck))
|
|
{
|
|
if (itemView.form.size() <= bitFieldIdx)
|
|
continue;
|
|
|
|
// get position of impact to corresponding bit
|
|
const QPointF &impactPos = itemView.form.value(bitFieldIdx);
|
|
|
|
DEBUG_LOG(m_logger) << "XXXXXXXXXXXX calculateImpacts2b check impactPos: x= " << impactPos.x() << " y=" << impactPos.y();
|
|
|
|
if (inCircle(impactPos, impactRadius, participantPosUtmPoint))
|
|
sendEffectTypeEvent(itemView, participantPosition, EffectType::ExplosionArea, m_ddsHandler->currentPartitionTime());
|
|
} // foreach Impact
|
|
|
|
}
|
|
|
|
void HandlerExplosions::calculateImpactsEffect(const CentralEffectSimulation::AesItemViewUtm &itemView,
|
|
const QVector<LtsCoordTrans::LtsCoordinate> &triggeredEffectsLtsCoord,
|
|
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition,
|
|
Options lineOfSightOptions)
|
|
{
|
|
const LtsCoordinate &partLtsCoordinate = participantPosition.ltsCoordinate(); // WITH zValue
|
|
|
|
const int participantId = participantPosition.participantId();
|
|
INFO_LOG(m_logger) << "calculateImpactsEffect" << " START"
|
|
<< ", participantId = " << participantId
|
|
<< ", participantPosition.ltsCoordinate = '" << participantPosition.ltsCoordinate().toString() << "'"
|
|
<< ", triggeredEffectsLtsCoord.size() = " << triggeredEffectsLtsCoord.size()
|
|
<< ", -> partLtsCoordinate " << partLtsCoordinate
|
|
<< ", lineOfSightOptions " << lineOfSightOptions
|
|
<< ", chargeStruct= " << itemView.chargeStruct;
|
|
|
|
if (triggeredEffectsLtsCoord.empty())
|
|
return;
|
|
|
|
|
|
// get radius of munition
|
|
const ProjectDataManagement::GearClass &gearClass = m_ddsHandler->dtcPdm()->gearClassFromChargeTypeId(itemView.chargeStruct.chargeTypeId());
|
|
const bool bIsAirSupport = (gearClass.underlying() == ProjectDataManagement::GearClass_def::AirSupport);
|
|
|
|
const QVector<double> &rangesInMeter = chargeRanges(itemView.chargeStruct);
|
|
DEBUG_LOG(m_logger) << "calculateImpactsEffect" << " chargeRanges:"
|
|
<< ", chargeTypeId = " << itemView.chargeStruct.chargeTypeId()
|
|
<< ", gearClass = " << gearClass
|
|
<< ", bIsAirSupport = " << bIsAirSupport
|
|
<< ", chargeTypeId = " << itemView.chargeStruct.chargeTypeId()
|
|
<< ", ranges.size = " << rangesInMeter.size();
|
|
|
|
Q_ASSERT(!rangesInMeter.isEmpty());
|
|
|
|
// Der z-Wert duerfte der Unterkante des Participants entsprechen.
|
|
// Wir heben pauschal um 1.0m an, um leichte Bodenunebenheiten zu kompensieren:
|
|
// Muss dies evtl. Typ-anhaengig geschehen?
|
|
LtsCoordinate shiftedParticipantLtsCoord = partLtsCoordinate; // WITH zValue
|
|
shiftedParticipantLtsCoord.rz() += 1.0; // shift 1.0m
|
|
|
|
DEBUG_LOG(m_logger) << "shiftedParticipantLtsCoord = " << shiftedParticipantLtsCoord.toString(true);
|
|
|
|
for (const LtsCoordinate &newTriggeredImpactLtsCoord : qAsConst(triggeredEffectsLtsCoord))
|
|
{
|
|
// const int zoneToUse = itemView.zone;
|
|
// 2D or 3D??
|
|
const double planarDistancePartToImpact = newTriggeredImpactLtsCoord.distanceUtm2D(partLtsCoordinate);
|
|
// is distance > max range?
|
|
if (planarDistancePartToImpact > rangesInMeter.back())
|
|
continue;
|
|
|
|
DEBUG_LOG(m_logger) << " planarDistancePartToImpact = " << planarDistancePartToImpact
|
|
<< ", bIsAirSupport = " << bIsAirSupport;
|
|
|
|
// Wir heben pauschal um 1.0m an, um leichte Bodenunebenheiten zu kompensieren:
|
|
LtsCoordinate shiftedNewTriggeredImpact = newTriggeredImpactLtsCoord; // WITH zValue
|
|
shiftedNewTriggeredImpact.rz() += 1.0; // shift 1.0m
|
|
|
|
//
|
|
// 1 range -> typical explosionCluster
|
|
// == impactRadius
|
|
// 3 ranges -> typical explosionCluster with chargeTypeId of GearClass_def::AirSupport
|
|
// == lethalRadius, nonLethalRadius, hazardDistance
|
|
//
|
|
|
|
// GUEZE-3136 Prüfauftrag - Gefährdungsabstand - Meldung
|
|
if (bIsAirSupport)
|
|
{
|
|
|
|
int effect = 3;
|
|
const RANGE mostInnerRange = getMostInnerRange(planarDistancePartToImpact, itemView.chargeStruct, effect);
|
|
DEBUG_LOG(m_logger) << "getMostInnerRange, participantId=" << participantId
|
|
<< ", planarDistancePartToImpact=" << planarDistancePartToImpact
|
|
<< " -> RANGE=" << toString(mostInnerRange);
|
|
|
|
switch (mostInnerRange)
|
|
{
|
|
case RANGE::HAZARD_DISTANCE:
|
|
{
|
|
effect = 3; // hazardDistance
|
|
DEBUG_LOG(m_logger) << "Sende CES Hit Request: participantId=" << participantId << ", RANGE=" << toString(mostInnerRange) << ", effect=" << effect;
|
|
sendCesHitRequest(itemView, participantPosition, QDateTime::currentMSecsSinceEpoch(),
|
|
effect, ProjectDataManagement::EventType_def::EnterHazardDistance);
|
|
}
|
|
break;
|
|
case RANGE::NON_LETHAL:
|
|
case RANGE::LETHAL:
|
|
{
|
|
// Wenn verdeckt, dann sendCesHitRequest mit EnterHazardDistance:
|
|
bool bLOS = true;
|
|
const bool success = lineOfSight_elevationAndBuilding(m_ddsHandler->getExerciseId(),
|
|
shiftedNewTriggeredImpact,
|
|
shiftedParticipantLtsCoord,
|
|
lineOfSightOptions,
|
|
bLOS,
|
|
m_proxyBuilding);
|
|
INFO_LOG(m_logger) << " lineOfSight_elevationAndBuilding "
|
|
<< ", success=" << success
|
|
<< ", shiftedNewTriggeredImpact = " << shiftedNewTriggeredImpact.toString(true);
|
|
|
|
// check LOS / LineOfSight
|
|
DEBUG_LOG(m_logger) << "calculateImpactsEffect" << " check LOS / LineOfSight bLOS=" << bLOS
|
|
<< " participantId=" << participantId;
|
|
if (bLOS)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Sende CES Hit Request: participantId=" << participantId << ", RANGE=" << toString(mostInnerRange) << ", effect=" << effect;
|
|
sendCesHitRequest(itemView, participantPosition, QDateTime::currentMSecsSinceEpoch(),
|
|
effect, ProjectDataManagement::EventType_def::AirSupport); // AirSupport for both: LETHAL_RADIUS, NONLETHAL_RADIUS
|
|
}
|
|
#ifdef GUEZE_3136_IMPLEMENTED
|
|
else
|
|
{
|
|
DEBUG_LOG(m_logger) << "Sende CES Hit Request: participantId=" << participantId << ", RANGE=" << toString(mostInnerRange) << " (Verdeckung -> EnterHazardDistance)";
|
|
sendCesHitRequest(itemView, participantPosition, QDateTime::currentMSecsSinceEpoch(),
|
|
effect, ProjectDataManagement::EventType_def::EnterHazardDistance);
|
|
}
|
|
#endif // GUEZE_3136_IMPLEMENTED
|
|
} // case
|
|
|
|
case RANGE::OUTSIDE:
|
|
// empty
|
|
;
|
|
|
|
} // switch
|
|
|
|
|
|
|
|
}
|
|
else // if (bIsAirSupport)
|
|
{
|
|
|
|
QElapsedTimer timer;
|
|
DEBUG_LOG(m_logger) << "calculateImpactsEffect" << " vor lineOfSightOptions start";
|
|
if (m_logger.isInfoEnabled())
|
|
timer.start();
|
|
|
|
|
|
bool bLOS = true;
|
|
const bool success = lineOfSight_elevationAndBuilding(m_ddsHandler->getExerciseId(),
|
|
shiftedNewTriggeredImpact,
|
|
shiftedParticipantLtsCoord,
|
|
lineOfSightOptions,
|
|
bLOS,
|
|
m_proxyBuilding);
|
|
INFO_LOG(m_logger) << " lineOfSight_elevationAndBuilding took [ms] " << timer.elapsed()
|
|
<< ", success=" << success;
|
|
|
|
// check LOS / LineOfSight
|
|
DEBUG_LOG(m_logger) << "calculateImpactsEffect" << " check LOS / LineOfSight bLOS=" << bLOS
|
|
<< " participantId=" << participantId;
|
|
if (bLOS)
|
|
{
|
|
INFO_LOG(m_logger) << "calculateImpactsEffect 3 participantId = " << participantId
|
|
<< " -> bInCircle and given LOS -> sendEffectTypeEvent ...";
|
|
sendEffectTypeEvent(itemView, participantPosition, EffectType::ExplosionArea, m_ddsHandler->currentPartitionTime());
|
|
}
|
|
} // if (bIsAirSupport)
|
|
|
|
} // for newTriggeredImpactLtsCoord
|
|
}
|
|
|
|
void HandlerExplosions::calculateImpactsEffect_outdated(const CentralEffectSimulation::AesItemViewUtm &itemView,
|
|
const std::vector<Core::GeoPosition> &triggeredEffectsGeoPos,
|
|
const QVector<QPointF> &triggeredEffectsUtmPos,
|
|
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition,
|
|
Options lineOfSightOptions)
|
|
{
|
|
const LtsCoordTrans::LtsCoordinate &partLtsCoordinate = participantPosition.ltsCoordinate(); // WITH zValue
|
|
|
|
INFO_LOG(m_logger) << LOGFUNCTION << " START"
|
|
<< " participantId = " << participantPosition.participantId()
|
|
<< " participantPosition.ltsCoordinate = '" << participantPosition.ltsCoordinate().toString() << "'"
|
|
<< ", triggeredEffectsGeoPos.size() = " << triggeredEffectsGeoPos.size()
|
|
<< ", triggeredEffectsUtmPos.size() = " << triggeredEffectsUtmPos.size()
|
|
<< ", participantPosition = " << participantPosition
|
|
<< ", -> partLtsCoordinate " << partLtsCoordinate
|
|
<< ", lineOfSightOptions " << lineOfSightOptions;
|
|
|
|
INFO_LOG(m_logger) << LOGFUNCTION << participantPosition;
|
|
|
|
if (triggeredEffectsGeoPos.empty())
|
|
return;
|
|
|
|
if (static_cast<int>(triggeredEffectsGeoPos.size()) != triggeredEffectsUtmPos.size())
|
|
return;
|
|
|
|
DEBUG_LOG(m_logger) << "calculateImpactsEffect, triggeredEffectsUtmPos.size: " << triggeredEffectsGeoPos.size()
|
|
<< " participantPosition = " << participantPosition;
|
|
|
|
|
|
// get radius of munition
|
|
const double impactRadius = m_ddsHandler->chargeTypeRadius(itemView.chargeStruct);
|
|
|
|
// Der z-Wert duerfte der Unterkante des Participants entsprechen.
|
|
// Wir heben pauschal um 0.5m an.
|
|
// Muss dies evtl. Typ-anhaengig geschehen?
|
|
Core::GeoPosition participantPositionGeoPos = partLtsCoordinate.geoPosition(); // WITH zValue
|
|
participantPositionGeoPos.z() += 0.5; // shift 0.5m
|
|
|
|
const QPointF &participantPosUtmPoint = CentralEffectSimulation::geographic2Utm(participantPositionGeoPos, itemView.zone);
|
|
DEBUG_LOG(m_logger) << " participantPositionGeoPos=" << participantPositionGeoPos.lon() << "/" << participantPositionGeoPos.lat()
|
|
<< " itemView.zone=" << itemView.zone
|
|
<< " participantPosUtmPoint=" << participantPosUtmPoint.x() << "/" << participantPosUtmPoint.y();
|
|
|
|
DEBUG_LOG(m_logger) << "calculateImpactsEffect 1 triggeredEffectsUtmPos.size " << triggeredEffectsUtmPos.size();
|
|
|
|
int idx = -1;
|
|
for (const QPointF &newTriggeredImpactUtm : qAsConst(triggeredEffectsUtmPos))
|
|
{
|
|
++idx;
|
|
|
|
const bool bInCircle = inCircle(newTriggeredImpactUtm, impactRadius, participantPosUtmPoint);
|
|
|
|
DEBUG_LOG(m_logger) << "calculateImpactsEffect 2a check impactPos: x= " << newTriggeredImpactUtm.x() << " y=" << newTriggeredImpactUtm.y()
|
|
<< " participantPosUtmPoint=" << participantPosUtmPoint.x() << "/" << participantPosUtmPoint.y()
|
|
<< " impactRadius=" << impactRadius
|
|
<< ", inCircle = " << bInCircle
|
|
<< " participantId=" << participantPosition.participantId();
|
|
|
|
if (!bInCircle)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Not in Circle => continue";
|
|
continue;
|
|
}
|
|
|
|
const Core::GeoPosition &newTriggeredImpactGeo = triggeredEffectsGeoPos[idx];
|
|
const int exerciseId = m_ddsHandler->getExerciseId();
|
|
|
|
QElapsedTimer timer;
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " vor lineOfSightOptions start";
|
|
if (m_logger.isInfoEnabled())
|
|
timer.start();
|
|
|
|
bool bLOS = true;
|
|
const bool success = lineOfSight_elevationAndBuilding(exerciseId,
|
|
LtsCoordTrans::LtsCoordinate(newTriggeredImpactGeo),
|
|
LtsCoordTrans::LtsCoordinate(participantPositionGeoPos),
|
|
lineOfSightOptions,
|
|
bLOS,
|
|
m_proxyBuilding);
|
|
INFO_LOG(m_logger) << " lineOfSight_elevationAndBuilding took [ms] " << timer.elapsed()
|
|
<< " success=" << success;
|
|
|
|
// check LOS / LineOfSight
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " check LOS / LineOfSight bLOS=" << bLOS
|
|
<< " participantId=" << participantPosition.participantId();
|
|
if (bLOS)
|
|
{
|
|
INFO_LOG(m_logger) << "calculateImpactsEffect 3 participantId = " << participantPosition.participantId()
|
|
<< " -> bInCircle and given LOS -> sendEffectTypeEvent ...";
|
|
sendEffectTypeEvent(itemView, participantPosition, EffectType::ExplosionArea, m_ddsHandler->currentPartitionTime());
|
|
}
|
|
|
|
} // foreach newTriggeredImpactUtm
|
|
}
|
|
|
|
// Anmerkung: Im SFM werden Quadrate und keine Kreise genommen. ( Wird hier nach Absprache mit Manfred nicht gemacht)
|
|
bool HandlerExplosions::inCircle(const QPointF ¢erUtm, const double radius, const QPointF &pointUtm)
|
|
{
|
|
double squareDist = getSquareDistance(centerUtm, pointUtm);
|
|
double squareRadius = radius * radius;
|
|
DEBUG_LOG(m_logger) << "squareDist: " << squareDist << ", squareRadius: " << squareRadius
|
|
<< " radius=" << radius
|
|
<< " inCircle=" << (squareDist < squareRadius)
|
|
<< " centerUtm=" << centerUtm.x() << "/" << centerUtm.y()
|
|
<< " pointUtm=" << pointUtm.x() << "/" << pointUtm.y();
|
|
return (squareDist < squareRadius);
|
|
}
|
|
|
|
QVector<double> HandlerExplosions::chargeRanges(const AreaEffectSimulation::ChargeStruct &chargeStruct) const
|
|
{
|
|
const ProjectDataManagement::GearClass &gearClass = m_ddsHandler->dtcPdm()->gearClassFromChargeTypeId(chargeStruct.chargeTypeId());
|
|
DEBUG_LOG(m_logger) << "chargeTypeId = " << chargeStruct.chargeTypeId() << ", gearClass = " << gearClass;
|
|
|
|
if (gearClass.underlying() == ProjectDataManagement::GearClass_def::AirSupport)
|
|
{
|
|
const double lethalRadius = m_ddsHandler->getLethalRadius(chargeStruct);
|
|
const double nonLethalRadius = m_ddsHandler->getNonLethalRadius(chargeStruct);
|
|
const double hazardDistance = m_ddsHandler->getHazardDistance(chargeStruct);
|
|
return {lethalRadius, nonLethalRadius, hazardDistance};
|
|
}
|
|
|
|
const double impactRadius = m_ddsHandler->chargeTypeRadius(chargeStruct);
|
|
return {impactRadius};
|
|
}
|
|
|
|
int HandlerExplosions::iedClassToEffect(int iedClass) const
|
|
{
|
|
switch (iedClass)
|
|
{
|
|
case 1:
|
|
return 48;
|
|
case 2:
|
|
return 49;
|
|
case 3:
|
|
return 68;
|
|
case 4:
|
|
return 69;
|
|
case 5:
|
|
return 70;
|
|
case 255:
|
|
return 255;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
RANGE HandlerExplosions::getMostInnerRange(const QVector<double> &ranges, double currentDistance)
|
|
{
|
|
static_assert((int)RANGE::LETHAL == 1, "RANGE mismatch");
|
|
static_assert((int)RANGE::NON_LETHAL == 2, "RANGE mismatch");
|
|
static_assert((int)RANGE::HAZARD_DISTANCE == 3, "RANGE mismatch");
|
|
static_assert((int)RANGE::OUTSIDE == 4, "RANGE mismatch");
|
|
|
|
int cnt = 4; // OUTSIDE
|
|
|
|
QVector<double>::const_reverse_iterator rit = ranges.crbegin();
|
|
while (rit != ranges.crend())
|
|
{
|
|
double currentGearRange = *rit;
|
|
if (currentGearRange == 0)
|
|
{
|
|
--cnt;
|
|
++rit;
|
|
}
|
|
if (currentDistance < currentGearRange )
|
|
--cnt;
|
|
else
|
|
break;
|
|
++rit;
|
|
}
|
|
return static_cast<RANGE>(cnt);
|
|
}
|
|
|
|
RANGE HandlerExplosions::getMostInnerRange(double currentDistance, const AreaEffectSimulation::ChargeStruct &chargeStruct, int &retEffect)
|
|
{
|
|
int hazard_Distance = m_ddsHandler->dtcPdm()->hazardDistance(chargeStruct.chargeTypeId(), chargeStruct.igniterTypeId(), chargeStruct.igniterFunctionId());
|
|
if (currentDistance > hazard_Distance)
|
|
return RANGE::OUTSIDE;
|
|
int lastRange = 9999;
|
|
ProjectDataManagement::SeverityType lastST = ProjectDataManagement::SeverityType::noEffect;
|
|
for (const auto &casEffect : m_ddsHandler->getCasEffects(chargeStruct))
|
|
{
|
|
if (currentDistance < casEffect.impactRadius() && casEffect.impactRadius() < lastRange)
|
|
{
|
|
lastRange = casEffect.impactRadius();
|
|
lastST = casEffect.severityType();
|
|
retEffect = iedClassToEffect(casEffect.iedClass());
|
|
}
|
|
}
|
|
|
|
switch (lastST.underlying())
|
|
{
|
|
case ProjectDataManagement::SeverityType::noEffect:
|
|
return RANGE::HAZARD_DISTANCE;
|
|
case ProjectDataManagement::SeverityType::lethal:
|
|
return RANGE::LETHAL;
|
|
case ProjectDataManagement::SeverityType::light:
|
|
case ProjectDataManagement::SeverityType::medium:
|
|
case ProjectDataManagement::SeverityType::heavy:
|
|
return RANGE::NON_LETHAL;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return RANGE::OUTSIDE;
|
|
}
|
|
|
|
void HandlerExplosions::sendEffectTypeEvent(const CentralEffectSimulation::AesItemViewUtm &itemView,
|
|
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition,
|
|
EffectType effectType,
|
|
qint64 effectTime)
|
|
{
|
|
Q_UNUSED(effectTime);
|
|
// Trefferereignis Sekundaerwirkung
|
|
|
|
const int ctasEffectType = static_cast<int>(effectType) & 0xffff;
|
|
const int participantId = participantPosition.participantId();
|
|
DEBUG_LOG(m_logger) << "Sending EffectType event " << ctasEffectType
|
|
<< ", participant: " << participantId
|
|
<< ", aesItemId: " << itemView.aesItemId
|
|
<< ", ctasEffectType: " << ctasEffectType;
|
|
sendCesHitRequest(itemView,
|
|
participantPosition,
|
|
//effectTime >= 0 ? effectTime : participantPosition.sampleTime(),
|
|
QDateTime::currentMSecsSinceEpoch(),
|
|
ctasEffectType,
|
|
ProjectDataManagement::EventType_def::HitEffectSimulation);
|
|
}
|
|
|
|
// Send via TAB-Proxy to battlefield
|
|
void HandlerExplosions::sendCesHitRequest(const CentralEffectSimulation::AesItemViewUtm &itemView,
|
|
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition,
|
|
const qint64 effectTime,
|
|
const int remaining,
|
|
const ProjectDataManagement::EventType eventType)
|
|
{
|
|
int32_t legacyChargeTypeId = 0;
|
|
legacyChargeTypeId = m_ddsHandler->getLegacyIdFromChargeTypeId(itemView.chargeStruct);
|
|
// [E01911-49] This is just a band-aid.
|
|
if (m_ddsHandler->isSmartChargeType(itemView.chargeStruct))
|
|
legacyChargeTypeId = 0;
|
|
if (remaining < 0)
|
|
return;
|
|
|
|
DEBUG_LOG(m_logger) << "sendCesHitRequest"
|
|
<< " participantId=" << participantPosition.participantId()
|
|
<< " remaining=" << remaining
|
|
<< " eventType=" << eventType
|
|
<< " chargeTypeId=" << itemView.chargeStruct.chargeTypeId();
|
|
|
|
// Bei Nebel soll die Weste nur einmal piepen und nicht regelmaessig. Das wird mit Wirkung = 3 uebertragen
|
|
int effect = remaining;
|
|
if ((m_ddsHandler->isSmoke(itemView.chargeStruct)) && (remaining == 0))
|
|
effect = 3;
|
|
|
|
|
|
const int equipNo = m_ddsHandler->participantId2EquipNo(participantPosition.participantId());
|
|
DEBUG_LOG(m_logger) << " ftc sendCESHit to Participant participantId = " << participantPosition.participantId()
|
|
<< " equipNo = " << equipNo
|
|
<< " aesItemId =" << itemView.aesItemId;
|
|
|
|
// participantPosition.participantPosition == 0 -> send to all
|
|
if ((participantPosition.participantId() != 0) && (equipNo <= 0))
|
|
{
|
|
DEBUG_LOG(m_logger) << "participantPosition.participantId != 0 && equipNo <= 0" ;
|
|
return;
|
|
}
|
|
if (participantPosition.participantId() != 0 &&
|
|
participantPosition.vulnerabilityState == ProjectDataManagement::VulnerabilityState_def::CatastrophicKill)
|
|
{
|
|
DEBUG_LOG(m_logger) << "participantPosition.participantId != 0 CatastrophicKill)";
|
|
return;
|
|
}
|
|
|
|
#ifdef ORIGINATOR_AT_SENDCESHITREQUEST
|
|
const int32_t originatorParticipantId = m_aesItemId2partIdMap.value(itemView.aesItemId, 0);
|
|
const int originatorEquipNo = m_ddsHandler->participantId2EquipNo(originatorParticipantId);
|
|
#else
|
|
const int originatorEquipNo = 0;
|
|
#endif // ORIGINATOR_AT_SENDCESHITREQUEST
|
|
|
|
const TrainingAreaBase::TabPosition pos(participantPosition.pos(),
|
|
Core::PositionReportingSource_def::Gps, // irrelevant
|
|
participantPosition.buildingId(),
|
|
participantPosition.roomId());
|
|
|
|
// mineFieldBitIndex wird als reserve1 uegertragen und hier wird die Wirkdauer uebertragen.
|
|
const int duration = m_ddsHandler->dtcAes().get()->explosionCluster(itemView.aesItemId).effectDuration() / 1000;
|
|
|
|
// mineFieldBitIndex wird als reserve2 uebertragen und hier wird die Wirkbeginn uebertragen.
|
|
int effectTime2 = 0;
|
|
if (eventType == ProjectDataManagement::EventType::EnterHazardDistance)
|
|
{
|
|
const auto &ec = m_ddsHandler->dtcAes().get()->explosionCluster(itemView.aesItemId);
|
|
if (ec.effectStart.is_set())
|
|
effectTime2 = ec.effectStart.get() / 1000; // msec to sec
|
|
}
|
|
|
|
|
|
TrainingAreaBase::SendCesHitRequest requestTab(
|
|
equipNo, // equipmentNumber (injured/damaged)
|
|
participantPosition.participantId(), // participantId
|
|
legacyChargeTypeId > 0 ? legacyChargeTypeId : itemView.chargeStruct.chargeTypeId(), // chargeTypeId
|
|
itemView.aesItemId, // aesId
|
|
originatorEquipNo, // originatorEquipmentNumber
|
|
effect, // Wirkung
|
|
pos,
|
|
effectTime2 > 0 ? effectTime2 : effectTime, // effectTime
|
|
eventType,
|
|
itemView.partyId, // originPartyId
|
|
duration
|
|
);
|
|
DEBUG_LOG(m_logger) << " participantId=" << participantPosition.participantId()
|
|
<< ", chargeTypeId=" << itemView.chargeStruct.chargeTypeId()
|
|
<< ", legacyChargeTypeId=" << legacyChargeTypeId
|
|
<< ", m_sendCesHitRequestQueue.size()=" << m_sendCesHitRequestQueue.size()
|
|
<< ", duration=" << duration;
|
|
|
|
DEBUG_LOG(m_logger) << "ftcTab->SendCesHitRequest: " << requestTab;
|
|
m_ddsHandler->ftcTab()->sendCesHitRequest(requestTab, this);
|
|
if (!m_ddsHandler->isSmartChargeType(itemView.chargeStruct))
|
|
sendParticipantEvent(m_aesItemViewsUtm.value(itemView.aesItemId), participantPosition, ProjectDataManagement::EventType_def::HitEffectSimulation);
|
|
}
|
|
|
|
double HandlerExplosions::getSquareDistance(const QPointF ¢erUtm, const QPointF &pointUtm)
|
|
{
|
|
QPointF p(pointUtm - centerUtm);
|
|
return p.x() * p.x() + p.y() * p.y();
|
|
}
|
|
|
|
void HandlerExplosions::on_newHazardInfo(int participantId, int aesId)
|
|
{
|
|
DEBUG_LOG(m_logger) << "HandlerFunctionalAreas::on_HazardFinished participantId=" << participantId
|
|
<< " aesId =" << aesId
|
|
<< " TODO: Nach Start des Wirkfeldes müssen Teilnehmer alle 30 Sekunden bis Ablauf des Wirkfelds darüber informiert werden, dass sie sich im Gefährdungsabstand befinden";
|
|
|
|
}
|
|
|
|
void HandlerExplosions::sendEnterHazardDistance(const int participantId, const int aesItemId)
|
|
{
|
|
DEBUG_LOG(m_logger) << "sendEnterHazardDistance: participantId=" << participantId
|
|
<< " aesItemId=" << aesItemId;
|
|
DEBUG_LOG(m_logger) << "sendCesHitRequest";
|
|
const int ctasEffectType = 0;
|
|
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &cesParticipantData = m_handlerParticipant->position(participantId);
|
|
|
|
m_enteredHazardDistanceStore.participantEnteringArea(participantId, aesItemId);
|
|
|
|
ProjectDataManagement::EventType eventType = ProjectDataManagement::EventType_def::EnterHazardDistance;
|
|
sendCesHitRequest(m_aesItemViewsUtm.value(aesItemId),
|
|
cesParticipantData,
|
|
QDateTime::currentMSecsSinceEpoch(),
|
|
ctasEffectType,
|
|
eventType);
|
|
sendParticipantEvent(m_aesItemViewsUtm.value(aesItemId), cesParticipantData, ProjectDataManagement::EventType_def::EnterHazardDistance);
|
|
}
|
|
|
|
void HandlerExplosions::sendLeaveHazardDistance(const int participantId, const int aesItemId, const int remaining)
|
|
{
|
|
DEBUG_LOG(m_logger) << "sendLeaveHazardDistance: participantId=" << participantId
|
|
<< " aesItemId=" << aesItemId;
|
|
DEBUG_LOG(m_logger) << "sendCesHitRequest"
|
|
<< " aesItemId=" << aesItemId;
|
|
|
|
const int ctasEffectType = remaining;
|
|
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &cesParticipantData = m_handlerParticipant->position(participantId);
|
|
const CentralEffectSimulation::AesItemViewUtm itemView = m_aesItemViewsUtm.value(aesItemId);
|
|
|
|
m_enteredHazardDistanceStore.participantLeavingArea(participantId, aesItemId);
|
|
|
|
sendCesHitRequest(itemView,
|
|
cesParticipantData,
|
|
QDateTime::currentMSecsSinceEpoch(),
|
|
ctasEffectType,
|
|
ProjectDataManagement::EventType_def::LeaveHazardDistance);
|
|
sendParticipantEvent(itemView, cesParticipantData, ProjectDataManagement::EventType_def::LeaveHazardDistance);
|
|
}
|
|
|
|
void HandlerExplosions::sendParticipantEvent(const CentralEffectSimulation::AesItemViewUtm &item,
|
|
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition,
|
|
const ProjectDataManagement::EventType eventType)
|
|
{
|
|
DEBUG_LOG(m_logger) << "sendParticipantEvent participantId=" << participantPosition.participantId();
|
|
ParticipantManagement::TypeClass typeClass = m_handlerParticipant->typeClassFromTypeId(participantPosition.participantTypeId());
|
|
|
|
if (typeClass == ParticipantManagement::TypeClass_def::Unit ||
|
|
typeClass == ParticipantManagement::TypeClass_def::SupportUnit ||
|
|
typeClass == ParticipantManagement::TypeClass_def::SupportEntity ||
|
|
typeClass == ParticipantManagement::TypeClass_def::Unknown)
|
|
return;
|
|
DEBUG_LOG(m_logger) << " sendParticipantEvent participantId=" << participantPosition.participantId();
|
|
|
|
int equipmentNumber = m_ddsHandler->participantId2EquipNo(participantPosition.participantId());
|
|
if (equipmentNumber <= 0)
|
|
return;
|
|
DEBUG_LOG(m_logger) << " sendParticipantEvent participantId=" << participantPosition.participantId();
|
|
|
|
//const AreaEffectSimulation::OffRouteMine &of = m_ddsHandler->dtcAes()->offRouteMine(offRouteItem.aesItemId);
|
|
const AreaEffectSimulation::ExplosionCluster &explosionCluster = m_ddsHandler->dtcAes()->explosionCluster(item.aesItemId);
|
|
|
|
|
|
CentralEffectSimulation::ParticipantEvent pe;
|
|
|
|
DEBUG_LOG(m_logger) << " sendParticipantEvent participantId=" << participantPosition.participantId()
|
|
<< " lat= " << participantPosition.pos().lat()
|
|
<< " lon= " << participantPosition.pos().lon()
|
|
<< " equipmentNumber=" << equipmentNumber
|
|
<< " eventType=" << eventType
|
|
<< " Name=" << explosionCluster.name()
|
|
<< " position=" << participantPosition.pos().lat() << "/" << participantPosition.pos().lon();
|
|
pe.id(m_ddsHandler->getParticipantEventId());
|
|
pe.eventTypeId(eventType);
|
|
pe.participantId(participantPosition.participantId());
|
|
pe.eventTime(participantPosition.sampleTime());
|
|
pe.aesItemId(item.aesItemId);
|
|
pe.partyId(participantPosition.partyId());
|
|
pe.aesName(explosionCluster.name());
|
|
pe.equipmentNumber(equipmentNumber);
|
|
pe.friendlyFire(item.partyId == participantPosition.partyId());
|
|
pe.position(participantPosition.pos());
|
|
|
|
m_ddsHandler->dtsCes()->publish(pe);
|
|
qint64 id = pe.id();
|
|
QTimer::singleShot(1 * 60 * 1000, this, [id, this]() { m_ddsHandler->dtsCes()->removeParticipantEventByKey(id); });
|
|
}
|
|
|
|
|
|
//
|
|
// class HandledImpactsStorage::Private
|
|
//
|
|
class EnteredHarzardDistanceStorage::Private
|
|
{
|
|
public:
|
|
Private() = default;
|
|
~Private() = default;
|
|
|
|
// data:
|
|
QHash<int, QSet<int>> m_participantId2aesItemId; // Key = participantId ; entered areas
|
|
};
|
|
|
|
//
|
|
// class HandledImpactsStorage
|
|
//
|
|
EnteredHarzardDistanceStorage::EnteredHarzardDistanceStorage()
|
|
: d(new Private)
|
|
{}
|
|
|
|
EnteredHarzardDistanceStorage::~EnteredHarzardDistanceStorage()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void EnteredHarzardDistanceStorage::participantEnteringArea(int participantId, int areaId)
|
|
{
|
|
// Key = participantId ; entered areas
|
|
d->m_participantId2aesItemId[participantId].insert(areaId); // QSet: no duplicate
|
|
}
|
|
|
|
void EnteredHarzardDistanceStorage::participantLeavingArea(int participantId, int areaId)
|
|
{
|
|
// Key = participantId ; entered areas
|
|
const auto &areaIt = d->m_participantId2aesItemId.find(participantId);
|
|
if (areaIt != d->m_participantId2aesItemId.end())
|
|
areaIt->remove(areaId);
|
|
}
|
|
|
|
bool EnteredHarzardDistanceStorage::hasParticipantAlreadyEnteredArea(int participantId, int areaId) const
|
|
{
|
|
// Key = participantId ; entered areas
|
|
const auto &areaIt = d->m_participantId2aesItemId.find(participantId);
|
|
if (areaIt != d->m_participantId2aesItemId.end())
|
|
return areaIt->contains(areaId);
|
|
return false;
|
|
}
|
|
|
|
void EnteredHarzardDistanceStorage::removeParticipant(int participantId)
|
|
{
|
|
// Key = participantId ; entered areas
|
|
d->m_participantId2aesItemId.remove(participantId); // QSet:
|
|
}
|
|
|
|
void EnteredHarzardDistanceStorage::removeArea(int aesItemId)
|
|
{
|
|
// Key = participantId ; entered areas
|
|
QMutableHashIterator<int, QSet<int>> it(d->m_participantId2aesItemId);
|
|
while (it.hasNext())
|
|
{
|
|
it.next();
|
|
QSet<int> &aesItemsRef = it.value();
|
|
aesItemsRef.remove(aesItemId); // is based on indxOf ...
|
|
} // while
|
|
}
|
|
|
|
QVector<int> EnteredHarzardDistanceStorage::allParticipantIdsOfArea(int areaId) const
|
|
{
|
|
// Key = participantId ; entered areas
|
|
QVector<int> allIds;
|
|
allIds.reserve(d->m_participantId2aesItemId.size());
|
|
|
|
QHashIterator<int, QSet<int>> it(d->m_participantId2aesItemId);
|
|
while (it.hasNext())
|
|
{
|
|
it.next();
|
|
const QSet<int> &aesItemsRef = it.value();
|
|
if (aesItemsRef.contains(areaId))
|
|
allIds.push_back(it.key());
|
|
} // while
|
|
return allIds;
|
|
}
|
|
|
|
QString toString(RANGE range)
|
|
{
|
|
switch (range)
|
|
{
|
|
case RANGE::LETHAL: return QStringLiteral("LETHAL");
|
|
case RANGE::NON_LETHAL: return QStringLiteral("NON_LETHAL");
|
|
case RANGE::HAZARD_DISTANCE: return QStringLiteral("HAZARD_DISTANCE");
|
|
case RANGE::OUTSIDE: return QStringLiteral("OUTSIDE");
|
|
}
|
|
return QStringLiteral("RANGE_Error");
|
|
}
|
|
|
|
} // namespace CesExplosions
|