Files
qt5-legatus-legacy/Lts/Proxies/TrainingAreaBase/recv_from_dds_aes.cpp
T
2026-08-13 18:33:10 +02:00

1387 lines
50 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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