#include "recv_from_dds_aes.h" #include #include "main_handler.h" #include "settings.h" #include #include #include #include #include #include namespace TrainingAreaBase { AesStateHandler::AesStateHandler(MainHandler *mainHandler) : m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger()) , m_dbInterface(mainHandler->dbHandler()->db()) , m_exerciseId(mainHandler->exerciseId()) {} 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(MainHandler *mainHandler, const std::shared_ptr &txCallback, QObject *parent) : QObject(parent) , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger()) , 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_checkOutdatedTelegramsTimer->setSingleShot(true); m_checkOutdatedTelegramsTimer->setInterval(5000); connect(m_checkOutdatedTelegramsTimer, &QTimer::timeout, this, &RecvFromDdsAes::onCheckOutdatedTelegrams); 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() { 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; 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); } sendCreateEffectSimulation(data, aesTetraId, request.equipmentNumber); m_aesStateHandler->update(request.aesId, aesTetraId, true); return true; } template void RecvFromDdsAes::handleEffectSimulation(const T &data, DataModification modType, TetraIcd::AreaEffectSimulationType aesType) { DEBUG_LOG(m_logger) << "RecvFromDdsAes::handleEffectSimulation(id=" << data.id << ", modType: " << static_cast(modType) << ", type=" << static_cast(aesType) << ", name: " << data.name << ", state: " << data.effectStateId << ", old state: " << m_currentEffectState.value(data.id) << ")"; if (std::is_same::value || std::is_same::value || std::is_same::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: { 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) { handleSendToTetra(data, aesType); } sendPauseEffectSimulation(data.id); } else if (data.effectStateId == AreaEffectSimulation::AesEffectState::EsActivated && m_currentEffectState.value(data.id) == AreaEffectSimulation::AesEffectState::EsPaused) { sendResumeEffectSimulation(data); } else if (!data.effectStart.is_set() || data.effectStart.get() == 0 || data.effectStateId == AreaEffectSimulation::AesEffectState::EsRemoved || data.effectStateId == AreaEffectSimulation::AesEffectState::EsExpired) { if (modType == DataModification::eAdded) { bool placeHolder; dataChanged(data, placeHolder); } sendRemoveEffectSimulation(aesType, data.id); resetStartTime(data); } else { if (!(std::is_same::value) || (data.effectStateId == AreaEffectSimulation::AesEffectState::EsActivated)) handleSendToTetra(data, aesType); } m_currentEffectState[data.id] = data.effectStateId; break; } } } template void RecvFromDdsAes::handleSendToTetra(const T &data, TetraIcd::AreaEffectSimulationType aesType) { bool bAlreadyExists = false; const int aesTetraId = m_aesStateHandler->createAesTetraId(data.id, bAlreadyExists); bool bOnlyNameChanged = false; bool bSomethingChanged = false;; if (!bAlreadyExists) { dataChanged(data, bOnlyNameChanged); bSomethingChanged = true; } else bSomethingChanged = dataChanged(data, bOnlyNameChanged); if (bOnlyNameChanged && !std::is_same::value && !std::is_same::value) { 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) { 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); auto it = m_temporarySingleMineState.find(data.id); if (it != m_temporarySingleMineState.end()) { onTriggeredImpactsChanged(it->second, DataModification::eModified); m_temporarySingleMineState.erase(data.id); } } 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; if (data.aesItemId == 0) return; bool completeUntriggered = true; for (size_t i = 0; i < data.bitfield.size() && completeUntriggered; ++i) { if ((i + 1) * 8 <= static_cast(data.impactCount)) { if (data.bitfield.at(i) != 0xFF) completeUntriggered = false; } else { uint8_t endMask = 0; for (uint32_t j = 0; j < (static_cast(data.impactCount) - i * 8); ++j) endMask |= 0x80 >> j; if (data.bitfield.at(i) != endMask) completeUntriggered = false; } } if (completeUntriggered) return; 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) { 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; } TetraIcd::MineAreaSingleMineState out; out.aesDdsId = data.aesItemId; 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; for (const auto &mine : vec) { if (!m_mainHandler->pdmHandler()->isReactivatableChargeType({mine.chargeTypeId, 0,0})) continue; if (mine != m_allReactivatableDusMines.value(mine.eqId)) { if (!mine.triggered && m_allReactivatableDusMines.contains(mine.eqId) && m_allReactivatableDusMines.value(mine.eqId).triggered ) m_minesToSend << mine.eqId; m_allReactivatableDusMines.insert(mine.eqId, mine); } } if (!m_minesToSend.isEmpty()) m_sendDusMineReactivtionTimer->start(); 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 : std::as_const(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); } void RecvFromDdsAes::onOffRouteMineChanged(const AreaEffectSimulation::OffRouteMine &data, DataModification modType) { Q_UNUSED(modType); fillCentralChangesTable(data); } void RecvFromDdsAes::onExplosionClusterChanged(const AreaEffectSimulation::ExplosionCluster &data, DataModification modType) { handleEffectSimulation(data, modType, TetraIcd::AreaEffectSimulationType::ExplosionArea); } template void setCommonParams(T &out, const S &in, const int aesTetraId) { out.aesDdsId = in.id; Q_UNUSED(aesTetraId); out.name = in.name; out.partyId = in.partyId.is_set() ? in.partyId.get() : 0; } template 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; if (data.aesTypeId != AreaEffectSimulation::AesType::AtExplosionClusterSingleExplosions) return; const int aesItemId = data.aesItemId; if (aesItemId <= 0) return; const AreaEffectSimulation::ExplosionCluster &aes = m_dtcAes->explosionCluster(aesItemId); if (aes.id != aesItemId) return; const bool bIsLaserGuidable = m_mainHandler->pdmHandler()->isLaserGuidable(aes.chargeStruct); if (!bIsLaserGuidable) return; const std::vector &singleEffectsRef = data.singleEffects; if (!singleEffectsRef.empty()) { 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); } const AreaEffectSimulation::SingleEffect &firstSingleEffect = singleEffectsRef.front(); const Core::GeoPosition &fsePosition = firstSingleEffect.position; cAes.relCorner = m_posHandler->geographicToRelative(fsePosition); 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 &mhPointsRef = cAes.mhPoints; mhPointsRef.clear(); mhPointsRef.reserve(aes.mhPoints.size()); for (const Core::GeoPosition2D &pos : std::as_const(aes.mhPoints)) mhPointsRef.emplace_back(m_posHandler->geographicToRelative(pos, 0)); cAes.chargeTypeId = 254; 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 = 0; cAes.relEffectPosition = m_posHandler->geographicToRelative(aes.impactPosition, 0); 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); 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)) { 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); 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) { const bool bIsLaserGuidable = m_mainHandler->pdmHandler()->isLaserGuidable(aes.chargeStruct); DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendCreateEffectSimulation " << ", aes " << aes << ", bIsLaserGuidable " << bIsLaserGuidable; if (bIsLaserGuidable) { 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); 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 &mhPointsRef = cAes.mhPoints; mhPointsRef.clear(); mhPointsRef.reserve(aes.mhPoints.size()); for (const Core::GeoPosition2D &pos : std::as_const(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); 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)); 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.layingDepth = aes.width; cAes.layingDensity = aes.density; 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; 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(aes.deconMode); fAes.deconTimeout = aes.deconTime; fAes.maintenanceMode = static_cast(aes.maintMode); fAes.maintenanceTimeout = aes.maintTime; fAes.medicalMode = static_cast(aes.medicMode); 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.name = aes.name; fAes.taType = static_cast(aes.type); 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(); dAes.effectDuration = aes.effectDuration; dAes.mineId = aes.mineId; 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) { 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(aesType) << ", " << aesTetraId << ", " << name << ")"; TetraIcd::ModifyAreaEffectSimulationName mAes; mAes.aesType = aesType; mAes.aesDdsId = aesId; mAes.name = name; txData(mAes); } void RecvFromDdsAes::sendRemoveEffectSimulation(TetraIcd::AreaEffectSimulationType aesType, int aesDdsId) { if (aesDdsId == 0) return; if (aesType == TetraIcd::AreaEffectSimulationType::GasPipeDetonation) 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, false)) { WARNING_LOG(m_logger) << "Could not update Aes state handler (" << aesDdsId << ", " << aesTetraId << ")"; } DEBUG_LOG(m_logger) << "RecvFromDdsAes::sendRemoveEffectSimulation(id=" << aesDdsId << ", type=" << static_cast(aesType) << ", tetraId=" << aesTetraId << ")"; if (aesType == TetraIcd::AreaEffectSimulationType::TechnicalArea) { TetraIcd::TechnicalArea fAes; fAes.taType = TetraIcd::TechnicalArea::RemoveArea; fAes.aesDdsId = aesDdsId; txData(fAes); } else { TetraIcd::DeactivateEffectSimulation dAes; dAes.aesType = aesType; dAes.aesDdsId = aesDdsId; 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; 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.sequelNumber = AreaEffectSimulation::Polygons::getSequelNumber(aes.id, aes.cannonCount * aes.cannonRounds, aes.originalEffectDuration, aes.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.sequelNumber = AreaEffectSimulation::Polygons::getSequelNumber(aes.id, aes.impactCount, aes.originalEffectDuration, aes.effectDuration); dAes.nextImpact = aes.remainExplosionNumber; dAes.resumeTime = aes.effectStart.is_set() ? aes.effectStart.get() : 0; dAes.remainDuration = aes.effectDuration; txData(dAes); } std::shared_ptr RecvFromDdsAes::pdm() { return m_mainHandler->pdmHandler(); } void RecvFromDdsAes::onCheckOutdatedTelegrams() { QSet deactivatedAesIds; deactivatedAesIds.reserve(m_sentAesTelegrams.size()); 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 void RecvFromDdsAes::fillCentralChangesTable(const T &data) { dataChanged(data); } }