1387 lines
50 KiB
C++
Executable File
1387 lines
50 KiB
C++
Executable File
/**
|
||
* Handle incoming ETS Tetra Telegrams and convert them to dds data topics
|
||
* $Id$
|
||
* $HeadURL$
|
||
* $Author$
|
||
* Created by Christian Ehrlicher, SLSE, 2016-04-14
|
||
*/
|
||
|
||
#include "recv_from_dds_aes.h"
|
||
#include "main_handler.h"
|
||
#include "settings.h"
|
||
#include <AreaEffectSimulation/Utils/polygons.h>
|
||
#include <AreaEffectSimulation/aes_translation.h>
|
||
#include <ngldb/db_service_handler.h>
|
||
#include <TrainingAreaBase/tab_fts.h>
|
||
#include <QTransform>
|
||
|
||
namespace TrainingAreaBase
|
||
{
|
||
|
||
//
|
||
// RecvFromDdsAes
|
||
//
|
||
AesStateHandler::AesStateHandler(MainHandler *mainHandler)
|
||
: m_dbInterface(mainHandler->dbHandler()->db())
|
||
, m_exerciseId(mainHandler->exerciseId())
|
||
, m_logger(getLogger("AesStateHandler"))
|
||
{}
|
||
|
||
// not in header due to gcc4.8
|
||
template<>
|
||
void RecvFromDdsAes::txData(const TrainingAreaBase::TetraIcd::DusMineReactivation &tele)
|
||
{
|
||
m_txCallback->txData(tele);
|
||
m_sentAesTelegrams.push_back(new TrainingAreaBase::TetraIcd::DusMineReactivation(tele));
|
||
m_sentAesTelegramsAesIds.push_back(-1);
|
||
m_checkOutdatedTelegramsTimer->start();
|
||
DEBUG_LOG(m_logger) << "Appending " << tele.type() << " to m_sentAesTelegrams";
|
||
}
|
||
|
||
bool AesStateHandler::init()
|
||
{
|
||
const auto cb = [this](const Database::AesId2TaId & item)
|
||
{
|
||
|
||
|
||
m_tetraId2ddsId[item.aesTetraId] = item.aesDdsId;
|
||
m_ddsId2TetraId[item.aesDdsId] = item.aesTetraId;
|
||
DEBUG_LOG(m_logger) << "AES internal DDS id: " << item.aesDdsId << " - tetra id: " << item.aesTetraId;
|
||
if (item.isAvailableInTetra)
|
||
m_aesInTetraAvailable += item.aesTetraId;
|
||
};
|
||
|
||
if (!m_dbInterface->selectByExerciseId(cb, m_exerciseId))
|
||
{
|
||
ERROR_LOG(m_logger) << "Error retrieving data from database!";
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
int AesStateHandler::createAesTetraId(int aesDdsId, bool &alreadyExists)
|
||
{
|
||
alreadyExists = false;
|
||
|
||
Database::AesId2TaId aes2Ta;
|
||
aes2Ta.aesTetraId = getAesTetraId(aesDdsId);
|
||
if (aes2Ta.aesTetraId != 0)
|
||
{
|
||
alreadyExists = true;
|
||
return aes2Ta.aesTetraId;
|
||
}
|
||
|
||
aes2Ta.aesDdsId = aesDdsId;
|
||
aes2Ta.isAvailableInTetra = false;
|
||
aes2Ta.exerciseId = m_exerciseId;
|
||
if (!m_dbInterface->insert(aes2Ta))
|
||
{
|
||
WARNING_LOG(m_logger) << "Unable to create a valid area effect simulation id for tetra!";
|
||
return -1;
|
||
}
|
||
|
||
m_tetraId2ddsId[aes2Ta.aesTetraId] = aes2Ta.aesDdsId;
|
||
m_ddsId2TetraId[aes2Ta.aesDdsId] = aes2Ta.aesTetraId;
|
||
|
||
return aes2Ta.aesTetraId;
|
||
}
|
||
|
||
bool AesStateHandler::update(int aesDdsId, int aesTetraId, bool bSentToTetra)
|
||
{
|
||
if (aesTetraId <= 0)
|
||
return false;
|
||
|
||
Database::AesId2TaId aes2Ta;
|
||
aes2Ta.aesTetraId = aesTetraId;
|
||
aes2Ta.aesDdsId = aesDdsId;
|
||
aes2Ta.isAvailableInTetra = bSentToTetra;
|
||
aes2Ta.exerciseId = m_exerciseId;
|
||
if (!m_dbInterface->update(aes2Ta))
|
||
{
|
||
WARNING_LOG(m_logger) << "Unable to update area effect simulation id for tetra in database!";
|
||
return false;
|
||
}
|
||
|
||
if (bSentToTetra)
|
||
m_aesInTetraAvailable += aesTetraId;
|
||
else
|
||
m_aesInTetraAvailable -= aesTetraId;
|
||
|
||
return true;
|
||
}
|
||
|
||
//
|
||
// RecvFromDdsAes
|
||
//
|
||
RecvFromDdsAes::RecvFromDdsAes(MainHandler *mainHandler,
|
||
const std::shared_ptr<SendToTab> &txCallback,
|
||
QObject *parent)
|
||
: QObject(parent)
|
||
, m_ddsConnector(mainHandler->ddsHandler()->ddsConnector())
|
||
, m_txCallback(txCallback)
|
||
, m_aesStateHandler(new AesStateHandler(mainHandler))
|
||
, m_posHandler(mainHandler->posHandler())
|
||
, m_mainHandler(mainHandler)
|
||
, m_checkOutdatedTelegramsTimer(new QTimer(this))
|
||
, m_sendDusMineReactivtionTimer(new QTimer(this))
|
||
, m_logger(getLogger("RecvFromDdsAes"))
|
||
{
|
||
m_checkOutdatedTelegramsTimer->setSingleShot(true);
|
||
m_checkOutdatedTelegramsTimer->setInterval(5000);
|
||
connect(m_checkOutdatedTelegramsTimer, &QTimer::timeout,
|
||
this, &RecvFromDdsAes::onCheckOutdatedTelegrams);
|
||
|
||
// collect multiple reactivations and wait 200msecs for the telegrams to rush in
|
||
m_sendDusMineReactivtionTimer->setSingleShot(true);
|
||
m_sendDusMineReactivtionTimer->setInterval(200);
|
||
connect(m_sendDusMineReactivtionTimer, &QTimer::timeout,
|
||
this, &RecvFromDdsAes::onSendDusMineReactivationTimeout);
|
||
}
|
||
|
||
RecvFromDdsAes::~RecvFromDdsAes()
|
||
{
|
||
qDeleteAll(m_sentAesTelegrams);
|
||
m_sentAesTelegrams.clear();
|
||
m_dtcAes.reset();
|
||
m_aesStateHandler.reset();
|
||
}
|
||
|
||
bool RecvFromDdsAes::init()
|
||
{
|
||
// read old data from db
|
||
if (!m_aesStateHandler->init())
|
||
return false;
|
||
|
||
m_dtcAes = AreaEffectSimulation::Interface::Dtc::create(m_ddsConnector);
|
||
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::airSupportImpactChanged,
|
||
this, &RecvFromDdsAes::onAirSupportImpactChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::bCContaminationChanged,
|
||
this, &RecvFromDdsAes::onBcContaminationChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionAreaChanged,
|
||
this, &RecvFromDdsAes::onExplosionAreaChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionSingleChanged,
|
||
this, &RecvFromDdsAes::onExplosionSingleChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::mineAreaChanged,
|
||
this, &RecvFromDdsAes::onMineAreaChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::nContaminationChanged,
|
||
this, &RecvFromDdsAes::onNContaminationChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::triggeredImpactsChanged,
|
||
this, &RecvFromDdsAes::onTriggeredImpactsChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::dusMineStateChanged,
|
||
this, &RecvFromDdsAes::onDusMineStateChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::functionalAreaChanged,
|
||
this, &RecvFromDdsAes::onFunctionalAreaChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::technicalAreaChanged,
|
||
this, &RecvFromDdsAes::onTechnicalAreaChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::dusSingleMineChanged,
|
||
this, &RecvFromDdsAes::onDusSingleMineChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::gasPipeChanged,
|
||
this, &RecvFromDdsAes::onGasPipeChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::boobyTrapChanged,
|
||
this, &RecvFromDdsAes::onBoobyTrapChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::offRouteMineChanged,
|
||
this, &RecvFromDdsAes::onOffRouteMineChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::explosionClusterChanged,
|
||
this, &RecvFromDdsAes::onExplosionClusterChanged);
|
||
connect(m_dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::effectsChanged,
|
||
this, &RecvFromDdsAes::onEffectsChanged);
|
||
|
||
if (!m_dtcAes->enableAirSupportImpact() ||
|
||
!m_dtcAes->enableBCContamination() ||
|
||
!m_dtcAes->enableExplosionArea() ||
|
||
!m_dtcAes->enableExplosionCluster() ||
|
||
!m_dtcAes->enableEffects() ||
|
||
!m_dtcAes->enableExplosionSingle() ||
|
||
!m_dtcAes->enableMineArea() ||
|
||
!m_dtcAes->enableNContamination() ||
|
||
!m_dtcAes->enableTriggeredImpacts() ||
|
||
!m_dtcAes->enableDusMineState() ||
|
||
!m_dtcAes->enableFunctionalArea() ||
|
||
!m_dtcAes->enableTechnicalArea() ||
|
||
!m_dtcAes->enableDusSingleMine() ||
|
||
!m_dtcAes->enableGasPipe() ||
|
||
!m_dtcAes->enableBoobyTrap() ||
|
||
!m_dtcAes->enableOffRouteMine())
|
||
{
|
||
m_dtcAes.reset();
|
||
return false;
|
||
}
|
||
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::init() successful";
|
||
return true;
|
||
}
|
||
|
||
int RecvFromDdsAes::aesChargeId(int aesId) const
|
||
{
|
||
{
|
||
auto it = m_currentBCContaminations.find(aesId);
|
||
if (it != m_currentBCContaminations.end())
|
||
return it->second.chargeStruct().chargeTypeId();
|
||
}
|
||
{
|
||
auto it = m_currentExplosionArea.find(aesId);
|
||
if (it != m_currentExplosionArea.end())
|
||
return it->second.chargeStruct().chargeTypeId();
|
||
}
|
||
{
|
||
auto it = m_currentExplosionCluster.find(aesId);
|
||
if (it != m_currentExplosionCluster.end())
|
||
return it->second.chargeStruct().chargeTypeId();
|
||
}
|
||
{
|
||
auto it = m_currentExplosionSingle.find(aesId);
|
||
if (it != m_currentExplosionSingle.end())
|
||
return it->second.chargeStruct().chargeTypeId();
|
||
}
|
||
{
|
||
auto it = m_currentMineArea.find(aesId);
|
||
if (it != m_currentMineArea.end())
|
||
return it->second.chargeStruct().chargeTypeId();
|
||
}
|
||
{
|
||
auto it = m_currentNContamination.find(aesId);
|
||
if (it != m_currentNContamination.end())
|
||
return it->second.chargeStruct().chargeTypeId();
|
||
}
|
||
{
|
||
auto it = m_currentDusSingleMine.find(aesId);
|
||
if (it != m_currentDusSingleMine.end())
|
||
return it->second.chargeStruct().chargeTypeId();
|
||
}
|
||
{
|
||
auto it = m_currentGasPipes.find(aesId);
|
||
if (it != m_currentGasPipes.end())
|
||
return it->second.chargeStruct().chargeTypeId();
|
||
}
|
||
{
|
||
auto it = m_currentFunctionalArea.find(aesId);
|
||
if (it != m_currentFunctionalArea.end())
|
||
return it->second.chargeStruct().chargeTypeId();
|
||
}
|
||
{
|
||
auto it = m_currentBoobyTraps.find(aesId);
|
||
if (it != m_currentBoobyTraps.end())
|
||
return it->second.chargeStruct().chargeTypeId();
|
||
}
|
||
{
|
||
auto it = m_currentOffRouteMines.find(aesId);
|
||
if (it != m_currentOffRouteMines.end())
|
||
return it->second.chargeStruct().chargeTypeId();
|
||
}
|
||
{
|
||
auto it = m_currentTechnicalArea.find(aesId);
|
||
if (it != m_currentTechnicalArea.end())
|
||
return it->second.chargeStruct().chargeTypeId();
|
||
}
|
||
{
|
||
auto it = m_currentAirSupportImpacts.find(aesId);
|
||
if (it != m_currentAirSupportImpacts.end())
|
||
return it->second.chargeStruct().chargeTypeId();
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
TetraIcd::AreaEffectSimulationType RecvFromDdsAes::aesTypeFromId(int aesId) const
|
||
{
|
||
{
|
||
auto it = m_currentBCContaminations.find(aesId);
|
||
if (it != m_currentBCContaminations.end())
|
||
return TetraIcd::AreaEffectSimulationType::CContamination;
|
||
}
|
||
{
|
||
auto it = m_currentExplosionArea.find(aesId);
|
||
if (it != m_currentExplosionArea.end())
|
||
return TetraIcd::AreaEffectSimulationType::ExplosionArea;
|
||
}
|
||
{
|
||
auto it = m_currentExplosionCluster.find(aesId);
|
||
if (it != m_currentExplosionCluster.end())
|
||
return TetraIcd::AreaEffectSimulationType::ExplosionArea;
|
||
}
|
||
{
|
||
auto it = m_currentExplosionSingle.find(aesId);
|
||
if (it != m_currentExplosionSingle.end())
|
||
return TetraIcd::AreaEffectSimulationType::ExplosionSingle;
|
||
}
|
||
{
|
||
auto it = m_currentMineArea.find(aesId);
|
||
if (it != m_currentMineArea.end())
|
||
return TetraIcd::AreaEffectSimulationType::MineArea;
|
||
}
|
||
{
|
||
auto it = m_currentNContamination.find(aesId);
|
||
if (it != m_currentNContamination.end())
|
||
return TetraIcd::AreaEffectSimulationType::NContamination;
|
||
}
|
||
{
|
||
auto it = m_currentDusSingleMine.find(aesId);
|
||
if (it != m_currentDusSingleMine.end())
|
||
return TetraIcd::AreaEffectSimulationType::DusSingleMineSimulator;
|
||
}
|
||
{
|
||
auto it = m_currentGasPipes.find(aesId);
|
||
if (it != m_currentGasPipes.end())
|
||
return TetraIcd::AreaEffectSimulationType::GasPipeDetonation;
|
||
}
|
||
{
|
||
auto it = m_currentFunctionalArea.find(aesId);
|
||
if (it != m_currentFunctionalArea.end())
|
||
return TetraIcd::AreaEffectSimulationType::FunctionalArea;
|
||
}
|
||
{
|
||
auto it = m_currentOffRouteMines.find(aesId);
|
||
if (it != m_currentOffRouteMines.end())
|
||
return TetraIcd::AreaEffectSimulationType::MineArea;
|
||
}
|
||
{
|
||
auto it = m_currentBoobyTraps.find(aesId);
|
||
if (it != m_currentBoobyTraps.end())
|
||
return TetraIcd::AreaEffectSimulationType::MineArea;
|
||
}
|
||
{
|
||
auto it = m_currentTechnicalArea.find(aesId);
|
||
if (it != m_currentTechnicalArea.end())
|
||
return TetraIcd::AreaEffectSimulationType::TechnicalArea;
|
||
}
|
||
return TetraIcd::AreaEffectSimulationType::Unknown;
|
||
}
|
||
|
||
bool RecvFromDdsAes::sendSingleFireSMArt(const SendCesHitRequest &request)
|
||
{
|
||
AreaEffectSimulation::ExplosionSingle data;
|
||
data.chargeStruct().chargeTypeId(request.chargeTypeId());
|
||
data.effectDuration(1);
|
||
data.effectStart(request.effectTime());
|
||
data.id(request.aesId());
|
||
data.impactPosition(request.position().position());
|
||
data.isDeleted(false);
|
||
data.participantId(request.participantId());
|
||
data.partyId(request.partyId());
|
||
|
||
// must be sent to Tetra
|
||
bool bAlreadyExists = false;
|
||
const int aesTetraId = m_aesStateHandler->createAesTetraId(request.aesId(), bAlreadyExists);
|
||
|
||
if (!bAlreadyExists)
|
||
{
|
||
WARNING_LOG(m_logger) << "Trying to send a SMArt single fire to training area but it was not created until now... !?";
|
||
bool bOnlyNameChanged;
|
||
dataChanged(data, bOnlyNameChanged); //stores the object in internal map
|
||
}
|
||
|
||
sendCreateEffectSimulation(data, aesTetraId, request.equipmentNumber());
|
||
m_aesStateHandler->update(request.aesId(), aesTetraId, true);
|
||
return true;
|
||
}
|
||
|
||
|
||
/* create + modify
|
||
* - muss es nach Tetra?
|
||
* ja: -> wenn nötig wirkfeldkennung erzeugen & store to db, telegramm erzeugen (ggf.nur namensaenderung)
|
||
* nein: -> ggf. aus tetra entfernen
|
||
* delete
|
||
* - war es in Tetra verfügbar
|
||
* ja: -> Löschtelegramm erzeugen
|
||
* nein: -> ignore
|
||
*/
|
||
template <typename T>
|
||
void RecvFromDdsAes::handleEffectSimulation(const T &data, DataModification modType, TetraIcd::AreaEffectSimulationType aesType)
|
||
{
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::handleEffectSimulation(id=" << data.id()
|
||
<< ", modType: " << static_cast<int>(modType)
|
||
<< ", type=" << static_cast<int>(aesType)
|
||
<< ", name: " << data.name()
|
||
<< ", state: " << data.effectStateId()
|
||
<< ", old state: " << m_currentEffectState.value(data.id()) << ")";
|
||
|
||
if (std::is_same<T, AreaEffectSimulation::ExplosionSingle>::value ||
|
||
std::is_same<T, AreaEffectSimulation::ExplosionArea>::value ||
|
||
std::is_same<T, AreaEffectSimulation::ExplosionCluster>::value
|
||
)
|
||
{
|
||
if (m_mainHandler->pdmHandler()->isSmartChargeType(data.chargeStruct()))
|
||
{
|
||
DEBUG_LOG(m_logger) << "Do not send SMART Single/AreaFire to Tetra as broadcast, hits are send directly.";
|
||
return;
|
||
}
|
||
}
|
||
|
||
switch (modType)
|
||
{
|
||
case DataModification::eAdded:
|
||
case DataModification::eModified:
|
||
case DataModification::eRemoved:
|
||
{
|
||
// special cases active -> pause, pause -> active for area fire
|
||
if (data.effectStateId() == AreaEffectSimulation::AesEffectState::EsPaused &&
|
||
m_currentEffectState.value(data.id()) != AreaEffectSimulation::AesEffectState::EsPaused)
|
||
{
|
||
const int aesTetraId = m_aesStateHandler->getAesTetraId(data.id());
|
||
if (aesTetraId <= 0)
|
||
{
|
||
// make sure AES is already in Tetra
|
||
handleSendToTetra(data, aesType);
|
||
}
|
||
// switched from active to paused
|
||
sendPauseEffectSimulation(data.id());
|
||
}
|
||
else if (data.effectStateId() == AreaEffectSimulation::AesEffectState::EsActivated &&
|
||
m_currentEffectState.value(data.id()) == AreaEffectSimulation::AesEffectState::EsPaused)
|
||
{
|
||
// switched from paused to active
|
||
sendResumeEffectSimulation(data);
|
||
}
|
||
else if (!data.effectStart().is_set() ||
|
||
data.effectStart().get() == 0 ||
|
||
data.effectStateId() == AreaEffectSimulation::AesEffectState::EsRemoved ||
|
||
data.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired)
|
||
{
|
||
// we have to add expired aes items too, so we can get info about them
|
||
if (modType == DataModification::eAdded)
|
||
{
|
||
bool placeHolder;
|
||
|
||
dataChanged(data, placeHolder);
|
||
}
|
||
|
||
// no effect start or removed/expired -> remove effect simulation
|
||
// --> maybe was already sent to TA -> remove from TA
|
||
sendRemoveEffectSimulation(aesType, data.id());
|
||
resetStartTime(data);
|
||
}
|
||
else
|
||
{
|
||
// special treat for functional area
|
||
// true if not functional area or funtional area, but activated since prepared functional areas must not be sent out
|
||
if (!(std::is_same<T, AreaEffectSimulation::FunctionalArea>::value) ||
|
||
(data.effectStateId() == AreaEffectSimulation::AesEffectState::EsActivated))
|
||
handleSendToTetra(data, aesType);
|
||
}
|
||
m_currentEffectState[data.id()] = data.effectStateId();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
template <typename T>
|
||
void RecvFromDdsAes::handleSendToTetra(const T &data, TetraIcd::AreaEffectSimulationType aesType)
|
||
{
|
||
// must be sent to Tetra
|
||
bool bAlreadyExists = false;
|
||
const int aesTetraId = m_aesStateHandler->createAesTetraId(data.id(), bAlreadyExists);
|
||
|
||
bool bOnlyNameChanged = false;
|
||
// if new data -> bSomethingChanged = true, otherwise do check
|
||
bool bSomethingChanged = false;;
|
||
if (!bAlreadyExists)
|
||
{
|
||
dataChanged(data, bOnlyNameChanged); //stores the object in internal map
|
||
bSomethingChanged = true;
|
||
}
|
||
else
|
||
bSomethingChanged = dataChanged(data, bOnlyNameChanged);
|
||
|
||
|
||
if (bOnlyNameChanged &&
|
||
!std::is_same<T, AreaEffectSimulation::GasPipe>::value &&
|
||
!std::is_same<T, AreaEffectSimulation::TechnicalArea>::value) // Technical area always with create
|
||
{
|
||
sendModifyEffectSimulationName(aesType,
|
||
m_aesStateHandler->getAesTetraId(data.id()),
|
||
data.name(),
|
||
data.id());
|
||
m_aesStateHandler->update(data.id(), aesTetraId, true);
|
||
}
|
||
else if (bSomethingChanged)
|
||
{
|
||
sendCreateEffectSimulation(data, aesTetraId);
|
||
m_aesStateHandler->update(data.id(), aesTetraId, true);
|
||
}
|
||
}
|
||
|
||
void RecvFromDdsAes::onAirSupportImpactChanged(const AreaEffectSimulation::AirSupportImpact &data, DataModification modType)
|
||
{
|
||
// is ExplosionSingle in CTAS!
|
||
handleEffectSimulation(data, modType, TetraIcd::AreaEffectSimulationType::ExplosionSingle);
|
||
}
|
||
|
||
void RecvFromDdsAes::onBcContaminationChanged(const AreaEffectSimulation::BCContamination &data, DataModification modType)
|
||
{
|
||
handleEffectSimulation(data, modType, TetraIcd::AreaEffectSimulationType::CContamination);
|
||
}
|
||
|
||
void RecvFromDdsAes::onExplosionAreaChanged(const AreaEffectSimulation::ExplosionArea &data, DataModification modType)
|
||
{
|
||
handleEffectSimulation(data, modType, TetraIcd::AreaEffectSimulationType::ExplosionArea);
|
||
}
|
||
|
||
void RecvFromDdsAes::onExplosionSingleChanged(const AreaEffectSimulation::ExplosionSingle &data, DataModification modType)
|
||
{
|
||
handleEffectSimulation(data, modType, TetraIcd::AreaEffectSimulationType::ExplosionSingle);
|
||
}
|
||
|
||
void RecvFromDdsAes::onMineAreaChanged(const AreaEffectSimulation::MineArea &data, DataModification modType)
|
||
{
|
||
handleEffectSimulation(data, modType, TetraIcd::AreaEffectSimulationType::MineArea);
|
||
|
||
// check if there already is a single mine state to handle for this area
|
||
auto it = m_temporarySingleMineState.find(data.id());
|
||
if (it != m_temporarySingleMineState.end())
|
||
{
|
||
onTriggeredImpactsChanged(it->second, DataModification::eModified);
|
||
m_temporarySingleMineState.erase(data.id()); // remove after handling
|
||
}
|
||
}
|
||
|
||
void RecvFromDdsAes::onNContaminationChanged(const AreaEffectSimulation::NContamination &data, DataModification modType)
|
||
{
|
||
handleEffectSimulation(data, modType, TetraIcd::AreaEffectSimulationType::NContamination);
|
||
}
|
||
|
||
void RecvFromDdsAes::onFunctionalAreaChanged(const AreaEffectSimulation::FunctionalArea &data, DataModification modType)
|
||
{
|
||
handleEffectSimulation(data, modType, TetraIcd::AreaEffectSimulationType::FunctionalArea);
|
||
}
|
||
|
||
void RecvFromDdsAes::onTechnicalAreaChanged(const AreaEffectSimulation::TechnicalArea &data, DataModification modType)
|
||
{
|
||
handleEffectSimulation(data, modType, TetraIcd::AreaEffectSimulationType::TechnicalArea);
|
||
}
|
||
|
||
void RecvFromDdsAes::onTriggeredImpactsChanged(const AreaEffectSimulation::TriggeredImpacts &data, DataModification modType)
|
||
{
|
||
if (modType == DataModification::eRemoved)
|
||
return;
|
||
if (data.aesTypeId() != AreaEffectSimulation::AesType::AtMineAreaSingleMines)
|
||
return;
|
||
|
||
// mine area id = 0 -> DUS-minearea for homeless DUS-mines
|
||
if (data.aesItemId() == 0)
|
||
return;
|
||
|
||
// do not send mine status if all virtual mines are still untriggered
|
||
bool completeUntriggered = true;
|
||
for (size_t i = 0; i < data.bitfield().size() && completeUntriggered; ++i)
|
||
{
|
||
if ((i + 1) * 8 <= static_cast<unsigned long>(data.impactCount())) // are all bits filled? -> no need to create a mask
|
||
{
|
||
if (data.bitfield().at(i) != 0xFF)
|
||
completeUntriggered = false;
|
||
}
|
||
else // create a mask for bits in the last byte
|
||
{
|
||
uint8_t endMask = 0;
|
||
for (uint32_t j = 0; j < (static_cast<unsigned long>(data.impactCount()) - i * 8); ++j)
|
||
endMask |= 0x80 >> j;
|
||
|
||
if (data.bitfield().at(i) != endMask)
|
||
completeUntriggered = false;
|
||
}
|
||
}
|
||
if (completeUntriggered) // only send, if mines have been triggered
|
||
return;
|
||
|
||
// ignore expired, removed or faulty mineAreas
|
||
auto it = m_currentMineArea.find(data.aesItemId());
|
||
if (it != m_currentMineArea.end())
|
||
{
|
||
const auto &mineArea = it->second;
|
||
if (mineArea.isDus() || mineArea.isConcrete())
|
||
{
|
||
INFO_LOG(m_logger) << "Not publishing mineArea single state for " << data.aesItemId() << " - is a DUS mineArea";
|
||
return;
|
||
}
|
||
|
||
switch (mineArea.effectStateId().underlying())
|
||
{
|
||
case AreaEffectSimulation::AesEffectState::EsError:
|
||
case AreaEffectSimulation::AesEffectState::EsExpired:
|
||
case AreaEffectSimulation::AesEffectState::EsRemoved:
|
||
case AreaEffectSimulation::AesEffectState::EsUnknown:
|
||
{
|
||
INFO_LOG(m_logger) << "Not publishing mineArea single state for " << data.aesItemId() << " - with state " << AreaEffectSimulation::toString(mineArea.effectStateId());
|
||
return;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
INFO_LOG(m_logger) << "Storing mineArea single state, as we dont have the corresponding mineArea yet aesId" << data.aesItemId();
|
||
m_temporarySingleMineState[data.aesItemId()] = data;
|
||
return; // do not send, as we dont have the mineArea yet
|
||
}
|
||
|
||
TetraIcd::MineAreaSingleMineState out;
|
||
//bool bAlreadyExists;
|
||
out.aesDdsId = data.aesItemId();
|
||
//out.aesTetraIdUnused = m_aesStateHandler->createAesTetraId(data.aesItemId(), bAlreadyExists);
|
||
out.mineCount = data.impactCount();
|
||
out.mineState.reserve(data.bitfield().size());
|
||
for (const auto &c : data.bitfield())
|
||
out.mineState.push_back(c);
|
||
|
||
txData(out);
|
||
}
|
||
|
||
void RecvFromDdsAes::onDusMineStateChanged(const AreaEffectSimulation::DusMineState &data, DataModification modType)
|
||
{
|
||
if (modType == DataModification::eRemoved)
|
||
return;
|
||
|
||
const auto &vec = data.mines();
|
||
if (vec.size() == 0)
|
||
return;
|
||
|
||
// insert changed/new AreaEffectSimulation::DusMineStatus
|
||
for (const auto &mine : vec)
|
||
{
|
||
// Continue, if the mine is not reactivatable. MineIds between reactivatable ones and non-reactivatable ones might overlap!
|
||
// NOTE: igniterType/igniterFunction = 0 as long als DusMineState does not contaion values
|
||
if (!m_mainHandler->pdmHandler()->isReactivatableChargeType({mine.chargeTypeId(), 0,0}))
|
||
continue;
|
||
|
||
// has the status changed? (or maybe there's no such mine in the map yet...)
|
||
if (mine != m_allReactivatableDusMines.value(mine.eqId()))
|
||
{
|
||
// Now: not triggered, before existing and triggered:
|
||
if (!mine.triggered() &&
|
||
m_allReactivatableDusMines.contains(mine.eqId()) && m_allReactivatableDusMines.value(mine.eqId()).triggered() // has been reactivated
|
||
)
|
||
m_minesToSend << mine.eqId();
|
||
// Anyways: Insert
|
||
m_allReactivatableDusMines.insert(mine.eqId(), mine);
|
||
}
|
||
}
|
||
|
||
if (!m_minesToSend.isEmpty())
|
||
m_sendDusMineReactivtionTimer->start(); // timer since there may be a lot of DusMineState changes in a time (Reset all mines)
|
||
else
|
||
INFO_LOG(m_logger) << "Nothing changed in DusMineState of MineArea: " << data.id();
|
||
}
|
||
|
||
void RecvFromDdsAes::onSendDusMineReactivationTimeout()
|
||
{
|
||
TetraIcd::DusMineReactivation out;
|
||
out.dusSerialNumbers.reserve(m_minesToSend.size());
|
||
for (const auto &mineId : qAsConst(m_minesToSend))
|
||
{
|
||
m_allReactivatableDusMines[mineId].triggered(false);
|
||
out.dusSerialNumbers.push_back(mineId);
|
||
}
|
||
m_minesToSend.clear();
|
||
INFO_LOG(m_logger) << "Reactivating " << out.dusSerialNumbers.size() << " mines";
|
||
txData(out);
|
||
}
|
||
|
||
void RecvFromDdsAes::onDusSingleMineChanged(const AreaEffectSimulation::DusSingleMine &data, DataModification modType)
|
||
{
|
||
handleEffectSimulation(data, modType, TetraIcd::AreaEffectSimulationType::DusSingleMineSimulator);
|
||
}
|
||
|
||
void RecvFromDdsAes::onGasPipeChanged(const AreaEffectSimulation::GasPipe &data, DataModification modType)
|
||
{
|
||
handleEffectSimulation(data, modType, TetraIcd::AreaEffectSimulationType::DusSingleMineSimulator);
|
||
}
|
||
|
||
void RecvFromDdsAes::onBoobyTrapChanged(const AreaEffectSimulation::BoobyTrap &data, DataModification modType)
|
||
{
|
||
Q_UNUSED(modType);
|
||
fillCentralChangesTable(data); //Only calculated central
|
||
}
|
||
|
||
void RecvFromDdsAes::onOffRouteMineChanged(const AreaEffectSimulation::OffRouteMine &data, DataModification modType)
|
||
{
|
||
Q_UNUSED(modType);
|
||
fillCentralChangesTable(data); //Only calculated central
|
||
}
|
||
|
||
void RecvFromDdsAes::onExplosionClusterChanged(const AreaEffectSimulation::ExplosionCluster &data, DataModification modType)
|
||
{
|
||
handleEffectSimulation(data, modType, TetraIcd::AreaEffectSimulationType::ExplosionArea);
|
||
}
|
||
|
||
template<typename T, typename S>
|
||
void setCommonParams(T &out, const S &in, const int aesTetraId)
|
||
{
|
||
out.aesDdsId = in.id();
|
||
Q_UNUSED(aesTetraId);
|
||
//out.aesTetraIdUnused = aesTetraId;
|
||
out.name = in.name();
|
||
out.partyId = in.partyId().is_set() ? in.partyId().get() : 0;
|
||
}
|
||
|
||
template<typename T, typename S>
|
||
void setCommonCombatParams(T &out, const S &in, const int aesTetraId)
|
||
{
|
||
setCommonParams(out, in, aesTetraId);
|
||
out.effectStart = in.effectStart().is_set() ? in.effectStart().get() : 0;
|
||
out.effectDuration = in.effectDuration();
|
||
}
|
||
|
||
|
||
void RecvFromDdsAes::onEffectsChanged(const AreaEffectSimulation::Effects &data, DataModification modType)
|
||
{
|
||
if (modType == DataModification::eRemoved)
|
||
return;
|
||
|
||
// Currently, we are only processing AreaEffectSimulation::Effects for laserGuided ammo like vulcano:
|
||
|
||
// skip by enum aesTypeId
|
||
// object_explosion_cluster.cpp: m_effects.aesTypeId(AesType::AtExplosionClusterSingleExplosions);
|
||
if (data.aesTypeId().underlying() != AreaEffectSimulation::AesType_def::AtExplosionClusterSingleExplosions)
|
||
return;
|
||
|
||
const int aesItemId = data.aesItemId();
|
||
if (aesItemId <= 0)
|
||
return;
|
||
|
||
// Now look for ExplosionCluster to check ammo:
|
||
const AreaEffectSimulation::ExplosionCluster &aes = m_dtcAes->explosionCluster(aesItemId);
|
||
if (aes.id() != aesItemId)
|
||
return;
|
||
|
||
// IsVulcano / LaserGuidable
|
||
const bool bIsLaserGuidable = m_mainHandler->pdmHandler()->isLaserGuidable(aes.chargeStruct());
|
||
if (!bIsLaserGuidable)
|
||
return;
|
||
|
||
// So, now we have just laserGuidable effects:
|
||
|
||
// code copied from sendCreateEffectSimulation:
|
||
const std::vector<AreaEffectSimulation::SingleEffect> &singleEffectsRef = data.singleEffects();
|
||
|
||
if (!singleEffectsRef.empty())
|
||
{
|
||
// like before:
|
||
bool bAlreadyExists = false;
|
||
const int aesTetraId = m_aesStateHandler->createAesTetraId(aesItemId, bAlreadyExists);
|
||
|
||
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::onEffectsChanged(Effects, "
|
||
<< aesTetraId << ")";
|
||
TetraIcd::CreateAreaExplosion cAes;
|
||
|
||
setCommonCombatParams(cAes, aes, aesTetraId);
|
||
if(cAes.effectStart > 0)
|
||
{
|
||
qint64 delayMSec = Settings::explosionToEffectDelayInMSec();
|
||
if (delayMSec < 0)
|
||
delayMSec = 3000;
|
||
cAes.effectStart += (delayMSec); //add some seconds to start time
|
||
}
|
||
|
||
// // cAes.relCorner = m_posHandler->geographicToRelative(aes.area().cornerBottomLeft(), 0);
|
||
const AreaEffectSimulation::SingleEffect &firstSingleEffect = singleEffectsRef.front();
|
||
const Core::GeoPosition &fsePosition = firstSingleEffect.position();
|
||
cAes.relCorner = m_posHandler->geographicToRelative(fsePosition); // overloaded with Core::GeoPosition
|
||
|
||
cAes.width = qRound(aes.area().width());
|
||
cAes.height = qRound(aes.area().height());
|
||
cAes.rotationAngle = aes.area().rotation();
|
||
cAes.mhpRotation = aes.mhpRotation();
|
||
cAes.mhpWidth = aes.mhpWidth();
|
||
cAes.mhpLength = aes.mhpLength();
|
||
cAes.impactCount = aes.impactCount();
|
||
cAes.impactEdgeLength = qRound(pdm()->impactEdgeLength(aes.chargeStruct()));
|
||
cAes.hazardDistance = m_mainHandler->pdmHandler()->hazardDistance(aes.chargeStruct());
|
||
|
||
std::vector<TetraIcd::RelativePosition> &mhPointsRef = cAes.mhPoints;
|
||
mhPointsRef.clear();
|
||
mhPointsRef.reserve(aes.mhPoints().size());
|
||
for (const Core::GeoPosition2D &pos : qAsConst(aes.mhPoints()))
|
||
mhPointsRef.emplace_back(m_posHandler->geographicToRelative(pos, 0));
|
||
|
||
// Explosion Areas need Legacy Indirect TAS Codes:
|
||
// const auto chargeType(pdm()->chargeTypeFromId(aes.chargeTypeId()));
|
||
// cAes.chargeTypeId = chargeType.legacyId() > 0 ? chargeType.legacyId() : chargeType.id();
|
||
// if (cAes.chargeTypeId <= 0)
|
||
// return;
|
||
|
||
cAes.chargeTypeId = 254; // to trigger tracer or similar, value 254 selected by Malte Diers
|
||
|
||
txData(cAes);
|
||
}
|
||
}
|
||
|
||
void RecvFromDdsAes::sendCreateEffectSimulation(const AreaEffectSimulation::AirSupportImpact &aes, int aesTetraId)
|
||
{
|
||
if (aes.isCentral())
|
||
{
|
||
sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType::ExplosionSingle, aes.id());
|
||
return;
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendCreateEffectSimulation(AirSupportImpact, "
|
||
<< aesTetraId << ")";
|
||
TetraIcd::CreateSingleExplosion cAes;
|
||
|
||
setCommonCombatParams(cAes, aes, aesTetraId);
|
||
// cAes.header.equipmentNumber = equipmentNumber;
|
||
cAes.header.equipmentNumber = 0;
|
||
cAes.relEffectPosition = m_posHandler->geographicToRelative(aes.impactPosition(), 0);
|
||
|
||
// Air Support Impact needs Legacy Indirect TAS Codes:
|
||
const auto chargeType(pdm()->chargeTypeFromId(aes.chargeStruct().chargeTypeId()));
|
||
cAes.chargeTypeId = chargeType.legacyId() > 0 ? chargeType.legacyId() : chargeType.id();
|
||
|
||
cAes.effectDiameter = qRound(2 * pdm()->impactRadius(aes.chargeStruct()));
|
||
|
||
if (cAes.chargeTypeId <= 0)
|
||
return;
|
||
|
||
txData(cAes);
|
||
}
|
||
|
||
void RecvFromDdsAes::sendCreateEffectSimulation(const AreaEffectSimulation::BCContamination &aes, int aesTetraId)
|
||
{
|
||
if (aes.isCentral())
|
||
{
|
||
sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType::CContamination, aes.id());
|
||
return;
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendCreateEffectSimulation(BCContamination, "
|
||
<< aesTetraId << ")";
|
||
TetraIcd::CreateCContamination cAes;
|
||
cAes.expansionFactor = pdm()->dtc()->expansionFactor(aes.chargeStruct().chargeTypeId(),
|
||
aes.chargeStruct().igniterTypeId(),
|
||
aes.chargeStruct().igniterFunctionId());
|
||
|
||
setCommonCombatParams(cAes, aes, aesTetraId);
|
||
cAes.relEffectPosition = m_posHandler->geographicToRelative(aes.impactPosition(), 0);
|
||
cAes.effectRadius = qRound(aes.impactRadius());
|
||
cAes.propagationDirection = aes.propagationDirection();
|
||
cAes.windSpeed = qRound(aes.windSpeed());
|
||
cAes.validityTimeWind = aes.weatherTimestamp();
|
||
cAes.relPositionValidityTimeWind = m_posHandler->geographicToRelative(aes.weatherPosition(), 0);
|
||
|
||
// BC Contamination needs Legacy Indirect TAS Codes:
|
||
const auto chargeType(pdm()->chargeTypeFromId(aes.chargeStruct().chargeTypeId()));
|
||
cAes.chargeTypeId = chargeType.legacyId() > 0 ? chargeType.legacyId() : chargeType.id();
|
||
|
||
switch (pdm()->dtc()->volatilityTypeId(aes.chargeStruct().chargeTypeId(),
|
||
aes.chargeStruct().igniterTypeId(),
|
||
aes.chargeStruct().igniterFunctionId()).underlying())
|
||
{
|
||
case ProjectDataManagement::VolatilityType::Floating:
|
||
cAes.volatility = TetraIcd::CreateCContamination::Volatiliy::Floating;
|
||
break;
|
||
case ProjectDataManagement::VolatilityType::Resident:
|
||
cAes.volatility = TetraIcd::CreateCContamination::Volatiliy::Resident;
|
||
break;
|
||
default:
|
||
cAes.volatility = TetraIcd::CreateCContamination::Volatiliy::Resident;
|
||
break;
|
||
}
|
||
|
||
if (cAes.chargeTypeId <= 0)
|
||
return;
|
||
|
||
txData(cAes);
|
||
}
|
||
|
||
void RecvFromDdsAes::sendCreateEffectSimulation(const AreaEffectSimulation::ExplosionArea &aes, int aesTetraId)
|
||
{
|
||
if (aes.isCentral())
|
||
{
|
||
sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType::ExplosionArea, aes.id());
|
||
return;
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendCreateEffectSimulation(ExplosionArea, "
|
||
<< aesTetraId << ")";
|
||
TetraIcd::CreateAreaExplosion cAes;
|
||
|
||
setCommonCombatParams(cAes, aes, aesTetraId);
|
||
cAes.relCorner = m_posHandler->geographicToRelative(aes.area().cornerBottomLeft(), 0);
|
||
cAes.width = qRound(aes.area().width());
|
||
cAes.height = qRound(aes.area().height());
|
||
cAes.rotationAngle = aes.area().rotation();
|
||
cAes.impactCount = aes.cannonCount() * aes.cannonRounds();
|
||
cAes.impactEdgeLength = qRound(pdm()->impactEdgeLength(aes.chargeStruct()));
|
||
cAes.hazardDistance = m_mainHandler->pdmHandler()->hazardDistance(aes.chargeStruct());
|
||
|
||
// Explosion Areas need Legacy Indirect TAS Codes:
|
||
const auto chargeType(pdm()->chargeTypeFromId(aes.chargeStruct().chargeTypeId()));
|
||
cAes.chargeTypeId = chargeType.legacyId() > 0 ? chargeType.legacyId() : chargeType.id();
|
||
|
||
if (cAes.chargeTypeId <= 0)
|
||
return;
|
||
|
||
txData(cAes);
|
||
}
|
||
|
||
void RecvFromDdsAes::sendCreateEffectSimulation(const AreaEffectSimulation::ExplosionCluster &aes, int aesTetraId)
|
||
{
|
||
// IsVulcano / LaserGuidable
|
||
const bool bIsLaserGuidable = m_mainHandler->pdmHandler()->isLaserGuidable(aes.chargeStruct());
|
||
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendCreateEffectSimulation "
|
||
<< ", aes " << aes
|
||
<< ", bIsLaserGuidable " << bIsLaserGuidable;
|
||
|
||
if (bIsLaserGuidable)
|
||
{
|
||
// It´s typical for isLaserGuidable ammo, that the real impact is not know at the beginning.
|
||
// So return here and continue at onEffectsChanged();
|
||
return;
|
||
}
|
||
|
||
if (aes.isCentral())
|
||
{
|
||
sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType::ExplosionArea, aes.id());
|
||
return;
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendCreateEffectSimulation(ExplosionArea, "
|
||
<< aesTetraId << ")";
|
||
TetraIcd::CreateAreaExplosion cAes;
|
||
|
||
setCommonCombatParams(cAes, aes, aesTetraId);
|
||
//if(cAes.effectStart > 0)
|
||
// cAes.effectStart+=aes.originalFlightDuration(); //add flight_duration to start time time
|
||
cAes.relCorner = m_posHandler->geographicToRelative(aes.area().cornerBottomLeft(), 0);
|
||
cAes.width = qRound(aes.area().width());
|
||
cAes.height = qRound(aes.area().height());
|
||
cAes.rotationAngle = aes.area().rotation();
|
||
cAes.mhpRotation = aes.mhpRotation();
|
||
cAes.mhpWidth = aes.mhpWidth();
|
||
cAes.mhpLength = aes.mhpLength();
|
||
cAes.impactCount = aes.impactCount();
|
||
cAes.impactEdgeLength = qRound(pdm()->impactEdgeLength(aes.chargeStruct()));
|
||
cAes.impactRadius = m_mainHandler->pdmHandler()->impactRadius(aes.chargeStruct());
|
||
std::vector<TetraIcd::RelativePosition> &mhPointsRef = cAes.mhPoints;
|
||
mhPointsRef.clear();
|
||
mhPointsRef.reserve(aes.mhPoints().size());
|
||
for (const Core::GeoPosition2D &pos : qAsConst(aes.mhPoints()))
|
||
mhPointsRef.emplace_back(m_posHandler->geographicToRelative(pos, 0));
|
||
|
||
int chargeTypeId = 0;
|
||
const auto chargeType(pdm()->chargeTypeFromId(aes.chargeStruct().chargeTypeId()));
|
||
chargeTypeId = pdm()->legacyChargeTypeIgniterType(aes.chargeStruct().chargeTypeId(), aes.chargeStruct().igniterTypeId(), aes.chargeStruct().igniterFunctionId());
|
||
|
||
//fallback
|
||
// Explosion Areas need Legacy Indirect TAS Codes:
|
||
if (chargeTypeId > 0)
|
||
cAes.chargeTypeId = chargeTypeId;
|
||
else
|
||
{
|
||
cAes.chargeTypeId = chargeType.legacyId() > 0 ? chargeType.legacyId() : chargeType.id();
|
||
}
|
||
|
||
if (cAes.chargeTypeId <= 0)
|
||
return;
|
||
|
||
txData(cAes);
|
||
}
|
||
|
||
void RecvFromDdsAes::sendCreateEffectSimulation(const AreaEffectSimulation::ExplosionSingle &aes, int aesTetraId, int equipmentNumber)
|
||
{
|
||
if (aes.isCentral())
|
||
{
|
||
sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType::ExplosionSingle, aes.id());
|
||
return;
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendCreateEffectSimulation(ExplosionSingle, "
|
||
<< aesTetraId << ")";
|
||
TetraIcd::CreateSingleExplosion cAes;
|
||
|
||
setCommonCombatParams(cAes, aes, aesTetraId);
|
||
cAes.header.equipmentNumber = equipmentNumber;
|
||
cAes.relEffectPosition = m_posHandler->geographicToRelative(aes.impactPosition(), 0);
|
||
cAes.effectDiameter = qRound(2 * pdm()->impactRadius(aes.chargeStruct()));
|
||
|
||
// Single Explosions need Legacy Indirect TAS Codes:
|
||
const auto chargeType(pdm()->chargeTypeFromId(aes.chargeStruct().chargeTypeId()));
|
||
cAes.chargeTypeId = chargeType.legacyId() > 0 ? chargeType.legacyId() : chargeType.id();
|
||
|
||
if (cAes.chargeTypeId <= 0)
|
||
return;
|
||
|
||
txData(cAes);
|
||
}
|
||
|
||
void RecvFromDdsAes::sendCreateEffectSimulation(const AreaEffectSimulation::MineArea &aes, int aesTetraId)
|
||
{
|
||
if (aes.isCentral())
|
||
{
|
||
sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType::MineArea, aes.id());
|
||
return;
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendCreateEffectSimulation(MineArea, "
|
||
<< aesTetraId << ")";
|
||
TetraIcd::CreateMineArea cAes;
|
||
|
||
setCommonCombatParams(cAes, aes, aesTetraId);
|
||
// cAes.relEffectPosition = m_posHandler->geographicToRelative(aes.boundingRectCorner(), 0);
|
||
// cAes.areaWidth = qRound(aes.boundingRectWidth());
|
||
// cAes.areaHeight = qRound(aes.boundingRectHeight());
|
||
cAes.layingDepth = aes.width();
|
||
cAes.layingDensity = aes.density();
|
||
|
||
// Mines might need Legacy Indirect TAS Codes:
|
||
const auto chargeType(pdm()->chargeTypeFromId(aes.chargeStruct().chargeTypeId()));
|
||
cAes.chargeTypeId = chargeType.legacyId() > 0 ? chargeType.legacyId() : chargeType.id();
|
||
|
||
TetraIcd::MineAreaSegment segment;
|
||
cAes.segments.reserve(aes.segments().size());
|
||
for (size_t i = 0; i < aes.segments().size(); ++i)
|
||
{
|
||
const AreaEffectSimulation::MineSegment &s = aes.segments().at(i);
|
||
if (aes.isCenterLine())
|
||
{
|
||
segment.relStart = m_posHandler->geographicToRelative(s.positionStart(), 0);
|
||
segment.relEnd = m_posHandler->geographicToRelative(s.positionEnd(), 0);
|
||
}
|
||
else
|
||
{
|
||
const QPointF relStart = m_posHandler->geographicToRelativePointF(s.positionStart());
|
||
const QPointF relEnd = m_posHandler->geographicToRelativePointF(s.positionEnd());
|
||
const QLineF line({relStart.x(), relStart.y()}, {relEnd.x(), relEnd.y()});
|
||
DEBUG_LOG(m_logger) << "relStart: x: " << relStart.x() << ", y: " << relStart.y();
|
||
DEBUG_LOG(m_logger) << "relEnd: x: " << relEnd.x() << ", y: " << relEnd.y();
|
||
QTransform trans;
|
||
trans.rotate(0-line.angle());
|
||
const QPointF ofs = trans.map(QPointF(aes.width() / 2, 0));
|
||
DEBUG_LOG(m_logger) << "ofs: x: " << ofs.x() << ", y: " << ofs.y();
|
||
|
||
segment.relStart.offsetX = qRound(relStart.x() + ofs.y());
|
||
segment.relStart.offsetY = qRound(relStart.y() - ofs.x());
|
||
segment.relEnd.offsetX = qRound(relEnd.x() + ofs.y());
|
||
segment.relEnd.offsetY = qRound(relEnd.y() - ofs.x());
|
||
|
||
DEBUG_LOG(m_logger) << "start offset: x: " << segment.relStart.offsetX << ", y: " << segment.relStart.offsetY;
|
||
DEBUG_LOG(m_logger) << "end offset: x: " << segment.relEnd.offsetX << ", y: " << segment.relEnd.offsetY;
|
||
}
|
||
cAes.segments.push_back(segment);
|
||
}
|
||
|
||
if (cAes.chargeTypeId <= 0)
|
||
return;
|
||
|
||
txData(cAes);
|
||
}
|
||
|
||
void RecvFromDdsAes::sendCreateEffectSimulation(const AreaEffectSimulation::NContamination &aes, int aesTetraId)
|
||
{
|
||
if (aes.isCentral())
|
||
{
|
||
sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType::NContamination, aes.id());
|
||
return;
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendCreateEffectSimulation(NContamination, "
|
||
<< aesTetraId << ")";
|
||
TetraIcd::CreateNContamination cAes;
|
||
|
||
setCommonCombatParams(cAes, aes, aesTetraId);
|
||
cAes.relEffectPosition = m_posHandler->geographicToRelative(aes.impactPosition(), 0);
|
||
cAes.innerRadius = qRound(aes.impactRadius());
|
||
cAes.borderLengthInner = qRound(aes.borderLengthInner());
|
||
cAes.borderLengthOuter = qRound(aes.borderLengthOuter());
|
||
cAes.spanAngle = aes.spanAngle();
|
||
cAes.rotationAngle = aes.rotationAngle();
|
||
// ChargeType Id not needed for NContaminations
|
||
cAes.chargeTypeId = aes.chargeStruct().chargeTypeId();
|
||
|
||
txData(cAes);
|
||
}
|
||
|
||
void RecvFromDdsAes::sendCreateEffectSimulation(const AreaEffectSimulation::FunctionalArea &aes, int aesTetraId)
|
||
{
|
||
if (aes.isCentral())
|
||
{
|
||
sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType::FunctionalArea, aes.id());
|
||
return;
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendCreateEffectSimulation(FunctionalArea, "
|
||
<< aesTetraId << ")";
|
||
TetraIcd::CreateFunctionalArea fAes;
|
||
|
||
setCommonParams(fAes, aes, aesTetraId);
|
||
fAes.relEffectPosition = m_posHandler->geographicToRelative(aes.area().cornerBottomLeft(), 0);
|
||
fAes.width = qRound(aes.area().width());
|
||
fAes.height = qRound(aes.area().height());
|
||
fAes.deconMode = static_cast<TetraIcd::FunctionalAreaOperatingMode>(aes.deconMode().underlying());
|
||
fAes.deconTimeout = aes.deconTime();
|
||
fAes.maintenanceMode = static_cast<TetraIcd::FunctionalAreaOperatingMode>(aes.maintMode().underlying());
|
||
fAes.maintenanceTimeout = aes.maintTime();
|
||
fAes.medicalMode = static_cast<TetraIcd::FunctionalAreaOperatingMode>(aes.medicMode().underlying());
|
||
fAes.medicalTimeout = aes.medicTime();
|
||
|
||
txData(fAes);
|
||
}
|
||
|
||
void RecvFromDdsAes::sendCreateEffectSimulation(const AreaEffectSimulation::TechnicalArea &aes, int aesTetraId)
|
||
{
|
||
if (aes.isCentral())
|
||
{
|
||
sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType::TechnicalArea, aes.id());
|
||
return;
|
||
}
|
||
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendCreateEffectSimulation(TechnicalArea, "
|
||
<< aesTetraId << ")";
|
||
TetraIcd::TechnicalArea fAes;
|
||
|
||
fAes.aesDdsId = aes.id();
|
||
//fAes.aesTetraIdUnused = aesTetraId;
|
||
fAes.name = aes.name();
|
||
fAes.taType = static_cast<TetraIcd::TechnicalArea::Type>(aes.type().underlying());
|
||
fAes.relCorner = m_posHandler->geographicToRelative(aes.area().cornerBottomLeft(), 0);
|
||
fAes.width = qRound(aes.area().width());
|
||
fAes.height = qRound(aes.area().height());
|
||
|
||
txData(fAes);
|
||
}
|
||
|
||
void RecvFromDdsAes::sendCreateEffectSimulation(const AreaEffectSimulation::DusSingleMine &aes, int aesTetraId)
|
||
{
|
||
if (aes.isCentral())
|
||
{
|
||
sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType::DusSingleMineSimulator, aes.id());
|
||
return;
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendCreateEffectSimulation(DusSingleMine, "
|
||
<< aesTetraId << ")";
|
||
TetraIcd::CreateDusSingleMine dAes;
|
||
|
||
setCommonParams(dAes, aes, aesTetraId);
|
||
dAes.relEffectPosition = m_posHandler->geographicToRelative(aes.position(), 0);
|
||
dAes.effectStart = aes.effectStart().get(); // is always set for DUS single mine
|
||
dAes.effectDuration = aes.effectDuration();
|
||
dAes.mineId = aes.mineId();
|
||
// Mines might need Legacy Indirect TAS Codes:
|
||
const auto chargeType(pdm()->chargeTypeFromId(aes.chargeStruct().chargeTypeId()));
|
||
dAes.chargeTypeId = chargeType.legacyId() > 0 ? chargeType.legacyId() : chargeType.id();
|
||
|
||
txData(dAes);
|
||
}
|
||
|
||
void RecvFromDdsAes::sendCreateEffectSimulation(const AreaEffectSimulation::GasPipe &aes, int aesTetraId)
|
||
{
|
||
// not send to TAB
|
||
Q_UNUSED(aes);
|
||
Q_UNUSED(aesTetraId);
|
||
}
|
||
|
||
|
||
void RecvFromDdsAes::sendModifyEffectSimulationName(TetraIcd::AreaEffectSimulationType aesType, int aesTetraId, const std::string &name, int aesId)
|
||
{
|
||
|
||
if (aesTetraId == 0)
|
||
{
|
||
WARNING_LOG(m_logger) << "Trying to modify an effect simulation name but have no aes identification: " << name;
|
||
return;
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendModifyEffectSimulationName(" << static_cast<int>(aesType)
|
||
<< ", " << aesTetraId << ", " << name << ")";
|
||
|
||
TetraIcd::ModifyAreaEffectSimulationName mAes;
|
||
mAes.aesType = aesType;
|
||
mAes.aesDdsId = aesId;
|
||
//mAes.aesTetraIdUnused = aesTetraId;
|
||
mAes.name = name;
|
||
|
||
txData(mAes);
|
||
}
|
||
|
||
void RecvFromDdsAes::sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType aesType, int aesDdsId)
|
||
{
|
||
if (aesDdsId == 0)
|
||
return;
|
||
if (aesType == TetraIcd::AreaEffectSimulationType::GasPipeDetonation)
|
||
return;
|
||
|
||
// we are sending the remove telegram - no matter if it was sent already or not. PrtCnv must take care
|
||
// to not sent it out if it is not needed (e.g. it is already in the sammeldeaktivierung)
|
||
const int aesTetraId = m_aesStateHandler->getAesTetraId(aesDdsId);
|
||
if (aesTetraId == 0)
|
||
{
|
||
DEBUG_LOG(m_logger) << "Could not find a valid tetra id for aes dds id " << aesDdsId;
|
||
return;
|
||
}
|
||
|
||
if (!m_aesStateHandler->update(aesDdsId, aesTetraId, false))
|
||
{
|
||
WARNING_LOG(m_logger) << "Could not update Aes state handler (" << aesDdsId
|
||
<< ", " << aesTetraId << ")";
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendRemoveEffectSimulation(id=" << aesDdsId
|
||
<< ", type=" << static_cast<int>(aesType)
|
||
<< ", tetraId=" << aesTetraId << ")";
|
||
|
||
if (aesType == TetraIcd::AreaEffectSimulationType::TechnicalArea)
|
||
{
|
||
TetraIcd::TechnicalArea fAes;
|
||
fAes.taType = TetraIcd::TechnicalArea::RemoveArea;
|
||
fAes.aesDdsId = aesDdsId;
|
||
//fAes.aesTetraIdUnused = aesTetraId;
|
||
txData(fAes);
|
||
}
|
||
else
|
||
{
|
||
TetraIcd::DeactivateEffectSimulation dAes;
|
||
dAes.aesType = aesType;
|
||
dAes.aesDdsId = aesDdsId;
|
||
//dAes.aesTetraIdUnused = aesTetraId;
|
||
txData(dAes);
|
||
}
|
||
}
|
||
|
||
void RecvFromDdsAes::sendPauseEffectSimulation(int aesDdsId)
|
||
{
|
||
if (aesDdsId == 0)
|
||
return;
|
||
const int aesTetraId = m_aesStateHandler->getAesTetraId(aesDdsId);
|
||
if (aesTetraId == 0)
|
||
{
|
||
DEBUG_LOG(m_logger) << "Could not find a valid tetra id for aes dds id " << aesDdsId ;
|
||
return;
|
||
}
|
||
if (!m_aesStateHandler->update(aesDdsId, aesTetraId, true))
|
||
{
|
||
WARNING_LOG(m_logger) << "Could not update Aes state handler (" << aesDdsId
|
||
<< ", " << aesTetraId << ")";
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendPauseEffectSimulation(id=" << aesDdsId
|
||
<< ", tetraId=" << aesTetraId << ")";
|
||
|
||
TetraIcd::PauseEffectSimulation dAes;
|
||
dAes.aesDdsId = aesDdsId;
|
||
//dAes.aesTetraIdUnused = aesTetraId;
|
||
|
||
txData(dAes);
|
||
}
|
||
|
||
template<>
|
||
void RecvFromDdsAes::sendResumeEffectSimulation(const AreaEffectSimulation::ExplosionArea &aes)
|
||
{
|
||
if (aes.isCentral())
|
||
{
|
||
sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType::ExplosionArea, aes.id());
|
||
return;
|
||
}
|
||
const int aesDdsId = aes.id();
|
||
if (aesDdsId == 0)
|
||
return;
|
||
const int aesTetraId = m_aesStateHandler->getAesTetraId(aes.id());
|
||
if (aesTetraId == 0)
|
||
{
|
||
DEBUG_LOG(m_logger) << "Could not find a valid tetra id for aes dds id " << aesDdsId ;
|
||
return;
|
||
}
|
||
if (!m_aesStateHandler->update(aesDdsId, aesTetraId, true))
|
||
{
|
||
WARNING_LOG(m_logger) << "Could not update Aes state handler (" << aesDdsId
|
||
<< ", " << aesTetraId << ")";
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendPauseEffectSimulation(id=" << aesDdsId
|
||
<< ", tetraId=" << aesTetraId << ")";
|
||
|
||
TetraIcd::ResumeEffectSimulation dAes;
|
||
dAes.aesDdsId = aes.id();
|
||
//dAes.aesTetraIdUnused = aesTetraId;
|
||
dAes.sequelNumber = AreaEffectSimulation::Polygons::getSequelNumber(aes.id(),
|
||
aes.cannonCount() * aes.cannonRounds(),
|
||
aes.originalEffectDuration(),
|
||
aes.effectDuration()); // most likely _remaining_ effectDuration
|
||
dAes.nextImpact = aes.remainExplosionNumber();
|
||
dAes.resumeTime = aes.effectStart().is_set() ? aes.effectStart().get() : 0;
|
||
dAes.remainDuration = aes.effectDuration();
|
||
|
||
txData(dAes);
|
||
}
|
||
|
||
|
||
template<>
|
||
void RecvFromDdsAes::sendResumeEffectSimulation(const AreaEffectSimulation::ExplosionCluster &aes)
|
||
{
|
||
if (aes.isCentral())
|
||
{
|
||
sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType::ExplosionArea, aes.id());
|
||
return;
|
||
}
|
||
const int aesDdsId = aes.id();
|
||
if (aesDdsId == 0)
|
||
return;
|
||
const int aesTetraId = m_aesStateHandler->getAesTetraId(aes.id());
|
||
if (aesTetraId == 0)
|
||
{
|
||
WARNING_LOG(m_logger) << "Could not find a valid tetra id for aes dds id " << aesDdsId ;
|
||
return;
|
||
}
|
||
if (!m_aesStateHandler->update(aesDdsId, aesTetraId, true))
|
||
{
|
||
WARNING_LOG(m_logger) << "Could not update Aes state handler (" << aesDdsId
|
||
<< ", " << aesTetraId << ")";
|
||
}
|
||
DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendPauseEffectSimulation(id=" << aesDdsId
|
||
<< ", tetraId=" << aesTetraId << ")";
|
||
|
||
TetraIcd::ResumeEffectSimulation dAes;
|
||
dAes.aesDdsId = aes.id();
|
||
//dAes.aesTetraIdUnused = aesTetraId;
|
||
dAes.sequelNumber = AreaEffectSimulation::Polygons::getSequelNumber(aes.id(),
|
||
aes.impactCount(), //what number here?
|
||
aes.originalEffectDuration(),
|
||
aes.effectDuration()); // most likely _remaining_ effectDuration
|
||
dAes.nextImpact = aes.remainExplosionNumber();
|
||
dAes.resumeTime = aes.effectStart().is_set() ? aes.effectStart().get() : 0;
|
||
dAes.remainDuration = aes.effectDuration();
|
||
|
||
txData(dAes);
|
||
}
|
||
|
||
std::shared_ptr<PdmHandler> RecvFromDdsAes::pdm()
|
||
{
|
||
return m_mainHandler->pdmHandler();
|
||
}
|
||
|
||
void RecvFromDdsAes::onCheckOutdatedTelegrams()
|
||
{
|
||
QSet<int> deactivatedAesIds;
|
||
deactivatedAesIds.reserve(m_sentAesTelegrams.size()); // to much but ok
|
||
for (int i = m_sentAesTelegrams.size() - 1; i >= 0; --i)
|
||
{
|
||
TetraIcd::TrainingAreaTelegram *tele = m_sentAesTelegrams.at(i);
|
||
const int aesId = m_sentAesTelegramsAesIds.at(i);
|
||
if (tele->id() == TrainingAreaBase::TetraIcd::DEAKTIVIEREN_FUNKTIONSRAUM ||
|
||
tele->id() == TrainingAreaBase::TetraIcd::DEAKTIVIERUNG_WIRKFELD)
|
||
{
|
||
deactivatedAesIds += aesId;
|
||
continue;
|
||
}
|
||
if (!deactivatedAesIds.contains(aesId))
|
||
continue;
|
||
INFO_LOG(m_logger) << "Removing tele " << tele->type() << " from aes tele stack";
|
||
delete tele;
|
||
m_sentAesTelegrams.removeAt(i);
|
||
m_sentAesTelegramsAesIds.removeAt(i);
|
||
}
|
||
}
|
||
|
||
void RecvFromDdsAes::resetAllDusMines()
|
||
{
|
||
if (m_sendDusMineReactivtionTimer->isActive())
|
||
m_sendDusMineReactivtionTimer->stop();
|
||
disconnect(m_sendDusMineReactivtionTimer, &QTimer::timeout,
|
||
this, &RecvFromDdsAes::onSendDusMineReactivationTimeout);
|
||
QTimer::singleShot(300, this, [this]()
|
||
{
|
||
for (AreaEffectSimulation::DusMine &mine : m_allReactivatableDusMines)
|
||
mine.triggered(false);
|
||
m_minesToSend.clear();
|
||
connect(m_sendDusMineReactivtionTimer, &QTimer::timeout,
|
||
this, &RecvFromDdsAes::onSendDusMineReactivationTimeout);
|
||
});
|
||
}
|
||
|
||
template<typename T>
|
||
void RecvFromDdsAes::fillCentralChangesTable(const T &data)
|
||
{
|
||
dataChanged(data);//TODO: Only change name????
|
||
}
|
||
|
||
} // namespace AreaeffectSimulation
|
||
|