/** * 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 #include #include #include #include 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 &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 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: { // 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::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) { // 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::value && !std::is_same::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(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(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 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 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 &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 &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 &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(aes.deconMode().underlying()); fAes.deconTimeout = aes.deconTime(); fAes.maintenanceMode = static_cast(aes.maintMode().underlying()); fAes.maintenanceTimeout = aes.maintTime(); fAes.medicalMode = static_cast(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(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(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(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 RecvFromDdsAes::pdm() { return m_mainHandler->pdmHandler(); } void RecvFromDdsAes::onCheckOutdatedTelegrams() { QSet 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 void RecvFromDdsAes::fillCentralChangesTable(const T &data) { dataChanged(data);//TODO: Only change name???? } } // namespace AreaeffectSimulation