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

1538 lines
65 KiB
C++
Executable File

/**
* Handler for player data
* $Id$
* $HeadURL$
* $Author$
* Created by gr, SLSE, 2017-02-20
*/
#include "handler_participant_area.h"
#include "settings.h"
#include "CentralEffectSimulation/handler_participant.h"
#include <LtsUtilsCore/dds_bitarray.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <ProjectDataManagement/pdm_enums.hpp>
#include <cmath>
#include <QTimer>
#include <QDateTime>
namespace dds
{
namespace core
{
inline uint qHash(ProjectDataManagement::EventType et)
{
return ::qHash(et.underlying());
}
}
}
namespace Ces2D
{
inline uint qHash(EffectType et)
{
return ::qHash(int(et));
}
QString toString(EffectType et)
{
switch (et)
{
case EffectType::Unknown: return QStringLiteral("Unknown");
case EffectType::EffectiveMineRangeEnter: return QStringLiteral("EffectiveMineRangeEnter");
case EffectType::EffectiveMineRangeLeave: return QStringLiteral("EffectiveMineRangeLeave");
case EffectType::NContaminationLeave: return QStringLiteral("NContaminationLeave");
case EffectType::NContaminationEnterArea1: return QStringLiteral("NContaminationEnterArea1");
case EffectType::NContaminationEnterArea2: return QStringLiteral("NContaminationEnterArea2");
case EffectType::NContaminationDetonation: return QStringLiteral("NContaminationDetonation");
case EffectType::FunctionalAreaLeave: return QStringLiteral("FunctionalAreaLeave");
case EffectType::FunctionalAreaEnter: return QStringLiteral("FunctionalAreaEnter");
case EffectType::ExplosionArea: return QStringLiteral("ExplosionArea");
case EffectType::ExplosionSingle: return QStringLiteral("ExplosionSingle");
case EffectType::BCContaminationLeave: return QStringLiteral("BCContaminationLeave");
case EffectType::BCContaminationEnterFloating: return QStringLiteral("BCContaminationEnterFloating");
case EffectType::BCContaminationEnterResident: return QStringLiteral("BCContaminationEnterResident");
case EffectType::BCContaminationEnterFloatingArea: return QStringLiteral("BCContaminationEnterFloatingArea");
case EffectType::BeStillWarning: return QStringLiteral("BeStillWarning");
}
return QStringLiteral("Unknown (%1)").arg(int(et));
}
HandlerParticipantArea::HandlerParticipantArea(const std::shared_ptr<CentralEffectSimulation::DdsHandler> &ddsHandler,
const std::shared_ptr<CentralEffectSimulation::HandlerParticipant> &participantHandler,
const std::shared_ptr<HandlerAesData> &aesDataHandler,
const std::shared_ptr<HandlerBuildingModel> &buildingModelHandler,
QObject *parent)
: QObject(parent)
, m_ddsHandler(ddsHandler)
, m_handlerParticipant(participantHandler)
, m_handlerAesData(aesDataHandler)
, m_handlerBuildingModel(buildingModelHandler)
, m_logger(getLogger("HandlerParticipantArea"))
{
} // end of ctor()
//-----------------------------------------------------------------------------
HandlerParticipantArea::~HandlerParticipantArea()
{
} // end of dtor()
//-----------------------------------------------------------------------------
bool HandlerParticipantArea::init()
{
auto dtcAes = m_ddsHandler->dtcAes();
DEBUG_LOG(m_logger) << "Settings::calculateCentralAes() =" << Settings::calculateCentralAes();
dtcAes->enableBCContamination();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::bCContaminationChanged,
this, &HandlerParticipantArea::onBCContaminationChanged);
dtcAes->enableNContamination();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::nContaminationChanged,
this, &HandlerParticipantArea::onNContaminationChanged);
dtcAes->enableTriggeredImpacts();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::triggeredImpactsChanged,
this, &HandlerParticipantArea::onTriggeredImpactsChanged);
dtcAes->enableExplosionArea();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionAreaChanged,
this, &HandlerParticipantArea::onExplosionAreaChanged);
dtcAes->enableExplosionSingle();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionSingleChanged,
this, &HandlerParticipantArea::onExplosionSingleChanged);
dtcAes->enableExplosionSingleCentral();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionSingleCentralChanged,
this, &HandlerParticipantArea::onExplosionSingleCentralChanged);
dtcAes->enableAesItemView();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged,
this, &HandlerParticipantArea::onAesItemViewChanged);
dtcAes->enableDusSingleMine();
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::dusSingleMineChanged,
this, &HandlerParticipantArea::onDusSingleMineChanged);
connect(m_handlerParticipant.get(), &CentralEffectSimulation::HandlerParticipant::newIndoorPosition,
this, &HandlerParticipantArea::calculateAesParticipant);
connect(m_handlerParticipant.get(), &CentralEffectSimulation::HandlerParticipant::newOutdoorPosition,
this, &HandlerParticipantArea::calculateAesParticipant);
// Get 'static' data for all bcContaminations
{
auto cb = [this](const AreaEffectSimulation::BCContamination & bc)
{
onBCContaminationChanged(bc, DataModification::eAdded);
};
if (!dtcAes->bCContaminations(cb))
return false;
}
// Get 'static' data for all explosionAreas
{
auto cb = [this](const AreaEffectSimulation::NContamination & nc)
{
onNContaminationChanged(nc, DataModification::eAdded);
};
if (!dtcAes->nContaminations(cb))
return false;
}
// Get 'static' data for all explosionSingle
{
auto cb = [this](const AreaEffectSimulation::ExplosionSingle & es)
{
onExplosionSingleChanged(es, DataModification::eAdded);
};
if (!dtcAes->explosionSingles(cb))
return false;
}
// Get 'static' data for all explosionSingleCentral
{
auto cb = [this](const AreaEffectSimulation::ExplosionSingleCentral & esc)
{
onExplosionSingleCentralChanged(esc, DataModification::eAdded);
};
if (!dtcAes->explosionSingleCentrals(cb))
return false;
}
// Get 'static' data for all onAesItemView
{
auto cb = [this](const AreaEffectSimulation::AesItemView & aiv)
{
onAesItemViewChanged(aiv, DataModification::eAdded);
};
if (!dtcAes->aesItemViews(cb))
return false;
}
// Get 'static' data for all DusSingleMines
{
auto cb = [this](const AreaEffectSimulation::DusSingleMine & fa)
{
onDusSingleMineChanged(fa, DataModification::eAdded);
};
if (!dtcAes->dusSingleMines(cb))
return false;
}
// Get 'static' data for all explosionAreas
{
auto cb = [this](const AreaEffectSimulation::ExplosionArea & ea)
{
onExplosionAreaChanged(ea, DataModification::eAdded);
};
if (!dtcAes->explosionAreas(cb))
return false;
}
INFO_LOG(m_logger) << "HandlerParticipantArea::init() finished.";
return true;
} // end of init()
//-----------------------------------------------------------------------------
void HandlerParticipantArea::onSendCesHitReply(const Core::FunctionReplyObject &replyObject)
{
DEBUG_LOG(m_logger) << "onSendCesHitReply for id " << replyObject.id() << " success= " << replyObject.error();
if (replyObject.error())
WARNING_LOG(m_logger) << "Can not send enter/leave FunctionsArea to participant in training area: " << replyObject();
// Send next Request, if available
if (!m_sendCesHitRequestQueue.isEmpty())
{
DEBUG_LOG(m_logger) << "m_sendCesAreaActionRequestQueue.size()=" << m_sendCesHitRequestQueue.size();
m_sendCesHitRequestQueue.dequeue();
// Send next Message
if (!m_sendCesHitRequestQueue.isEmpty())
{
const auto &hr = m_sendCesHitRequestQueue.head();
DEBUG_LOG(m_logger) << "sendCesHitRequest: " << hr;
m_ddsHandler->ftcTab()->sendCesHitRequest(hr, this);
}
}
}
// ///////////////////////////////////
// A E S (AreaEffectSimulation)
// ///////////////////////////////////
void HandlerParticipantArea::onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType)
{
// not needed, handled through HandlerParticipantGasPipe
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtGasPipe)
return;
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtUnknown)
return;
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtBoobyTrap)
return;
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtOffRouteMine)
return;
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtOffRouteMineSecondary)
return;
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtMineArea)
return;
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtMineCorridor)
return;
if (m_ddsHandler->isSmartChargeType(data.chargeTypeId()))
{
DEBUG_LOG(m_logger) << "isSmartType = TRUE";
return;
}
const int aesItemId = data.aesItemId();
DEBUG_LOG(m_logger) << "Got AesItemView id: " << aesItemId
<< ", type: " << data.aesTypeId()
<< ", effectState: " << data.effectStateId()
<< ", modType: " << modType;
if (modType == DataModification::eRemoved)
{
aesItemViewRemoved(aesItemId);
return;
}
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtMineAreaSingleMines)
{
// build Polygon Mines:
auto &formMines = m_aesItemViews[data.aesItemId()].formMines;
int &zone = m_aesItemViews[data.aesItemId()].zone;
formMines.clear();
formMines.reserve(data.form().size());
for (const Core::GeoPosition2D &pointGeo : data.form())
formMines.push_back(CentralEffectSimulation::geographic2Utm(pointGeo, &zone));
return;
}
const AreaEffectSimulation::AesEffectState &effectState = data.effectStateId();
const double impactRadius = m_ddsHandler->chargeTypeRadius(data.chargeTypeId());
DEBUG_LOG(m_logger) << "impactRadius=" << impactRadius;
switch (effectState.underlying())
{
case AreaEffectSimulation::AesEffectState_def::EsPrepared:
{
m_aesItemViews.insert(data.aesItemId(), CentralEffectSimulation::AesItemViewUtm::create(data, m_aesItemViews[data.aesItemId()], impactRadius));
break;
}
case AreaEffectSimulation::AesEffectState_def::EsActivated:
{
const auto aesItemViewUtm(CentralEffectSimulation::AesItemViewUtm::create(data, m_aesItemViews[data.aesItemId()], impactRadius));
switch (data.aesTypeId().underlying())
{
case AreaEffectSimulation::AesType_def::AtExplosionArea:
case AreaEffectSimulation::AesType_def::AtExplosionSingle:
case AreaEffectSimulation::AesType_def::AtExplosionAreaSingleExplosions:
case AreaEffectSimulation::AesType_def::AtMineArea:
case AreaEffectSimulation::AesType_def::AtAirSupportImpact:
m_aesItemViews.insert(data.aesItemId(), aesItemViewUtm);
break;
case AreaEffectSimulation::AesType_def::AtBCContamination:
case AreaEffectSimulation::AesType_def::AtNContamination:
// for each participant:
// Bounding-Box Check,
// form-Check
// if new field for this participant send sendCesHitRequest
calculateBcContamination(aesItemViewUtm);
m_aesItemViews.insert(data.aesItemId(), aesItemViewUtm);
break;
case AreaEffectSimulation::AesType_def::AtFunctionalArea:
case AreaEffectSimulation::AesType_def::AtDangerArea:
calculateFunctionArea(aesItemViewUtm);
m_aesItemViews.insert(data.aesItemId(), aesItemViewUtm);
break;
case AreaEffectSimulation::AesType_def::AtUnknown:
case AreaEffectSimulation::AesType_def::AtMineCorridor:
case AreaEffectSimulation::AesType_def::AtBorderline:
case AreaEffectSimulation::AesType_def::AtReconArea:
case AreaEffectSimulation::AesType_def::AtTechnicalArea:
case AreaEffectSimulation::AesType_def::AtDusSingleMine:
case AreaEffectSimulation::AesType_def::AtMineAreaSingleMines:
case AreaEffectSimulation::AesType_def::AtGasPipe:
case AreaEffectSimulation::AesType_def::AtExplosionSingleCentral:
case AreaEffectSimulation::AesType_def::AtExplosionSingleCentralTrajectory:
case AreaEffectSimulation::AesType_def::AtBoobyTrap:
case AreaEffectSimulation::AesType_def::AtBoobyTrapSecondary:
case AreaEffectSimulation::AesType_def::AtOffRouteMine:
case AreaEffectSimulation::AesType_def::AtOffRouteMineSecondary:
break;
} // switch aesTypeId
break;
} // case EsActivated
case AreaEffectSimulation::AesEffectState_def::EsRemoved:
case AreaEffectSimulation::AesEffectState_def::EsExpired:
{
aesItemViewRemoved(aesItemId);
break;
}
default:
// do nothing
break;
} // switch effectStateId
} // onAesItemViewChanged
void HandlerParticipantArea::onDusSingleMineChanged(const AreaEffectSimulation::DusSingleMine &data, DataModification modType)
{
switch (modType)
{
case DataModification::eRemoved:
break;
case DataModification::eAdded:
case DataModification::eModified:
switch (data.effectStateId().underlying())
{
case AreaEffectSimulation::AesEffectState_def::EsActivated:
{
//DEBUG_LOG(m_logger) << "Got onDusSingleMineChanged data.id()=" << data.id();
CentralEffectSimulation::AesItemViewUtm itemView;
itemView.aesItemId = data.id();
itemView.chargeTypeId = data.chargeTypeId();
itemView.partyId = data.partyId().is_set() ? data.partyId().get() : -1;
itemView.aesTypeId = AreaEffectSimulation::AesType_def::AtDusSingleMine;
itemView.effectStart = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch();
itemView.formMines.push_back(CentralEffectSimulation::geographic2Utm(data.position(), &itemView.zone));
itemView.dusMineId = data.mineId();
m_aesItemViews[itemView.aesItemId] = itemView;
AreaEffectSimulation::TriggeredImpacts ti;
ti.aesItemId(itemView.aesItemId);
ti.bitfield().resize(1, 0xff);
m_triggeredImpacts[itemView.aesItemId] = ti;
break;
}
case AreaEffectSimulation::AesEffectState_def::EsPrepared:
case AreaEffectSimulation::AesEffectState_def::EsExpired:
case AreaEffectSimulation::AesEffectState_def::EsRemoved:
m_aesItemViews.remove(data.id());
break;
default:
// Nothing
break;
}
break;
}
}
void HandlerParticipantArea::onBCContaminationChanged(const AreaEffectSimulation::BCContamination &data, DataModification modType)
{
switch (modType)
{
case DataModification::eRemoved:
break;
case DataModification::eAdded:
case DataModification::eModified:
switch (data.effectStateId().underlying())
{
case AreaEffectSimulation::AesEffectState_def::EsReceived:
case AreaEffectSimulation::AesEffectState_def::EsCreated:
case AreaEffectSimulation::AesEffectState_def::EsPrepared:
case AreaEffectSimulation::AesEffectState_def::EsActivated:
{
CentralEffectSimulation::AesItemViewUtm &ref = m_aesItemViews[data.id()];
const auto dtcPdm = m_ddsHandler->dtcPdm();
if (dtcPdm->volatilityTypeId(data.chargeTypeId()) == ProjectDataManagement::VolatilityType_def::Resident)
{
ref.radials = {data.impactRadius()};
ref.center = CentralEffectSimulation::geographic2Utm(data.impactPosition(), &ref.zone);
}
else
ref.radials.clear();
break;
}
default:
break;
}
}
}
void HandlerParticipantArea::onNContaminationChanged(const AreaEffectSimulation::NContamination &data, DataModification modType)
{
switch (modType)
{
case DataModification::eRemoved:
break;
case DataModification::eAdded:
case DataModification::eModified:
switch (data.effectStateId().underlying())
{
case AreaEffectSimulation::AesEffectState_def::EsReceived:
case AreaEffectSimulation::AesEffectState_def::EsCreated:
case AreaEffectSimulation::AesEffectState_def::EsPrepared:
case AreaEffectSimulation::AesEffectState_def::EsActivated:
{
CentralEffectSimulation::AesItemViewUtm &ref = m_aesItemViews[data.id()];
ref.radials = {data.impactRadius(),
data.borderLengthInner(),
data.borderLengthInner() + data.borderLengthOuter()
};
ref.center = CentralEffectSimulation::geographic2Utm(data.impactPosition(), &ref.zone);
break;
}
default:
break;
}
}
}
void HandlerParticipantArea::calculateBcContamination(const CentralEffectSimulation::AesItemViewUtm &itemView)
{
DEBUG_LOG(m_logger) << "HandlerParticipantArea::calculateBcContamination aesItemId =" << itemView.aesItemId;
const auto participantsMap = m_handlerParticipant->positions();
for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPos : participantsMap)
{
if (m_handlerParticipant->isOutdated(participantPos)) // Participant Position is not valid Todo Magic Number
continue;
inFormCheck(itemView, participantPos);
} // for
} // calculateBcContamination
void HandlerParticipantArea::calculateFunctionArea(const CentralEffectSimulation::AesItemViewUtm &itemView)
{
DEBUG_LOG(m_logger) << "HandlerParticipantArea::calculateFunctionArea aesItemId =" << itemView.aesItemId;
const auto partPosMap = m_handlerParticipant->positions();
for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPos : partPosMap)
{
if (m_handlerParticipant->isOutdated(participantPos))
continue;
inFormCheck(itemView, participantPos);
}
} // calculateFunctionArea
void HandlerParticipantArea::deleteFunctionalArea(const int aesItemId)
{
CentralEffectSimulation::AesItemViewUtm itemView = m_aesItemViews.value(aesItemId);
DEBUG_LOG(m_logger) << "delete " << itemView.aesItemId << ", aesItemId: " << aesItemId;
const auto partPosMap = m_handlerParticipant->positions();
for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPos : partPosMap)
{
if (m_handlerParticipant->isOutdated(participantPos))
continue;
// Same as leave FunctionArea
if (itemView.aesTypeId == AreaEffectSimulation::AesType_def::AtFunctionalArea)
{
sendParticipantEvent(itemView, participantPos, ProjectDataManagement::EventType_def::LeaveFunctionalArea);
sendEffectTypeEvent(itemView, participantPos, EffectType::FunctionalAreaLeave, -1, -1);
}
else
sendParticipantEvent(itemView, participantPos, ProjectDataManagement::EventType_def::LeaveDangerArea);
DEBUG_LOG(m_logger) << "Participant inside id=" << participantPos.participantId();
} // for all Participants
} // deleteFunctionalArea
void HandlerParticipantArea::calculateAesParticipant(const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPos)
{
if (m_handlerParticipant->isOutdated(participantPos))
return;
DEBUG_LOG(m_logger) << "HandlerParticipantArea::calculateAesParticipant paricipantId: " << participantPos.participantId();
for (const CentralEffectSimulation::AesItemViewUtm &itemView : qAsConst(m_aesItemViews))
{
DEBUG_LOG(m_logger) << " itemView: " << itemView.aesItemId << ", " << itemView.aesTypeId;
switch (itemView.aesTypeId.underlying())
{
case AreaEffectSimulation::AesType_def::AtMineArea:
if (inFormCheck(itemView, participantPos))
// Check, if Participant is in a circle of mine
inMineRadiusCheck(itemView, participantPos);
else
// no longer in mine area - reset all EffectiveMineRangeEnter mineIndexes
m_effectTypeBitFieldIndexes[itemView.aesItemId][participantPos.participantId()].clear();
break;
case AreaEffectSimulation::AesType_def::AtMineAreaSingleMines:
break;
case AreaEffectSimulation::AesType_def::AtDusSingleMine:
inMineRadiusCheck(itemView, participantPos);
break;
case AreaEffectSimulation::AesType_def::AtBCContamination:
case AreaEffectSimulation::AesType_def::AtNContamination:
case AreaEffectSimulation::AesType_def::AtFunctionalArea:
case AreaEffectSimulation::AesType_def::AtDangerArea:
inFormCheck(itemView, participantPos);
break;
default:
// nothing
break;
} // switch
} // for
} // calculateAesParticipant
// return true, if participant is inside of the dangerous area of a mine
bool HandlerParticipantArea::inMineRadiusCheck(const CentralEffectSimulation::AesItemViewUtm &itemView,
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition)
{
int32_t bitFieldIdx = 0;
const double impactRadius = m_ddsHandler->chargeTypeRadius(itemView.chargeTypeId);
const AreaEffectSimulation::TriggeredImpacts &triggeredImpact = m_triggeredImpacts[itemView.aesItemId];
const QPointF participantPosUtmPoint = CentralEffectSimulation::geographic2Utm(participantPosition.pos(), itemView.zone);
bool bOutOfBounds = false;
for (const auto &itPos : itemView.formMines)
{
DEBUG_LOG(m_logger) << "participantPositionUtm: " << CentralEffectSimulation::debug(participantPosUtmPoint)
<< ", MinePosUtm: " << CentralEffectSimulation::debug(itPos);
// Check, if mine is active
// already triggered (bit is already 0)
if (!LtsUtils::BitArray::testBit(triggeredImpact.bitfield(), bitFieldIdx) && !bOutOfBounds)
{
++bitFieldIdx;
continue;
}
const int mineIdx = (itemView.aesTypeId == AreaEffectSimulation::AesType_def::AtDusSingleMine)
? itemView.dusMineId : bitFieldIdx + 1; // mineIdx in minefield starts at 1
// Check, if participant is inside danger-radius of the mine
if (inCircle(itPos, impactRadius, participantPosUtmPoint))
{
DEBUG_LOG(m_logger) << "Mine Hit for aesItemId: " << itemView.aesItemId
<< ", mineIdx: " << mineIdx
<< ", participantId: " << participantPosition.participantId();
sendEffectTypeEvent(itemView, participantPosition, EffectType::EffectiveMineRangeEnter, mineIdx, -1);
}
else
sendEffectTypeEvent(itemView, participantPosition, EffectType::EffectiveMineRangeLeave, mineIdx, -1);
bitFieldIdx++;
}
return false;
}
// return true, if participant is inside of the bounding Box
bool HandlerParticipantArea::inBoundingBoxCheck(const CentralEffectSimulation::AesItemViewUtm &itemView,
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition)
{
if (itemView.effectStateId != AreaEffectSimulation::AesEffectState_def::EsActivated)
return false;
if (m_handlerParticipant->isOutdated(participantPosition)) // Participant Position is not valid Todo Magic Number
return false;
// QRectF(topLeft, bottomRight) -> no need to mormalize, QRectF:contains() handles it correct
const QRectF boundingBoxImpact(itemView.boundingBoxBottomLeft,
itemView.boundingBoxTopRight);
const QPointF participantPosUtmPoint = CentralEffectSimulation::geographic2Utm(participantPosition.pos(), itemView.zone);
if (boundingBoxImpact.contains(participantPosUtmPoint))
{
DEBUG_LOG(m_logger) << "inBoundingBoxCheck Paricipant " << participantPosition.participantId()
<< " inside AesItemView:" << itemView.aesItemId;
return true;
}
TRACE_LOG(m_logger) << "inBoundingBoxCheck Paricipant " << participantPosition.participantId()
<< " outside AesItemView:" << itemView.aesItemId;
return false;
}
// return true, if participant is inside of the form of AesItemView
bool HandlerParticipantArea::inFormCheck(const CentralEffectSimulation::AesItemViewUtm &itemView,
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition)
{
if (itemView.effectStateId != AreaEffectSimulation::AesEffectState_def::EsActivated)
return false;
if (m_handlerParticipant->isOutdated(participantPosition)) // Participant Position is not valid
return false;
const QRectF boundingBoxImpact(itemView.boundingBoxBottomLeft,
itemView.boundingBoxTopRight);
const QPointF participantPosUtmPoint = CentralEffectSimulation::geographic2Utm(participantPosition.pos(), itemView.zone);
const QString participantPosMgrs = CentralEffectSimulation::geographic2mgrs(participantPosition.pos(), itemView.zone);
DEBUG_LOG(m_logger) << "inFormCheck start, paricipantId: " << participantPosition.participantId()
<< ", aesItemId: " << itemView.aesItemId
<< ", aesTypeId: " << itemView.aesTypeId
<< ", form.size: " << itemView.form.size()
<< ", Mgrs:" << participantPosMgrs;
if (boundingBoxImpact.contains(participantPosUtmPoint) &&
itemView.form.containsPoint(participantPosUtmPoint, Qt::FillRule::OddEvenFill))
{
DEBUG_LOG(m_logger) << "inside AES form, paricipantId: " << participantPosition.participantId()
<< ", aesItemId: " << itemView.aesItemId;
switch (itemView.aesTypeId.underlying())
{
case AreaEffectSimulation::AesType_def::AtBCContamination:
sendEffectTypeEvent(itemView, participantPosition, effectTypeForBCContaminationArea(itemView, participantPosUtmPoint), -1, -1);
break;
case AreaEffectSimulation::AesType_def::AtDangerArea:
sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::EnterDangerArea);
break;
case AreaEffectSimulation::AesType_def::AtExplosionArea:
break;
case AreaEffectSimulation::AesType_def::AtFunctionalArea:
sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::EnterFunctionalArea);
sendEffectTypeEvent(itemView, participantPosition, EffectType::FunctionalAreaEnter, -1, -1);
break;
case AreaEffectSimulation::AesType_def::AtMineArea:
sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::EnterMineArea);
break;
case AreaEffectSimulation::AesType_def::AtNContamination:
sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::EnterNuclear);
sendEffectTypeEvent(itemView, participantPosition, effectTypeForNContaminationArea(itemView, participantPosUtmPoint, participantPosition.sampleTime()), -1, -1);
break;
default:
INFO_LOG(m_logger) << "inFormCheck Unhandled AES-Type" << itemView.aesTypeId;
break;
}
return true;
} // if inside
// outside
switch (itemView.aesTypeId.underlying())
{
case AreaEffectSimulation::AesType_def::AtBCContamination:
sendEffectTypeEvent(itemView, participantPosition, EffectType::BCContaminationLeave, -1, -1);
break;
case AreaEffectSimulation::AesType_def::AtDangerArea:
sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::LeaveDangerArea);
break;
case AreaEffectSimulation::AesType_def::AtExplosionArea:
break;
case AreaEffectSimulation::AesType_def::AtFunctionalArea:
sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::LeaveFunctionalArea);
sendEffectTypeEvent(itemView, participantPosition, EffectType::FunctionalAreaLeave, -1, -1);
break;
case AreaEffectSimulation::AesType_def::AtMineArea:
sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::LeaveMineArea);
break;
case AreaEffectSimulation::AesType_def::AtNContamination:
sendParticipantEvent(itemView, participantPosition, ProjectDataManagement::EventType_def::LeaveNuclear);
sendEffectTypeEvent(itemView, participantPosition, EffectType::NContaminationLeave, -1, -1);
break;
default:
break;
} // end switch
#if 0
if (m_logger.isDebugEnabled())
{
DEBUG_LOG(m_logger) << "inFormCheck: Paricipant " << participantPosition.participantId << " outside AesItemView " << itemView.aesItemId;
DEBUG_LOG(m_logger) << "ParticipantPos = " << debug(participantPosUtmPoint);
for (const auto &itPos : itemView.form)
DEBUG_LOG(m_logger) << "itemView =" << debug(itPos);
}
#endif
return false;
}
void HandlerParticipantArea::onTriggeredImpactsChanged(const AreaEffectSimulation::TriggeredImpacts &data, DataModification modType)
{
if (modType != DataModification::eAdded &&
modType != DataModification::eModified)
return;
DEBUG_LOG(m_logger) << "Got TriggeredImpacts: " << data;
switch (data.aesTypeId().underlying())
{
case AreaEffectSimulation::AesType_def::AtExplosionArea:
case AreaEffectSimulation::AesType_def::AtExplosionSingle:
case AreaEffectSimulation::AesType_def::AtExplosionAreaSingleExplosions:
// explosion at the position of participant
calculateImpacts(data);
break;
case AreaEffectSimulation::AesType_def::AtMineAreaSingleMines:
case AreaEffectSimulation::AesType_def::AtMineArea:
// participant run on mine
// => store Mine-State and take into account when a participant runs to one of this mines
m_triggeredImpacts.insert(data.aesItemId(), data);
break;
default:
//nothigng
break;
}
} // onTriggeredImpactsChanged
void HandlerParticipantArea::onExplosionAreaChanged(const AreaEffectSimulation::ExplosionArea &data, DataModification modType)
{
if (modType != DataModification::eAdded &&
modType != DataModification::eModified)
return;
DEBUG_LOG(m_logger) << "Got ExplosionArea, m_aesItemId2partIdMap.insert " << data.id() << " / " << data.participantId();
if (data.participantId().is_set())
m_aesItemId2partIdMap.insert(data.id(), data.participantId().get());
else
m_aesItemId2partIdMap.remove(data.id());
} // onExplosionAreaChanged
void HandlerParticipantArea::onExplosionSingleChanged(const AreaEffectSimulation::ExplosionSingle &data, DataModification modType)
{
bool handleExplosion = false;
if (m_ddsHandler->isSmartChargeType(data.chargeTypeId()))
{
DEBUG_LOG(m_logger) << "isSmartType = TRUE";
return;
}
if (modType != DataModification::eAdded &&
modType != DataModification::eModified)
return;
if (data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsRemoved)
{
DEBUG_LOG(m_logger) << "Remove SingleExplosion";
m_explosionSingleStates.remove(data.id());
return;
}
// Only handle transitions from prepared or fired to activated
if ((m_explosionSingleStates.value(data.id()) == AreaEffectSimulation::AesEffectState_def::EsPrepared ||
m_explosionSingleStates.value(data.id()) == AreaEffectSimulation::AesEffectState_def::EsFired) &&
data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsActivated)
{
DEBUG_LOG(m_logger) << "Handle SingleExplosion";
handleExplosion = true;
}
m_explosionSingleStates.insert(data.id(), data.effectStateId());
if (!handleExplosion)
return;
DEBUG_LOG(m_logger) << "isSmartType = " << m_ddsHandler->isSmartChargeType(data.chargeTypeId());
if (m_ddsHandler->isSmartChargeType(data.chargeTypeId()))
{
DEBUG_LOG(m_logger) << "isSmartType = TRUE";
}
else
{
int impactPosZone = -1;
const QPointF impactPos(CentralEffectSimulation::geographic2Utm(data.impactPosition(), &impactPosZone));
const CentralEffectSimulation::AesItemViewUtm itemView = m_aesItemViews.value(data.id());
const double impactRadius = m_ddsHandler->chargeTypeRadius(data.chargeTypeId());
DEBUG_LOG(m_logger) << "Handle ExplosionSingle (EsActivated)";
const auto participantPositions = m_handlerParticipant->positions();
for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition : participantPositions)
{
if (participantPosition.insideBuilding())
continue;
if (m_handlerParticipant->isOutdated(participantPosition))
continue;
const QPointF participantPosUtm = CentralEffectSimulation::geographic2Utm(participantPosition.pos(), impactPosZone);
DEBUG_LOG(m_logger) << "impact pos: " << CentralEffectSimulation::debug(impactPos)
<< " with radius " << impactRadius
<< " and participant position " << CentralEffectSimulation::debug(participantPosUtm);
if (inCircle(impactPos, impactRadius, participantPosUtm))
{
DEBUG_LOG(m_logger) << "participantId = " << participantPosition.participantId() << " inCircle = true";
sendEffectTypeEvent(itemView, participantPosition, EffectType::ExplosionSingle, -1, QDateTime::currentMSecsSinceEpoch());
}
else
DEBUG_LOG(m_logger) << "participantId = " << participantPosition.participantId() << " inCircle = false";
}
}
} // onExplosionSingleChanged
void HandlerParticipantArea::onExplosionSingleCentralChanged(const AreaEffectSimulation::ExplosionSingleCentral &data, DataModification modType)
{
if (modType != DataModification::eAdded && modType != DataModification::eModified)
return;
if (data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsRemoved) // EsExpired? EsError? EsUnknown?
{
DEBUG_LOG(m_logger) << "Remove ExplosionSingleCentral with aesId = " << data.id();
m_explosionSingleCentralStates.remove(data.id());
return;
} // if
if ((m_explosionSingleCentralStates.value(data.id()) != AreaEffectSimulation::AesEffectState_def::EsPrepared &&
m_explosionSingleCentralStates.value(data.id()) != AreaEffectSimulation::AesEffectState_def::EsFired) ||
data.effectStateId() != AreaEffectSimulation::AesEffectState_def::EsActivated)
{
m_explosionSingleCentralStates.insert(data.id(), data.effectStateId());
return;
} // if
m_explosionSingleCentralStates.insert(data.id(), data.effectStateId());
DEBUG_LOG(m_logger) << "Handle ExplosionSingleCentral with aesId = " << data.id();
// pre-calculate geometric data
const AreaEffectSimulation::AesItemView &itemView = m_handlerAesData->itemView(data.id());
const Core::GeoPosition &centerGeo = itemView.form().front();
const LtsCoordTrans::UtmCoordinate centerUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(centerGeo.lon(), centerGeo.lat());
const LtsUtils::QPoint3D center(centerUtm.x, centerUtm.y, centerGeo.z());
const Core::GeoPosition &pointOnSphereGeo = itemView.form().back();
const LtsCoordTrans::UtmCoordinate pointOnSphereUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(pointOnSphereGeo.lon(), pointOnSphereGeo.lat());
const LtsUtils::QPoint3D pointOnSphere(pointOnSphereUtm.x, pointOnSphereUtm.y, pointOnSphereGeo.z());
const double radiusSquared = (center - pointOnSphere).lengthSquared();
const int effectType = static_cast<int>(EffectType::ExplosionSingle) & 0xffff;
const QMap<int, BuildingModel *> buildingModels = m_handlerBuildingModel->buildingModels();
const QList<CentralEffectSimulation::HandlerParticipant::CesParticipantData> participantPositions = m_handlerParticipant->positions().values();
for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition : qAsConst(participantPositions))
{
if (participantPosition.insideBuilding())
continue;
if (m_handlerParticipant->isOutdated(participantPosition))
continue;
const LtsCoordTrans::UtmCoordinate positionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(participantPosition.lastGeoPosition.lon(), participantPosition.lastGeoPosition.lat());
const LtsUtils::QPoint3D position(positionUtm.x, positionUtm.y, participantPosition.lastGeoPosition.z());
const double distanceSquared = (center - position).lengthSquared();
if (distanceSquared > radiusSquared)
continue;
bool isCovered = false;
for (auto it = buildingModels.constBegin(); it != buildingModels.constEnd(); ++it)
{
const BuildingModel *buildingModel = it.value();
if (buildingModel->hasIntersection(centerGeo, participantPosition.lastGeoPosition))
{
DEBUG_LOG(m_logger) << "Participant " << participantPosition.participantId() << " is covered by building " << it.key();
isCovered = true;
break;
} // if
} // for
if (isCovered)
continue;
DEBUG_LOG(m_logger) << "Participant " << participantPosition.participantId() << " is hit";
sendCesHitRequest(itemView, participantPosition, QDateTime::currentMSecsSinceEpoch(), effectType, -1);
} // for
}
// Check for all participants wheter he is in the explosion area
void HandlerParticipantArea::calculateImpacts(const AreaEffectSimulation::TriggeredImpacts &data)
{
auto it = m_aesItemViews.find(data.aesItemId());
if (it == m_aesItemViews.end())
{
WARNING_LOG(m_logger) << "Could not find item view for aes with id " << data.aesItemId();
return;
}
CentralEffectSimulation::AesItemViewUtm &aesItemView = *it;
// older then 10sec? ignore
if (data.sampleTime() < m_ddsHandler->currentPartitionTime() - 10 * 1000)
{
if (aesItemView.sampleTimeHandledImpacts < data.sampleTime())
{
aesItemView.sampleTimeHandledImpacts = data.sampleTime();
aesItemView.handledImpacts = data.bitfield();
}
return;
}
QVector<int> bitsToCheck;
aesItemView.handledImpacts.resize(data.bitfield().size(), 0xFF);
for (int bitFieldIdx = 0; bitFieldIdx < data.impactCount(); ++bitFieldIdx)
{
// not yet triggered (bit is still 1)
if (LtsUtils::BitArray::testBit(data.bitfield(), bitFieldIdx))
continue;
// already checked (bit is already 0)
if (!LtsUtils::BitArray::testBit(aesItemView.handledImpacts, bitFieldIdx))
continue;
DEBUG_LOG(m_logger) << "New impact at idx " << bitFieldIdx;
bitsToCheck += bitFieldIdx;
}
// remember handled impacts
aesItemView.sampleTimeHandledImpacts = data.sampleTime();
aesItemView.handledImpacts = data.bitfield();
const auto participantPositions = m_handlerParticipant->positions();
for (const CentralEffectSimulation::HandlerParticipant::CesParticipantData &pPos : participantPositions)
{
if (pPos.vulnerabilityState == ProjectDataManagement::VulnerabilityState_def::CatastrophicKill)
continue;
if (pPos.insideBuilding())
continue;
if (!inBoundingBoxCheck(aesItemView, pPos))
continue;
calculateImpacts(aesItemView, bitsToCheck, pPos);
}
} // calculateImpacts
// calculate all impacts of the field for a participant
void HandlerParticipantArea::calculateImpacts(const CentralEffectSimulation::AesItemViewUtm &itemView,
const QVector<int> &impactsToCheck,
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition)
{
const bool bIsSmartChargeType = m_ddsHandler->isSmartChargeType(itemView.chargeTypeId);
DEBUG_LOG(m_logger) << "calculateImpacts, isSMart: " << bIsSmartChargeType;
// get radius of munition
const double impactRadius = m_ddsHandler->chargeTypeRadius(itemView.chargeTypeId);
const QPointF participantPosUtmPoint = CentralEffectSimulation::geographic2Utm(participantPosition.pos(), itemView.zone);
for (int bitFieldIdx : impactsToCheck)
{
if (itemView.form.size() <= bitFieldIdx)
continue;
if (bIsSmartChargeType)
{
continue;
}
// get position of impact to corresponding bit
const QPointF &impactPos = itemView.form.value(bitFieldIdx);
if (inCircle(impactPos, impactRadius, participantPosUtmPoint))
sendEffectTypeEvent(itemView, participantPosition, EffectType::ExplosionArea, bitFieldIdx, QDateTime::currentMSecsSinceEpoch());
} // foreach Impact
} // calculateImpacts
// Send via TAB-Proxy to battlefield
void HandlerParticipantArea::sendCesHitRequest(const CentralEffectSimulation::AesItemViewUtm &itemView,
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition,
qint64 effectTime,
int remaining,
int mineFieldBitIndex)
{
int32_t legacyChargeTypeId = 0;
legacyChargeTypeId = m_ddsHandler->getLegacyIdFromChargeTypeId(itemView.chargeTypeId);
if (remaining < 0)
return;
DEBUG_LOG(m_logger) << "sendCesHitRequest Settings::calculateCentralAes=" << std::boolalpha << Settings::calculateCentralAes()
<< " insideWlsArea=" << std::boolalpha << participantPosition.insideWlsArea()
<< " isSmartChargeType=" << std::boolalpha << m_ddsHandler->isSmartChargeType(itemView.chargeTypeId)
<< " participantId=" << participantPosition.participantId();
if (!m_ddsHandler->isSmartChargeType(itemView.chargeTypeId))
{
DEBUG_LOG(m_logger) << "don't sendCesHitRequest Smart Type return";
return;
}
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;
}
int32_t originatorParticipantId = m_aesItemId2partIdMap.value(itemView.aesItemId, 0);
const TrainingAreaBase::TabPosition pos(participantPosition.pos(),
Core::PositionReportingSource_def::Gps, // irrelevant
participantPosition.buildingId(),
participantPosition.roomId());
const int originatorEquipNo = m_ddsHandler->participantId2EquipNo(originatorParticipantId);
TrainingAreaBase::SendCesHitRequest requestTab(
equipNo, // equipmentNumber
participantPosition.participantId(), // participantId
itemView.chargeTypeId, // chargeTypeId
itemView.aesItemId, // aesId
originatorEquipNo, // originatorEquipmentNumber
remaining, // remainEffectPercent ( no protection outside Building )
pos,
effectTime, // effectTime
itemView.partyId, // originPartyId
mineFieldBitIndex
);
DEBUG_LOG(m_logger) << " participantId=" << participantPosition.participantId()
<< " chargeTypeId=" << itemView.chargeTypeId
<< " legacyChargeTypeId=" << legacyChargeTypeId;
m_sendCesHitRequestQueue.enqueue(requestTab);
if (m_sendCesHitRequestQueue.size() == 1)
{
DEBUG_LOG(m_logger) << "SendCesHitRequest: " << requestTab;
m_ddsHandler->ftcTab()->sendCesHitRequest(requestTab, this);
}
}
void HandlerParticipantArea::sendCesHitRequest(const AreaEffectSimulation::AesItemView &itemView,
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition,
qint64 effectTime,
int remaining,
int mineFieldBitIndex)
{
if (remaining < 0)
return;
int32_t legacyChargeTypeId = 0;
legacyChargeTypeId = m_ddsHandler->getLegacyIdFromChargeTypeId(itemView.chargeTypeId());
DEBUG_LOG(m_logger) << "sendCesHitRequest Settings::calculateCentralAes=" << std::boolalpha << Settings::calculateCentralAes()
<< " insideWlsArea=" << std::boolalpha << participantPosition.insideWlsArea()
<< " isSmartChargeType=" << std::boolalpha << m_ddsHandler->isSmartChargeType(itemView.chargeTypeId())
<< " participantId=" << participantPosition.participantId();
const int equipmentNumber = m_ddsHandler->participantId2EquipNo(participantPosition.participantId());
DEBUG_LOG(m_logger) << " ftc sendCESHit to Participant participantId = " << participantPosition.participantId()
<< " equipNo = " << equipmentNumber << " aesItemId =" << itemView.aesItemId();
// participantPosition.participantPosition == 0 -> send to all
if (participantPosition.participantId() != 0 && equipmentNumber <= 0)
{
DEBUG_LOG(m_logger) << "participantPosition.participantId != 0 && equipNo <= 0" ;
return;
} // if
if (participantPosition.participantId() != 0 &&
participantPosition.vulnerabilityState == ProjectDataManagement::VulnerabilityState_def::CatastrophicKill)
{
DEBUG_LOG(m_logger) << "participantPosition.participantId != 0 CatastrophicKill)";
return;
} // if
int32_t originatorParticipantId = m_aesItemId2partIdMap.value(itemView.aesItemId(), 0);
const int originatorEquipmentNumber = m_ddsHandler->participantId2EquipNo(originatorParticipantId);
const TrainingAreaBase::TabPosition pos(
participantPosition.pos(), Core::PositionReportingSource_def::Gps, participantPosition.buildingId(),
participantPosition.roomId()
);
TrainingAreaBase::SendCesHitRequest requestTab(
equipmentNumber, participantPosition.participantId(), legacyChargeTypeId, itemView.aesItemId(), originatorEquipmentNumber,
remaining, pos, effectTime, itemView.partyId().get(), mineFieldBitIndex
);
DEBUG_LOG(m_logger) << " participantId=" << participantPosition.participantId() << " chargeTypeId=" << itemView.chargeTypeId()
<< " legacyChargeTypeId=" << legacyChargeTypeId;
m_sendCesHitRequestQueue.enqueue(requestTab);
if (m_sendCesHitRequestQueue.size() == 1)
{
DEBUG_LOG(m_logger) << "SendCesHitRequest: " << requestTab;
m_ddsHandler->ftcTab()->sendCesHitRequest(requestTab, this);
} // if
}
void HandlerParticipantArea::sendParticipantEvent(const CentralEffectSimulation::AesItemViewUtm &itemView,
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition,
const ProjectDataManagement::EventType eventType)
{
bool calculateAes = false;
ParticipantManagement::TypeClass typeClass = m_handlerParticipant->typeClassFromTypeId(participantPosition.participantTypeId());
// can happen since the positions are collected for all participants (no check if it is a SupportEntity or similar)
if (typeClass == ParticipantManagement::TypeClass_def::Unit ||
typeClass == ParticipantManagement::TypeClass_def::SupportUnit ||
typeClass == ParticipantManagement::TypeClass_def::SupportEntity ||
typeClass == ParticipantManagement::TypeClass_def::Aircraft ||
typeClass == ParticipantManagement::TypeClass_def::Vessel ||
typeClass == ParticipantManagement::TypeClass_def::Unknown)
{
DEBUG_LOG(m_logger) << "Participant with id " << participantPosition.participantId()
<< " has invalid typeClass: " << typeClass
<< " eventType: " << eventType;
return;
}
if (!eventMustBeSend(itemView.aesItemId, participantPosition.participantId(), eventType))
{
DEBUG_LOG(m_logger) << "Not sending Event " << eventType << " for participant " << participantPosition.participantId()
<< " and aes " << itemView.aesItemId << " - already sent";
return;
}
switch (Settings::calculateCentralAes())
{
case Settings::NO_AES_CALCUALTION:
calculateAes = false;
break;
case Settings::AES_CALCUALTION:
calculateAes = true;
break;
case Settings::AES_CALCUALTION_ONLY_IN_WLS_AREA:
if (participantPosition.insideWlsArea())
calculateAes = true;
else
{
DEBUG_LOG(m_logger) << "!participantPosition.insideWlsArea()";
calculateAes = false;
}
}
// send hit events only with central AES Simulation
if (calculateAes
&& (eventType == ProjectDataManagement::EventType_def::HitB
|| eventType == ProjectDataManagement::EventType_def::HitC
|| eventType == ProjectDataManagement::EventType_def::Hit
|| eventType == ProjectDataManagement::EventType_def::HitEffectSimulation
|| eventType == ProjectDataManagement::EventType_def::HitMine
|| eventType == ProjectDataManagement::EventType_def::HitN))
{
DEBUG_LOG(m_logger) << "send hit events only with central AES Simulation participant=" << participantPosition.participantId();
return;
}
int equipmentNumber = m_ddsHandler->participantId2EquipNo(participantPosition.participantId());
if (equipmentNumber <= 0)
{
WARNING_LOG(m_logger) << "Not sending Event " << eventType << " for participant " << participantPosition.participantId()
<< " - participant has no equipment number";
return;
}
DEBUG_LOG(m_logger) << "sendParticipantEvent" << eventType
<< " participantId=" << participantPosition.participantId()
<< " AesItemId=" << itemView.aesItemId;
CentralEffectSimulation::ParticipantEvent pe;
pe.id(m_ddsHandler->getParticipantEventId());
pe.eventTypeId(eventType.underlying());
pe.participantId(participantPosition.participantId());
pe.eventTime(participantPosition.sampleTime());
pe.aesItemId(itemView.aesItemId);
pe.partyId(participantPosition.partyId());
//pe.aesName(m_handlerAesData->aesName(itemView.aesItemId));
pe.equipmentNumber(equipmentNumber);
pe.vulnerability(participantPosition.vulnerabilityState.underlying());
pe.position(participantPosition.pos());
// Criterion for friendly Fire:
if (itemView.partyId != -1
&& ((eventType == ProjectDataManagement::EventType_def::EnterMineArea
|| eventType == ProjectDataManagement::EventType_def::EnterNuclear
|| eventType == ProjectDataManagement::EventType_def::HitB
|| eventType == ProjectDataManagement::EventType_def::HitC
|| eventType == ProjectDataManagement::EventType_def::Hit
|| eventType == ProjectDataManagement::EventType_def::HitEffectSimulation
|| eventType == ProjectDataManagement::EventType_def::HitMine
|| eventType == ProjectDataManagement::EventType_def::HitN)
&& itemView.partyId == participantPosition.partyId()))
pe.friendlyFire(true);
else
pe.friendlyFire(false);
INFO_LOG(m_logger) << "sendParticipantEvent: " << pe
<< ", currentMSecsSinceEpoch = " << QDateTime::currentMSecsSinceEpoch()
<< ", time difference = " << QDateTime::currentMSecsSinceEpoch() - pe.eventTime();
m_ddsHandler->dtsCes()->publish(pe);
const auto id = pe.id();
QTimer::singleShot(1 * 60 * 1000, this, [id, this]() { m_ddsHandler->dtsCes()->removeParticipantEventByKey(id); });
}
void HandlerParticipantArea::sendEffectTypeEvent(const CentralEffectSimulation::AesItemViewUtm &itemView,
const CentralEffectSimulation::HandlerParticipant::CesParticipantData &participantPosition,
EffectType effectType,
int mineFieldBitIndex,
qint64 effectTime)
{
Q_UNUSED(effectTime);
// Trefferereignis Sekundaerwirkung
switch (Settings::calculateCentralAes())
{
case Settings::NO_AES_CALCUALTION:
return;
case Settings::AES_CALCUALTION:
break;
case Settings::AES_CALCUALTION_ONLY_IN_WLS_AREA:
if (!participantPosition.insideWlsArea())
{
DEBUG_LOG(m_logger) << "!participantPosition.insideWlsArea()";
return;
}
}
/*if (!participantPosition.insideWlsArea())
{
// forget all enter/leave - the TNE calculates on its own
m_effectTypeStates[itemView.aesItemId][participantPosition.participantId()].clear();
m_effectTypeBitFieldIndexes[itemView.aesItemId][participantPosition.participantId()].clear();
return;
}*/
QByteArray mfb;
if (mineFieldBitIndex >= 0)
mfb = ", mineFieldBitIndex: " + QByteArray::number(mineFieldBitIndex);
if (!effectTypeMustBeSend(itemView.aesItemId,
participantPosition.participantId(),
effectType, mineFieldBitIndex,
participantPosition.insideBuilding()))
{
DEBUG_LOG(m_logger) << "Not sending EffectType event " << toString(effectType)
<< ", participant: " << participantPosition.participantId()
<< ", aesItemId: " << itemView.aesItemId
<< mfb
<< " - already sent";
return;
}
const int ctasEffectType = static_cast<int>(effectType) & 0xffff;
DEBUG_LOG(m_logger) << "Sending EffectType event " << toString(effectType)
<< ", participant: " << participantPosition.participantId()
<< ", aesItemId: " << itemView.aesItemId
<< mfb
<< ", ctasEffectType: " << ctasEffectType;
sendCesHitRequest(itemView,
participantPosition,
//effectTime >= 0 ? effectTime : participantPosition.sampleTime(),
QDateTime::currentMSecsSinceEpoch(),
ctasEffectType,
mineFieldBitIndex);
}
bool HandlerParticipantArea::eventMustBeSend(int aesItemId,
int participantId,
const ProjectDataManagement::EventType eventType)
{
// end enter-leave only once
QSet<ProjectDataManagement::EventType> &activeEvents = m_eventStates[aesItemId][participantId]; // reference!
// - only send event if it was not already sent
if (activeEvents.contains(eventType))
{
DEBUG_LOG(m_logger) << "activeEvents=" << eventType;
return false;
}
activeEvents.insert(eventType);
// - remove opposite events (e.g remove Enter event when we leave the area)
switch (eventType.underlying())
{
case ProjectDataManagement::EventType_def::EnterDangerArea:
activeEvents.remove(ProjectDataManagement::EventType_def::LeaveDangerArea);
break;
case ProjectDataManagement::EventType_def::LeaveDangerArea:
activeEvents.remove(ProjectDataManagement::EventType_def::EnterDangerArea);
break;
case ProjectDataManagement::EventType_def::EnterMineArea:
activeEvents.remove(ProjectDataManagement::EventType_def::LeaveMineArea);
break;
case ProjectDataManagement::EventType_def::LeaveMineArea:
activeEvents.remove(ProjectDataManagement::EventType_def::EnterMineArea);
break;
case ProjectDataManagement::EventType_def::EnterFunctionalArea:
activeEvents.remove(ProjectDataManagement::EventType_def::LeaveFunctionalArea);
break;
case ProjectDataManagement::EventType_def::LeaveFunctionalArea:
activeEvents.remove(ProjectDataManagement::EventType_def::EnterFunctionalArea);
break;
case ProjectDataManagement::EventType_def::EnterNuclear:
activeEvents.remove(ProjectDataManagement::EventType_def::LeaveNuclear);
break;
case ProjectDataManagement::EventType_def::LeaveNuclear:
activeEvents.remove(ProjectDataManagement::EventType_def::EnterNuclear);
break;
default:
break;
}
return true;
}
bool HandlerParticipantArea::effectTypeMustBeSend(int aesItemId,
int participantId,
EffectType effectType,
int mineFieldBitIndex,
bool insideBuilding)
{
// end enter-leave only once
QSet<EffectType> &activeEffects = m_effectTypeStates[aesItemId][participantId]; // reference!
// - only send event if it was not already sent
if (activeEffects.contains(effectType))
{
DEBUG_LOG(m_logger) << "effectTypeStates already contains aesItemId " << aesItemId;
return false;
}
activeEffects += effectType;
bool bMustBeSend = true;
switch (effectType)
{
case EffectType::EffectiveMineRangeEnter:
{
activeEffects.remove(EffectType::EffectiveMineRangeLeave);
activeEffects.remove(EffectType::EffectiveMineRangeEnter); // must check per bit, not per area
QSet<int> &mineFieldBits = m_effectTypeBitFieldIndexes[aesItemId][participantId];
if (mineFieldBits.contains(mineFieldBitIndex) || insideBuilding)
bMustBeSend = false;
else
{
mineFieldBits.insert(mineFieldBitIndex);
bMustBeSend = true;
}
break;
}
case EffectType::EffectiveMineRangeLeave:
{
activeEffects.remove(EffectType::EffectiveMineRangeEnter);
activeEffects.remove(EffectType::EffectiveMineRangeLeave); // must check per bit, not per area
QSet<int> &mineFieldBits = m_effectTypeBitFieldIndexes[aesItemId][participantId];
mineFieldBits.remove(mineFieldBitIndex);
bMustBeSend = false; // no need to send leave to CTAS
break;
}
case EffectType::NContaminationLeave:
bMustBeSend = false;
bMustBeSend |= activeEffects.remove(EffectType::NContaminationEnterArea1);
bMustBeSend |= activeEffects.remove(EffectType::NContaminationEnterArea2);
bMustBeSend |= activeEffects.remove(EffectType::NContaminationDetonation);
break;
case EffectType::NContaminationEnterArea1:
bMustBeSend = true;
activeEffects.remove(EffectType::NContaminationLeave);
activeEffects.remove(EffectType::NContaminationEnterArea2);
activeEffects.remove(EffectType::NContaminationDetonation);
break;
case EffectType::NContaminationEnterArea2:
bMustBeSend = true;
activeEffects.remove(EffectType::NContaminationLeave);
activeEffects.remove(EffectType::NContaminationEnterArea1);
activeEffects.remove(EffectType::NContaminationDetonation);
break;
case EffectType::NContaminationDetonation:
bMustBeSend = true;
activeEffects.remove(EffectType::NContaminationLeave);
activeEffects.remove(EffectType::NContaminationEnterArea1);
activeEffects.remove(EffectType::NContaminationEnterArea2);
break;
case EffectType::FunctionalAreaLeave:
bMustBeSend = activeEffects.remove(EffectType::FunctionalAreaEnter);
break;
case EffectType::FunctionalAreaEnter:
activeEffects.remove(EffectType::FunctionalAreaLeave);
break;
case EffectType::ExplosionArea:
activeEffects.remove(EffectType::ExplosionArea);
bMustBeSend = true;
break;
case EffectType::ExplosionSingle:
bMustBeSend = true;
break;
case EffectType::BCContaminationLeave:
bMustBeSend = false;
bMustBeSend |= activeEffects.remove(EffectType::BCContaminationEnterFloating);
bMustBeSend |= activeEffects.remove(EffectType::BCContaminationEnterResident);
bMustBeSend |= activeEffects.remove(EffectType::BCContaminationEnterFloatingArea);
break;
case EffectType::BCContaminationEnterFloating:
bMustBeSend = true;
activeEffects.remove(EffectType::BCContaminationLeave);
activeEffects.remove(EffectType::BCContaminationEnterResident);
activeEffects.remove(EffectType::BCContaminationEnterFloatingArea);
break;
case EffectType::BCContaminationEnterResident:
bMustBeSend = true;
activeEffects.remove(EffectType::BCContaminationLeave);
activeEffects.remove(EffectType::BCContaminationEnterFloating);
activeEffects.remove(EffectType::BCContaminationEnterFloatingArea);
break;
case EffectType::BCContaminationEnterFloatingArea:
bMustBeSend = true;
activeEffects.remove(EffectType::BCContaminationLeave);
activeEffects.remove(EffectType::BCContaminationEnterFloating);
activeEffects.remove(EffectType::BCContaminationEnterResident);
break;
case EffectType::BeStillWarning:
case EffectType::Unknown:
break;
}
return bMustBeSend;
}
// Anmerkung: Im SFM werden Quadrate und keine Kreise genommen. ( Wird hier nach Absprache mit Manfred nicht gemacht)
bool HandlerParticipantArea::inCircle(const QPointF &centerUtm, const double radius, const QPointF &pointUtm)
{
double squareDist = CentralEffectSimulation::getSquareDistance(centerUtm, pointUtm);
double squareRadius = radius * radius;
DEBUG_LOG(m_logger) << "squareDist: " << squareDist << ", squareRadius: " << squareRadius;
return (squareDist < squareRadius);
} // inCircle
ProjectDataManagement::EventType HandlerParticipantArea::leaveEventTypeForAesType(AreaEffectSimulation::AesType aesTypeId)
{
switch (aesTypeId.underlying())
{
case AreaEffectSimulation::AesType_def::AtDangerArea:
return ProjectDataManagement::EventType::LeaveDangerArea;
case AreaEffectSimulation::AesType_def::AtMineArea:
return ProjectDataManagement::EventType::LeaveMineArea;
case AreaEffectSimulation::AesType_def::AtNContamination:
return ProjectDataManagement::EventType::LeaveNuclear;
case AreaEffectSimulation::AesType_def::AtFunctionalArea:
return ProjectDataManagement::EventType::LeaveFunctionalArea;
default:
break;
}
return ProjectDataManagement::EventType::Unknown;
}
EffectType HandlerParticipantArea::leaveEffectTypeForAesType(AreaEffectSimulation::AesType aesTypeId)
{
switch (aesTypeId.underlying())
{
case AreaEffectSimulation::AesType_def::AtBCContamination:
return EffectType::BCContaminationLeave;
case AreaEffectSimulation::AesType_def::AtNContamination:
return EffectType::NContaminationLeave;
case AreaEffectSimulation::AesType_def::AtFunctionalArea:
return EffectType::FunctionalAreaLeave;
default:
break;
}
return EffectType::Unknown;
}
EffectType HandlerParticipantArea::effectTypeForNContaminationArea(const CentralEffectSimulation::AesItemViewUtm &itemView,
const QPointF &participantPosUtmPoint,
qint64 partPosSampleTime)
{
if (itemView.radials.size() != 3)
return EffectType::NContaminationEnterArea2; // not yet known, we only know we're inside the area
const double squareDist = CentralEffectSimulation::getSquareDistance(itemView.center, participantPosUtmPoint);
if (squareDist <= pow(itemView.radials.at(0), 2))
{
// 5s delay
DEBUG_LOG(m_logger) << "partPosSampleTime: " << partPosSampleTime
<< ", itemView.effectStart: " << itemView.effectStart;
if (partPosSampleTime < itemView.effectStart + 5000)
return EffectType::NContaminationDetonation;
return EffectType::NContaminationEnterArea1;
}
if (squareDist <= pow(itemView.radials.at(1), 2))
return EffectType::NContaminationEnterArea1;
if (squareDist <= pow(itemView.radials.at(2), 2))
return EffectType::NContaminationEnterArea2;
return EffectType::NContaminationLeave;
}
EffectType HandlerParticipantArea::effectTypeForBCContaminationArea(const CentralEffectSimulation::AesItemViewUtm &itemView,
const QPointF &participantPosUtmPoint)
{
if (itemView.radials.size() != 1)
return EffectType::BCContaminationEnterFloating;
const double squareDist = CentralEffectSimulation::getSquareDistance(itemView.center, participantPosUtmPoint);
if (squareDist <= pow(itemView.radials.at(0), 2))
return EffectType::BCContaminationEnterResident; // first circle of resident contamination
return EffectType::BCContaminationEnterFloatingArea; // expanding/moving part of the contamination
}
void HandlerParticipantArea::aesItemViewRemoved(int aesItemId)
{
const CentralEffectSimulation::AesItemViewUtm aiv = m_aesItemViews.value(aesItemId);
if (aiv.aesItemId == aesItemId)
{
const auto effectTypLeave = leaveEffectTypeForAesType(aiv.aesTypeId);
if (effectTypLeave != EffectType::Unknown)
{
DEBUG_LOG(m_logger) << "aesItemViewRemoved, leaveEffectType: " << toString(effectTypLeave);
auto it = m_effectTypeStates.find(aesItemId);
if (it != m_effectTypeStates.end())
{
const QHash<int, QSet<EffectType>> &participants = it.value();
DEBUG_LOG(m_logger) << "aesItemViewRemoved, sending leave events to " << participants.size()
<< " participants";
for (auto itP = participants.begin(); itP != participants.end(); ++ itP)
sendEffectTypeEvent(aiv, m_handlerParticipant->position(itP.key()), effectTypLeave, -1, -1);
}
}
const auto eventTypeLeave = leaveEventTypeForAesType(aiv.aesTypeId);
if (eventTypeLeave != ProjectDataManagement::EventType::Unknown)
{
DEBUG_LOG(m_logger) << "aesItemViewRemoved, leaveEventType: " << eventTypeLeave;
auto it = m_eventStates.find(aesItemId);
if (it != m_eventStates.end())
{
const QHash<int, QSet<ProjectDataManagement::EventType>> &participants = it.value();
DEBUG_LOG(m_logger) << "aesItemViewRemoved, sending leave events to " << participants.size()
<< " participants";
for (auto itP = participants.begin(); itP != participants.end(); ++ itP)
sendParticipantEvent(aiv, m_handlerParticipant->position(itP.key()), eventTypeLeave);
}
}
}
m_effectTypeStates.remove(aesItemId);
m_effectTypeBitFieldIndexes.remove(aesItemId);
m_aesItemViews.remove(aesItemId);
m_explosionSingleStates.remove(aesItemId);
m_eventStates.remove(aesItemId);
}
} // namespace Ces2D