// ************************************************************************** // // (C) Copyright 2009 Swiss SIMTEC AG // // ************************************************************************** // // $HeadURL$ // $LastChangedBy$ // $LastChangedDate$ // $LastChangedRevision$ // // ************************************************************************** #include "PrtCnvBroadcastMgr.hpp" #include "PrtCnvDbThread.hpp" #include #include #include #include namespace PrtCnv { PrtCnvBroadcastMgr::PrtCnvBroadcastMgr() : m_ClaimWindow(0) , m_HighLoadFactor(0) , m_BroadcastLimit(0) , m_Config(nullptr) , m_ActClaimWindow(0) , m_State(e_ST_UNDEFINED) , m_TickCounter(0) , m_DbThread(nullptr) , m_BroadcastIndex(0) , m_IsFullLoad(false) , m_FinishedBroadcastIndex(0) , m_FirstBroadcastIndexOfBc1(0) , m_logger(getLogger("PrtCnvBroadcastMgr")) , m_loggerBcRequest(getLogger("PrtCnvBroadcastMgr.BcRequest")) { } // --------------------------------------------------------------- // Creates all subcomponents // --------------------------------------------------------------- int32_t PrtCnvBroadcastMgr::create(NGL::Settings *settings) { m_Config = settings; return 0; } // --------------------------------------------------------------- // Initializes the variables. // --------------------------------------------------------------- int32_t PrtCnvBroadcastMgr::init() { const QString broadcastGroup(QStringLiteral("Broadcast")); N_CoPrtCnv::CoPrtCnvRadioTelegram::setBroadcastDelayTimes( m_Config->getInteger(broadcastGroup, QStringLiteral("BroadcastDelay2"), 60), m_Config->getInteger(broadcastGroup, QStringLiteral("BroadcastDelay3"), 60)); N_CoPrtCnv::CoPrtCnvRadioTelegram::setMaxBroadcastSendCnt(m_Config->getInteger(broadcastGroup, QStringLiteral("MaxBroadcastSendCount"), 1)); // the time [ms] to wait between sending broadcasts const int sendGap = m_Config->getInteger(broadcastGroup, QStringLiteral("SendGap"), 500); m_ClaimWindow = (m_Config->getInteger(broadcastGroup, QStringLiteral("ClaimWindow"), 30) * 1000) / sendGap; m_HighLoadFactor = m_Config->getInteger(broadcastGroup, QStringLiteral("HighLoadFactor"), 3); // fixme: in spec steht 90sec normal und 3min für high m_BroadcastLimit = m_Config->getInteger(broadcastGroup, QStringLiteral("BroadcastLimit"), 10); cleanAndInit(true); INFO_LOG(m_loggerBcRequest) << "m_ActClaimWindow=" << m_ActClaimWindow << " m_State=" << m_State; return 0; } // --------------------------------------------------------------- // sets the DbThread used for get the stored broadcast- // telegrams // --------------------------------------------------------------- void PrtCnvBroadcastMgr::setDbThread(PrtCnvDbThread *a_dbThread) { m_DbThread = a_dbThread; } // --------------------------------------------------------------- // Initializes the broadcast-manager. If a_deleteTelegrams is // true, all stored telegrams and maps will be deleted. // --------------------------------------------------------------- void PrtCnvBroadcastMgr::cleanAndInit(const bool a_deleteTelegrams) { if (a_deleteTelegrams) { m_deactivatedAesArea2bcIndex.clear(); m_deactivatedBcIndex2AesArea.clear(); m_deactivatedDusMine2bcIndex.clear(); m_deactivatedBcIndex2DusMine.clear(); m_deactivatedFuncArea2bcIndex.clear(); m_deactivatedBcIndex2FuncArea.clear(); m_BroadcastMap.clear(); m_LastTelegramMap.clear(); // the telegrams itself were already deleted while looping through broadcastmap m_FunktionsraumTelegramMap.clear(); // the telegrams itself were already deleted while looping through broadcastmap m_WirkfeldTelegramMapByKennung.clear(); // the telegrams itself were already deleted while looping through broadcastmap m_WirkfeldTelegramMapByTime.clear(); // the telegrams itself were already deleted while looping through broadcastmap m_BroadcastIndex = 0; } m_claimerArray.clear(); m_ActClaimWindow = m_ClaimWindow; m_IsFullLoad = false; m_TickCounter = 0; m_State = e_ST_COLLECT; m_FinishedBroadcastIndex = 0; m_FirstBroadcastIndexOfBc1 = 0; } // --------------------------------------------------------------- // returns true when the telegram should be sent to central // --------------------------------------------------------------- bool PrtCnvBroadcastMgr::handleRadioTelegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram) { switch (a_radioTelegram.getTelID()) { case GEF_NACHFORDERUNG_BROADCAST_TELEGRAMME: handleNachforderungBroadcastTelegram(a_radioTelegram.getIdentity(), a_radioTelegram.getPayloadDataUInt32(8, 2) + 1, a_radioTelegram.getPayloadDataUInt8(7)); return false; default: // nothing break; } return true; // => send the telegram to central } // --------------------------------------------------------------- // Adds the TNE to the nachforderungs-list // --------------------------------------------------------------- void PrtCnvBroadcastMgr::handleNachforderungBroadcastTelegram(int identity, int broadcastIndex, int resetCounter) { INFO_LOG(m_loggerBcRequest) << "Got NachforderungBroadcast from TNE with id=" << identity << " resetCounter=" << resetCounter << " broadcastIndex=" << broadcastIndex - 1 << " m_TickCounter=" << m_TickCounter << " m_State=" << m_State; if (resetCounter == m_DbThread->getResetCounter()) { m_DbThread->incClaimCount(identity); // already in there? for (auto &ebi : m_claimerArray) { if (ebi.equipNo == identity) { ebi.bcIndex = broadcastIndex; return; } } m_claimerArray.emplace_back(identity, broadcastIndex); return; } WARNING_LOG(m_logger) << "NachforderungBroadcast from TNE with id=" << identity << ": resetCounter different we have: " << int(m_DbThread->getResetCounter()) << "; TNE has: " << resetCounter << " => discarded"; } // --------------------------------------------------------------- // returns the next normal broadcast to send. // Use the same a_currentTime in one tick-loop. // --------------------------------------------------------------- std::shared_ptr PrtCnvBroadcastMgr::getNextBroadcastTelegramToSend(const uint16_t a_beginSearchWithBroadcastIndex, const time_t a_currentTime) { // iterate the braodcast-list and get the next broadcast to send // but check whether it is not a sammeltelegram or has an attached // broadcast-only-telegram DEBUG_LOG(m_logger) << "getNextBroadcastTelegramToSend, startIdx: " << a_beginSearchWithBroadcastIndex; auto iter = m_BroadcastMap.upper_bound(0 == a_beginSearchWithBroadcastIndex ? m_FinishedBroadcastIndex : a_beginSearchWithBroadcastIndex); while (iter != m_BroadcastMap.end()) { auto broadcastTelegram = iter->second; gef_telegramm_id_et telId = broadcastTelegram->getTelID(); // the following telegrams will never be sent as normal broadcast if (GEF_WF_SAMMELDEAKTIVIERUNG_FUNKTIONSRAUM == telId || GEF_WF_SAMMELDEAKTIVIERUNG == telId || GEF_WF_SAMMELDEAKTIVIERUNG_DUS_MINEN == telId) { // respect an attached broadcast-only-telegram, otherwise step to next telegram if (!broadcastTelegram->getBroadcastonlyTelegram()) { ++iter; continue; } } // use the attached broadcast-only-telegram if there is one bool isBcOnly = false; if (broadcastTelegram->getBroadcastonlyTelegram()) { broadcastTelegram = broadcastTelegram->getBroadcastonlyTelegram(); isBcOnly = true; } // check whether it is time to send this broadcast-telegram the 2nd or 3rd time bool wasLast = false; if (broadcastTelegram->mustBeSentAsBroadcast(a_currentTime, wasLast)) { // remove a 3rd-time-sent broadcast-only from all maps if (wasLast) { if (isBcOnly || broadcastTelegram->isBroadcastOnly()) { removeFromAllMaps(broadcastTelegram); // this will destroy our iterator, but we don't need it anymore... } else { // time-optimizing: 3rd-time sent broadcast-telegrams must no longer be examined m_FinishedBroadcastIndex = broadcastTelegram->getBroadcastIndex(); } } // the identity could be dirty, because it could be sent as directed nachforderung in past broadcastTelegram->setIdentity(0); return broadcastTelegram; } else { // this broadcast-telegram must not yet be sent // ------------------------------------------------------------------------------------ // optimize by time: // Beside the fact that the m_BroadcastMap is ordered by BC-index there is also another // implicit order: first come all 3rd time sent telegramms, then all 2nd time sent // telegrams and finally all 1st time sent telegrams. Furthermore, the 3rd time sent // telegrams are ordered ascending by nextsendtime. The same order exists also for 2nd // and 1st time sent telegrams. This allows us some optimizings. // Example: // BC-index broadcast-counter next-send-time // 1 3 0 // 2 3 0 // 3 2 1000 // 4 2 1000 // 5 2 1001 // 6 2 1005 // 7 1 1000 // 8 1 1001 // 9 1 1001 // ------------------------------------------------------------------------------------- uint16_t bcCounter = broadcastTelegram->getBroadcastCounter(); if (2 == bcCounter) { // step to first telegram with bc-counter == 1, because all other telegrams will have same // or higher nextsendtime if (m_FirstBroadcastIndexOfBc1 > iter->first) { iter = m_BroadcastMap.lower_bound(m_FirstBroadcastIndexOfBc1); continue; } } else if (1 == bcCounter) { m_FirstBroadcastIndexOfBc1 = broadcastTelegram->getBroadcastIndex(); return nullptr; // break, because all further telegrams will have same or higher nextsendtime } } ++iter; } return nullptr; } // --------------------------------------------------------------- // returns the next broadcast to send for nachforderungen // --------------------------------------------------------------- std::shared_ptr PrtCnvBroadcastMgr::getNextNachforderungTelegramToSend() { removeExpiredTelegrams(); switch (m_State) { case e_ST_COLLECT: // is the claim-window elapsed (usually 30 sec)? if (m_TickCounter >= m_ActClaimWindow && !m_IsFullLoad) { // yes, the window has elapsed prepareForSendNachforderungen(); } else { // the claim-window has not elapsed getAllFinishedBroadcastsFromDb(); } break; case e_ST_SEND_NACHFORDERUNG: return evaluateNextNachforderungToSend(); default: break; } return {}; } // --------------------------------------------------------------- // Takes expired wirkfeld-telegrams out of maps. // --------------------------------------------------------------- void PrtCnvBroadcastMgr::removeExpiredTelegrams() { const time_t now = time(nullptr); auto iter = m_WirkfeldTelegramMapByTime.begin(); while (iter != m_WirkfeldTelegramMapByTime.end()) { // 5 Sekunden warten wir noch da sonst ggf. Wirkfelder mit Wirkzeiten < 5 Sekunden // schon rausfallen bevor sie ins Gefechtsfeld gesendet wurden. if (iter->first + 5 > now) return; auto radioTelegram = iter->second; INFO_LOG(m_logger) << "Remove expired telegram with telId=" << radioTelegram->getTelID() << " and BC=" << radioTelegram->getBroadcastIndex() << " from BC-list"; uint16_t wirkfeldKennung = radioTelegram->getPayloadDataUInt32(5, 2); removeWirkfeldTelegrams(wirkfeldKennung); iter = m_WirkfeldTelegramMapByTime.begin(); } } // --------------------------------------------------------------- // Prepares values for start sending nachforderungen // --------------------------------------------------------------- void PrtCnvBroadcastMgr::prepareForSendNachforderungen() { // are there any broadcast nachforderungen? m_TickCounter = 0; if (!m_claimerArray.empty()) { dump(); // yes, there are any nachforderungen // do we have to send them directed or broadcasted? m_State = e_ST_SEND_NACHFORDERUNG; // fill the receiver-map std::swap(m_claimerArray, m_claimerArray); std::sort(m_claimerArray.begin(), m_claimerArray.end(), [] (const EquipNoBcIndex &a, const EquipNoBcIndex &b) { if (a.bcIndex != b.bcIndex) return a.bcIndex < b.bcIndex; return a.equipNo < b.equipNo; }); // evaluate if we begin with sending directed or broadcasted INFO_LOG(m_loggerBcRequest) << "BroadcastLimit: " << m_BroadcastLimit; if (m_claimerArray.size() > m_BroadcastLimit) { m_claimerArray.clear(); m_claimerArray.emplace_back(0, m_claimerArray.begin()->bcIndex); } // now build up the telegrams for (const EquipNoBcIndex &enbi : m_claimerArray) { int curBcIndex = enbi.bcIndex; int nextBcIndex = -1; while (true) { auto tel = getNachforderungsTelegram(curBcIndex, enbi.bcIndex, nextBcIndex); curBcIndex = nextBcIndex; if (tel) { auto copy(std::make_shared(*tel)); copy->setIdentity(enbi.equipNo); // issi is set during sending m_broadcastTelegamsToSend.push_back(copy); } else break; }; std::sort(m_broadcastTelegamsToSend.begin(), m_broadcastTelegamsToSend.end(), [] (const std::shared_ptr &a, const std::shared_ptr &b) { const int bcIndexA = a->getTelID() == GEF_NOP ? a->getPayloadDataUInt32(3, 2) : a->getBroadcastIndex(); const int bcIndexB = b->getTelID() == GEF_NOP ? b->getPayloadDataUInt32(3, 2) : b->getBroadcastIndex(); if (bcIndexA != bcIndexB) return bcIndexA > bcIndexB; // reverse, we use back()/pop_back() return a->getIdentity() < b->getIdentity(); }); if (m_loggerBcRequest.isDebugEnabled()) { DEBUG_LOG(m_loggerBcRequest) << "Created broadcast list:"; for (const auto &tele : m_broadcastTelegamsToSend) DEBUG_LOG(m_loggerBcRequest) << "Identity: " << tele->getIdentity() << ", bc: " << tele->getBroadcastIndex() << ", type: " << tele->getTelID(); } } m_claimerArray.clear(); } } // --------------------------------------------------------------- // returns the next nachforderung to send // --------------------------------------------------------------- std::shared_ptr PrtCnvBroadcastMgr::evaluateNextNachforderungToSend() { if (m_broadcastTelegamsToSend.empty()) { m_State = e_ST_COLLECT; return nullptr; } auto tel = m_broadcastTelegamsToSend.back(); m_broadcastTelegamsToSend.pop_back(); return tel; } std::shared_ptr PrtCnvBroadcastMgr::createAccumulationTelegram(int curBcIndex, int startBcIndex, gef_telegramm_id_et telId, const QMap &bcIndex2Type, int maxIdsPerTelegram, int sizeOfSumField) const { if (bcIndex2Type.isEmpty()) return nullptr; auto itLastBcTele = m_BroadcastMap.end(); --itLastBcTele; const int endBcIndex = itLastBcTele->second->getBroadcastIndex(); DEBUG_LOG(m_loggerBcRequest) << "createAccumulationTelegram for telId " << telId << ", begin: " << startBcIndex << ", cur: " << curBcIndex << ", end: " << endBcIndex << ", #elements in container: " << bcIndex2Type.size(); DEBUG_LOG(m_loggerBcRequest) << "Given broadcast list for " << telegramId2String(telId); for (auto it = bcIndex2Type.begin(); it != bcIndex2Type.end(); ++it) DEBUG_LOG(m_loggerBcRequest) << " bcIndex: " << it.key() << ", id: " << it.value(); // the asserts are only sanity checks - should never happen auto itStart = bcIndex2Type.lowerBound(startBcIndex); if (itStart == bcIndex2Type.end()) { Q_ASSERT(false); return nullptr; } auto itEnd = bcIndex2Type.lowerBound(endBcIndex); if (itEnd == bcIndex2Type.end()) --itEnd; const int completeTelegrams = std::distance(itStart, itEnd) + 1; if (completeTelegrams <= 0) { Q_ASSERT(false); return nullptr; } const auto itCur = bcIndex2Type.find(curBcIndex); if (itCur == bcIndex2Type.end()) { Q_ASSERT(false); return nullptr; } const int telegramsStillSendable = std::distance(itCur, itEnd) + 1; if (telegramsStillSendable <= 0) { Q_ASSERT(false); return nullptr; } const int telegramsToSend = (completeTelegrams / maxIdsPerTelegram) + 1; if (telegramsStillSendable > telegramsToSend) { INFO_LOG(m_loggerBcRequest) << telId << ": sending nop since accuTelegram is sent later (" << telegramsStillSendable << ", need only " << telegramsToSend << ")"; return nullptr; } const int curIdStart = (telegramsToSend - telegramsStillSendable) * maxIdsPerTelegram; const int idsToSendNow = (curIdStart + maxIdsPerTelegram <= completeTelegrams ? maxIdsPerTelegram : completeTelegrams % maxIdsPerTelegram); INFO_LOG(m_loggerBcRequest) << telId << ": creating accuTelegram from index " << curIdStart << ", count: " << idsToSendNow; itStart += curIdStart; // hdr (3 bytes) + bc idx + sum field auto tele(std::make_shared(telId, 3 + 2 + sizeOfSumField + sizeof(uint16_t) * idsToSendNow, 0)); tele->setPayload2Byte(curBcIndex); if (sizeOfSumField == 1) tele->setPayload1Byte(idsToSendNow); // #Ids else if (sizeOfSumField == 2) tele->setPayload2Byte(idsToSendNow); // #Ids else { Q_ASSERT(false); return nullptr; } for (int i = 0; i < idsToSendNow; ++i) { tele->setPayload2Byte(itStart.value()); ++itStart; } return tele; } std::shared_ptr PrtCnvBroadcastMgr::createAccuTeleDeactivatedAesAreas(int curBcIndex, int startBcIndex) const { return createAccumulationTelegram(curBcIndex, startBcIndex, GEF_WF_SAMMELDEAKTIVIERUNG, m_deactivatedBcIndex2AesArea, 100, 1); } std::shared_ptr PrtCnvBroadcastMgr::createAccuTeleDeactivatedFuncAreas(int curBcIndex, int startBcIndex) const { return createAccumulationTelegram(curBcIndex, startBcIndex, GEF_WF_SAMMELDEAKTIVIERUNG_FUNKTIONSRAUM, m_deactivatedBcIndex2FuncArea, 100, 1); } std::shared_ptr PrtCnvBroadcastMgr::createAccuTeleDeactivatedDusMines(int curBcIndex, int startBcIndex) const { return createAccumulationTelegram(curBcIndex, startBcIndex, GEF_WF_SAMMELDEAKTIVIERUNG_DUS_MINEN, m_deactivatedBcIndex2DusMine, 500, 2); } std::shared_ptr PrtCnvBroadcastMgr::createAccuTeleActivatedDusMines(int curBcIndex) { if (m_activatedDusMines.isEmpty()) return {}; // we currently only can have exactly *one* telegram of this type (with max. 500 entries) const int idsToSendNow = m_activatedDusMines.size(); if (idsToSendNow > 500) { WARNING_LOG(m_logger) << "More than 500 activated DUS mines in system - this is not allowed!"; Q_ASSERT(false); return {}; } auto tele(std::make_shared(GEF_REAKTIVIERUNG_DUS_MINE, 5 + 2 + sizeof(uint16_t) * idsToSendNow, 0)); tele->setPayload2Byte(curBcIndex); tele->setPayload2Byte(idsToSendNow); for (int dusMineId : qAsConst(m_activatedDusMines)) tele->setPayload2Byte(dusMineId); return tele; } // --------------------------------------------------------------- // returns the Nachforderungs-telegram with this broadcast- // index from the broadcast-map with sammeltelegrams. If the // telegram with a_broadcastIndex does not exist, a NOP- // telegram will be returned, which must be consumed // immediately and must never be deleted. // The parameter a_broadcastIndex will be updated to the value // of found telegram. // ATTENTION: the returned a_broadcastIndex could be different // from the value returned by getBroadcastIndex(), for example // in NOP-telegrams getBroadcastIndex() returns the Anfang, // but a_broadcastIndex will be the Ende. // --------------------------------------------------------------- std::shared_ptrPrtCnvBroadcastMgr::getNachforderungsTelegram(int curBcIndex, int startBcIndex, int &nextBcIndex) { DEBUG_LOG(m_loggerBcRequest) << "getNachforderungsTelegram - curBcIndex: " << curBcIndex << ", startBcIndex: " << startBcIndex; if (curBcIndex == 0) curBcIndex++; DEBUG_LOG(m_loggerBcRequest) << "getNachforderungsTelegram - curBcIndex after Ssnity: " << curBcIndex << ", startBcIndex: " << startBcIndex; nextBcIndex = curBcIndex; bool doSendNop = false; while (true) { auto iter = m_BroadcastMap.lower_bound(nextBcIndex); if (iter == m_BroadcastMap.end()) break; auto radioTelegram = iter->second; DEBUG_LOG(m_loggerBcRequest) << "getNachforderungsTelegram - cur tel id is " << radioTelegram->getTelID() << ", bcIndex: " << radioTelegram->getBroadcastIndex() << ", send nop: " << doSendNop; if (nextBcIndex < iter->first) { // do we have gaps or is it a broadcast-only telegram => send NOP-telegram // but iterate for summarize all broadcast-only and gaps doSendNop = true; nextBcIndex = iter->first; } else if (radioTelegram->isBroadcastOnly()) { doSendNop = true; nextBcIndex = iter->first + 1; } else { std::shared_ptr retTele; switch (radioTelegram->getTelID()) { case GEF_DEAKTIVIERUNG_WIRKFELD: retTele = createAccuTeleDeactivatedAesAreas(radioTelegram->getBroadcastIndex(), startBcIndex); break; case GEF_DEAKTIVIEREN_FUNKTIONSRAUM: retTele = createAccuTeleDeactivatedFuncAreas(radioTelegram->getBroadcastIndex(), startBcIndex); break; case GEF_DEAKTIVIERUNG_EINZELMINE: retTele = createAccuTeleDeactivatedDusMines(radioTelegram->getBroadcastIndex(), startBcIndex); break; case GEF_REAKTIVIERUNG_DUS_MINE: retTele = createAccuTeleActivatedDusMines(radioTelegram->getBroadcastIndex()); break; default: retTele = radioTelegram; break; } // when we got a tele but have to send a nop first, wait for the next iteration and return nop now if (retTele && doSendNop) break; ++nextBcIndex; if (retTele) return retTele; doSendNop = true; } } // due to some asyncs during startup it *may* happen that the last entry(ies) of // the broadcast map are empty - make sure that we send a nop for them if (!doSendNop && nextBcIndex <= m_BroadcastIndex) { nextBcIndex = m_BroadcastIndex + 1; doSendNop = true; } if (doSendNop) { // return a NOP-telegram since next tele is a 'normal' telegram to send INFO_LOG(m_loggerBcRequest) << "Returning NOP: " << curBcIndex << " to " << nextBcIndex - 1; return getNopTelegram(curBcIndex, nextBcIndex - 1); } return nullptr; } // --------------------------------------------------------------- // Gets the braodcast-telegrams from Db, which were sent 1st // time and adds them to the maps. Expired telegrams will be // removed. // --------------------------------------------------------------- void PrtCnvBroadcastMgr::getAllFinishedBroadcastsFromDb(const bool a_doRestore) { // => get all broadcasts which are sent first time; replace some telegrams with // sammeltelegrams or NOP-telegrams // Attention: this must only be done, when we are not sending broadcast-nachforderungen, // because otherwise we could change sammeltelegrams which are already sent. uint16_t broadcastIndex = 0; while (true) { auto radioTelegram = m_DbThread->getNextBroadcastTelegram(); if (!radioTelegram) break; broadcastIndex = radioTelegram->getBroadcastIndex(); INFO_LOG(m_logger) << "Move Broadcast-telegram from DbThread>BroadcastMgr with telId=" << radioTelegram->getTelID() << " BC=" << broadcastIndex; dumpTelegram(m_logger, broadcastIndex, radioTelegram.get()); // abgelaufene Telegramme hier *nicht* loeschen da sonst z.B. WF_FL_FEUER_STOP/FORTSETZEN // spaeter nicht beachtet werden und WF_FL_FEUER nicht aus der Broadcastliste // entfernt wird. außerdem hätten wir so eine andere Ausgangsbasis als wenn der PrtCnv // nicht neu gestartet wurde was u.U. auch woanders noch zu Problemen führt. //if (radioTelegram->hasExpired()) //{ // INFO_LOG(m_logger) << "Telegram with BC " << broadcastIndex // << " is expired - throwing away"; // delete radioTelegram; //} //else { addToBroadcastMap(radioTelegram); } // remake overrun when restoring if (a_doRestore && MAX_BROADCAST_INDEX == broadcastIndex) handleOverrunOfBroadcastCounter(a_doRestore); } if (m_logger.isDebugEnabled()) { static int iBroadcastDumpCounter = 0; if ((iBroadcastDumpCounter++ % 30) == 0) { DEBUG_LOG(m_logger) << "Dumping broadcast map:"; for (const auto &it : m_BroadcastMap) dumpTelegram(m_logger, it.first, it.second.get()); } } // set the actual BC-index if (a_doRestore) { m_BroadcastIndex = broadcastIndex; m_FinishedBroadcastIndex = m_BroadcastIndex; } } // --------------------------------------------------------------- // adds the radio-telegram to the broadcast-map, or generates // the sammeltelegramme // --------------------------------------------------------------- void PrtCnvBroadcastMgr::addToBroadcastMap(const std::shared_ptr &a_radioTelegram) { INFO_LOG(m_logger) << "Move from radioList to bcList: BC=" << a_radioTelegram->getBroadcastIndex() << " telID=" << a_radioTelegram->getTelID(); switch (a_radioTelegram->getTelID()) { // following telegrams should never be broadcasts case GEF_INIT: case GEF_UMGEBUNGS_MELDUNG2: ERROR_LOG(m_logger) << "Internal error: telegram with telId=" << a_radioTelegram->getTelID() << " should never be a normal broadcast"; break; // the following telegrams should only be sent as normal broadcast (not as Nachforderung) case GEF_DATENVERKEHR_TDS_SRT: case GEF_DATENVERKEHR_TDS_DMF: case GEF_TREFFER_AUSLOESUNG: case GEF_ALLGEMEINE_NACHRICHT: addBroadcastOnlyTelegramToMap(a_radioTelegram); break; // the following telegrams must exist only once in BC-list case GEF_SPRACHAUSWAHL: case GEF_KONFIGURATION_WIRKMODELL: case GEF_TEILNEHMERMODE_SETZEN: case GEF_TEILNEHMER_KONFIG_SETZEN: replaceLastTelegramAndAddAtEnd(a_radioTelegram); break; case GEF_WIRKFELD_EINZELGESCHOSS: case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER: case GEF_WIRKFELD_MINENSPERRE: case GEF_WIRKFELD_A_KAMPFMITTEL: case GEF_WIRKFELD_C_KAMPFMITTEL: case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER_STOP: case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER_FORTSETZEN: case GEF_WIRKFELD_STATUS_MINENSPERRE: case GEF_WIRKFELD_EINZELMINENSIMULATOR: { // take wirkfeld to be (re)activated out of sammeltelegram of deactivated wirkfelder const uint16_t wirkfeldKennung = a_radioTelegram->getPayloadDataUInt32(5, 2); INFO_LOG(m_logger) << "Adding to braodcast map, Telegram Id: " << a_radioTelegram->getTelID() << ", kennung: " << wirkfeldKennung; if (isEffectSimulationIdDeactivated(wirkfeldKennung)) { INFO_LOG(m_logger) << "Got tele with tel id " << telegramId2String(a_radioTelegram->getTelID()) << ", wirkfeldkennung: " << wirkfeldKennung << ", bcIndex: " << a_radioTelegram->getBroadcastIndex() << " but this effect simulation is already deactivated... ignoring"; dumpTelegram(m_logger, a_radioTelegram->getBroadcastIndex(), a_radioTelegram.get()); emit areaEffectSimulationActivated(wirkfeldKennung); } else if (0 != wirkfeldKennung && wirkfeldKennung <= std::numeric_limits::max()) { switch (a_radioTelegram->getTelID()) { case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER_STOP: // remove the corresponding FeuerFortsetzen telegram addWirkfeldTelegram(a_radioTelegram, wirkfeldKennung, GEF_WIRKFELD_FLAECHENDECKENDES_FEUER_FORTSETZEN); break; case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER_FORTSETZEN: // remove the corresponding FeuerStop telegram addWirkfeldTelegram(a_radioTelegram, wirkfeldKennung, GEF_WIRKFELD_FLAECHENDECKENDES_FEUER_STOP); break; case GEF_WIRKFELD_AENDERUNG_BEZEICHNER: addWirkfeldTelegram(a_radioTelegram, wirkfeldKennung); break; case GEF_WIRKFELD_STATUS_MINENSPERRE: // remove the corresponding deactivierung_einzelmine telegrams addWirkfeldTelegram(a_radioTelegram, wirkfeldKennung, GEF_DEAKTIVIERUNG_EINZELMINE); break; default: removeWirkfeldTelegrams(wirkfeldKennung); addWirkfeldTelegram(a_radioTelegram, wirkfeldKennung); removeFromDeactivatedAesArea(wirkfeldKennung); emit areaEffectSimulationActivated(wirkfeldKennung); break; } // the original telegram will be added to broadcast-list m_BroadcastMap.insert(std::make_pair(a_radioTelegram->getBroadcastIndex(), a_radioTelegram)); } else { // this should never happen, because CoPrtCnvRadioTelegram::isContentValid() filters this out ERROR_LOG(m_logger) << "Internal error: Got invalid kennung: " << wirkfeldKennung; } break; } case GEF_WIRKFELD_AENDERUNG_BEZEICHNER: { auto wirkfeldTyp = static_cast(a_radioTelegram->getPayloadDataUInt32(5, 1)); uint16_t wirkfeldKennung = a_radioTelegram->getPayloadDataUInt32(6, 2); INFO_LOG(m_logger) << "addToBroadcastMap - Wirkfeld Aenderung Bezeichner for type " << wirkfeldTyp2Str(wirkfeldTyp) << " - kennung: " << wirkfeldKennung; addWirkfeldTelegram(a_radioTelegram, wirkfeldKennung); break; } case GEF_DEAKTIVIERUNG_WIRKFELD: { auto wirkfeldTyp = static_cast(a_radioTelegram->getPayloadDataUInt32(5, 1)); uint16_t wirkfeldKennung = a_radioTelegram->getPayloadDataUInt32(6, 2); INFO_LOG(m_logger) << "Deaktivierung Wirkfeld for type " << wirkfeldTyp2Str(wirkfeldTyp) << " - kennung: " << wirkfeldKennung << ", bc: " << a_radioTelegram->getBroadcastIndex(); switch (wirkfeldTyp) { case TrainingAreaBase::TetraIcd::AreaEffectSimulationType::MineArea: // Minensperre case TrainingAreaBase::TetraIcd::AreaEffectSimulationType::ExplosionArea: // Steilfeuer Fläche case TrainingAreaBase::TetraIcd::AreaEffectSimulationType::ExplosionSingle: // Steilfeuer Einzel case TrainingAreaBase::TetraIcd::AreaEffectSimulationType::NContamination: // A-Waffe case TrainingAreaBase::TetraIcd::AreaEffectSimulationType::CContamination: // C-Waffe case TrainingAreaBase::TetraIcd::AreaEffectSimulationType::DusSingleMineSimulator: // DUS-Einzelminensimulator case TrainingAreaBase::TetraIcd::AreaEffectSimulationType::DangerZoneExplosion: if (0 != wirkfeldKennung && wirkfeldKennung <= 32767) { emit areaEffectSimulationDeactivated(wirkfeldKennung); removeWirkfeldTelegrams(wirkfeldKennung); if (addToDeactivatedAesArea(wirkfeldKennung, a_radioTelegram->getBroadcastIndex())) addWirkfeldTelegram(a_radioTelegram, wirkfeldKennung); } else { // this should never happen, because CoPrtCnvRadioTelegram::isContentValid() filters this out ERROR_LOG(m_logger) << "Internal error: Got invalid kennung: " << wirkfeldKennung; } break; case TrainingAreaBase::TetraIcd::AreaEffectSimulationType::FunctionalArea: // Telegram DeaktivierenFunktionsraum should be used, instead DeaktivierungWirkfeld with wirkfeldTyp 6 default: // this should never happen, because CoPrtCnvRadioTelegram::isContentValid() filters this out ERROR_LOG(m_logger) << "Internal error: WirkfeldTyp " << wirkfeldTyp2Str(wirkfeldTyp) << " in GEF_DEAKTIVIERUNG_WIRKFELD not implemented"; } m_BroadcastMap.insert(std::make_pair(a_radioTelegram->getBroadcastIndex(), a_radioTelegram)); break; } case GEF_DEAKTIVIERUNG_EINZELMINE: { deactivateSingleMine(a_radioTelegram); break; } case GEF_REAKTIVIERUNG_DUS_MINE: { reactivateDusMine(a_radioTelegram); break; } case GEF_TECHNIK_FUNKTIONSRAUM: { // take funktionsraum to be (re)activated out of sammeltelegram of deactivated funktionsräume uint16_t funktionsraumkennung = a_radioTelegram->getPayloadDataUInt32(5, 2); // remove an old already existing funktionsraum-telegram with this kennung removeFromTechnikFunktionsAndBroadcastMaps(funktionsraumkennung); // insert the funktionsraum-telegram m_TechnikFunktionsraumTelegramMap.insert(std::make_pair(funktionsraumkennung, a_radioTelegram)); // the original telegram will be added to broadcast-list m_BroadcastMap.insert(std::make_pair(a_radioTelegram->getBroadcastIndex(), a_radioTelegram)); break; } case GEF_FUNKTIONSRAUM: { // take funktionsraum to be (re)activated out of sammeltelegram of deactivated funktionsräume uint16_t funktionsraumkennung = a_radioTelegram->getPayloadDataUInt32(5, 2); // remove an old already existing funktionsraum-telegram with this kennung removeFromFunktionsAndBroadcastMaps(funktionsraumkennung); removeFromDeactivatedFuncArea(funktionsraumkennung); // insert the funktionsraum-telegram m_FunktionsraumTelegramMap.insert(std::make_pair(funktionsraumkennung, a_radioTelegram)); // the original telegram will be added to broadcast-list m_BroadcastMap.insert(std::make_pair(a_radioTelegram->getBroadcastIndex(), a_radioTelegram)); break; } case GEF_DEAKTIVIEREN_FUNKTIONSRAUM: { uint16_t funktionsraumkennung = a_radioTelegram->getPayloadDataUInt32(5, 2); emit areaEffectSimulationDeactivated(funktionsraumkennung); removeFromFunktionsAndBroadcastMaps(funktionsraumkennung); if (addToDeactivatedFuncArea(funktionsraumkennung, a_radioTelegram->getBroadcastIndex())) m_FunktionsraumTelegramMap.insert(std::make_pair(funktionsraumkennung, a_radioTelegram)); m_BroadcastMap.insert(std::make_pair(a_radioTelegram->getBroadcastIndex(), a_radioTelegram)); break; } default: m_BroadcastMap.insert(std::make_pair(a_radioTelegram->getBroadcastIndex(), a_radioTelegram)); } // dump(); } template bool removeFromContainers(T &id2bcIndex, S &BcIndex2id, int aesAreaId) { auto it = id2bcIndex.find(aesAreaId); if (it == id2bcIndex.end()) return false; BcIndex2id.remove(it.value()); id2bcIndex.erase(it); return true; } template bool addToContainers(T &id2bcIndex, S &BcIndex2id, int aesAreaId, int bcIndex) { auto it = id2bcIndex.find(aesAreaId); if (it != id2bcIndex.end()) { if (it.value() == bcIndex) return false; BcIndex2id.remove(it.value()); } id2bcIndex[aesAreaId] = bcIndex; BcIndex2id[bcIndex] = aesAreaId; return true; } bool PrtCnvBroadcastMgr::removeFromDeactivatedAesArea(int aesAreaId) { return removeFromContainers(m_deactivatedAesArea2bcIndex, m_deactivatedBcIndex2AesArea, aesAreaId); } // returns true when added bool PrtCnvBroadcastMgr::addToDeactivatedAesArea(int aesAreaId, int bcIndex) { return addToContainers(m_deactivatedAesArea2bcIndex, m_deactivatedBcIndex2AesArea, aesAreaId, bcIndex); } bool PrtCnvBroadcastMgr::removeFromDeactivatedFuncArea(int aesAreaId) { return removeFromContainers(m_deactivatedFuncArea2bcIndex, m_deactivatedBcIndex2FuncArea, aesAreaId); } bool PrtCnvBroadcastMgr::addToDeactivatedFuncArea(int aesAreaId, int bcIndex) { return addToContainers(m_deactivatedFuncArea2bcIndex, m_deactivatedBcIndex2FuncArea, aesAreaId, bcIndex); } bool PrtCnvBroadcastMgr::removeFromDeactivatedDusMine(int dusMineId) { return removeFromContainers(m_deactivatedDusMine2bcIndex, m_deactivatedBcIndex2DusMine, dusMineId); } bool PrtCnvBroadcastMgr::addToDeactivatedDusMine(int dusMineId, int bcIndex) { return addToContainers(m_deactivatedDusMine2bcIndex, m_deactivatedBcIndex2DusMine, dusMineId, bcIndex); } bool PrtCnvBroadcastMgr::removeFromActivatedDusMine(int dusMineId) { return (m_activatedDusMines.remove(dusMineId)); } void PrtCnvBroadcastMgr::addToActivatedDusMine(int dusMineId) { m_activatedDusMines.insert(dusMineId); } // --------------------------------------------------------------- // searches the kennung within sammeltelegram. // returns -1 when kennung not found // --------------------------------------------------------------- int PrtCnvBroadcastMgr::getSammeltelegramIndex(const std::shared_ptr &a_sammelTelegram, const uint16_t a_kennung) const { // decide the length of anzahl uint16_t nbrLength = 0; switch (a_sammelTelegram->getTelID()) { case GEF_WF_SAMMELDEAKTIVIERUNG: case GEF_WF_SAMMELDEAKTIVIERUNG_FUNKTIONSRAUM: nbrLength = 1; break; case GEF_WF_SAMMELDEAKTIVIERUNG_DUS_MINEN: case GEF_REAKTIVIERUNG_DUS_MINE: nbrLength = 2; break; default: ERROR_LOG(m_logger) << "Unknown Sammeltelegram with telId=" << a_sammelTelegram->getTelID(); return -1; } // search the kennung in sammeltelegram const int cntAesIds = a_sammelTelegram->getPayloadDataUInt32(5, nbrLength); for (int i = 0; i < cntAesIds; i++) { if (a_kennung == a_sammelTelegram->getPayloadDataUInt32(5 + nbrLength + i * 2, 2)) return i; } return -1; } // --------------------------------------------------------------- // Handles the DEAKTIVIERUNG_EINZELMINE-telegram for normal- // broadcasts and nachforderungen. This includes deactivation // of mines from minen-sperre and DUS-mines. // --------------------------------------------------------------- void PrtCnvBroadcastMgr::deactivateSingleMine(const std::shared_ptr &a_radioTelegram) { uint32_t wirkfeldkennung = a_radioTelegram->getPayloadDataUInt32(9, 2); uint32_t minenkennung = a_radioTelegram->getPayloadDataUInt32(11, 2); // DUS or normal mine? if (0 == wirkfeldkennung) { removeFromActivatedDusMine(minenkennung); addToDeactivatedDusMine(minenkennung, a_radioTelegram->getBroadcastIndex()); DEBUG_LOG(m_logger) << "Deactivating DUS-Mine: " << minenkennung << ", #deactivated mines: " << m_deactivatedBcIndex2DusMine.size(); removeDusMineDeactivationFromBroadcastMap(minenkennung); // remove old if there is already a telegram m_BroadcastMap.insert(std::make_pair(a_radioTelegram->getBroadcastIndex(), a_radioTelegram)); } else { // normal mine => change mine in "wirkfeldStatusMinensperre" bool sentAsNormalBroadcastFinished = deactivateEinzelmineFromMinensperre(wirkfeldkennung, minenkennung, a_radioTelegram->getBroadcastIndex()); if (sentAsNormalBroadcastFinished) { // add the deactivierungEinzelmine-telegram as broadcast-only-telegram addWirkfeldTelegram(a_radioTelegram, wirkfeldkennung, GEF_BESTAETIGUNG, minenkennung); addBroadcastOnlyTelegramToMap(a_radioTelegram); } else { // the wirkfeldstatusMinensperre-telegram has changed while sending it as normal broadcast is not yet // finished => we not add the deactiveEinzelmine-telegram as broadcast-only, so the updated wirkfeld- // statusMinensperre-telegram will be sent as normal broadcast-telegram. The deactivateEinzelmine- // telegram must be deleted // delete a_radioTelegram; // no longer needed, shared_ptr } } } // --------------------------------------------------------------- // Deactivates the mine in the corresponding // "WirkfeldstatusMinensperre"-telegram and moves the // "WirkfeldstatusMinensperre"-telegram to the position of // "DeactiviereEinzelmine"-telegram. // Returns true if the "WirkfeldstatusMinensperre"-telegram // was already sent 3 times as normal broadcast. // --------------------------------------------------------------- bool PrtCnvBroadcastMgr::deactivateEinzelmineFromMinensperre(const uint16_t a_wirkfeldKennung, const uint16_t a_minenKennung, const uint16_t a_broadcastIndex) { // search the corresponding "WirkfeldstatusMinensperre"-telegram in the broadcast-map auto iter = m_WirkfeldTelegramMapByKennung.find(a_wirkfeldKennung); while (iter != m_WirkfeldTelegramMapByKennung.end()) { if (iter->first != a_wirkfeldKennung) break; auto radioTelegram = iter->second; if (radioTelegram->getTelID() == GEF_WIRKFELD_STATUS_MINENSPERRE && a_wirkfeldKennung == radioTelegram->getPayloadDataUInt32(5, 2)) { // check whether the minekennung is valid uint16_t nbrOfMines = radioTelegram->getPayloadDataUInt32(7, 2); if (nbrOfMines >= a_minenKennung && a_minenKennung != 0) { // calc the position of mine // definition: The first mine is mine 1; MSB is bit 0 => mine 1 is bit 7; mine 2 is bit 6; ... mine 8 is bit 0; Mine 9 is bit 7 (of next byte)... uint16_t bytePos = 9 + (a_minenKennung - 1) / 8; if (bytePos < radioTelegram->getPayloadSize()) { uint8_t mask = 0x80 >> ((a_minenKennung - 1) % 8); // INFO_LOG(m_logger) << "pos=" << (pos-9) << " mask=" << (int)mask; // clear the bit for the mine radioTelegram->setPayloadDataWithIndex(bytePos, (uint8_t)(radioTelegram->getPayloadDataUInt8(bytePos) & (~mask)), 1); moveTelegramInBroadcastMap(radioTelegram, a_broadcastIndex); // if we have a corresponding wirkstatus-minensperre which was not sent 3 times as normal broadcast, // we have to take it out and to send the new wirkfeldstatus-minensperre-telegram instead. Also all // the deactivate einzelmine-telegrams must not be stored within the broadcast-list as broadcast-only // telegram, therefore we return false bool dummy; if (radioTelegram->mustBeSentAsBroadcast(std::numeric_limits::max(), dummy, false)) { INFO_LOG(m_logger) << "A normal broadcast-telegram WirkfeldStatusMinensperre still exists"; return false; } return true; } else ERROR_LOG(m_logger) << "Got corrupt telegram WirkfeldstatusMinensperre with nbrOfMines=" << nbrOfMines << " and payloadSize=" << radioTelegram->getPayloadSize(); } else { // minenkennung is too big to fit into statusMinensperre => Errormessage, do not update in status, but send as normal broadcast ERROR_LOG(m_logger) << "Mine with kennung=" << a_minenKennung << " does not fit into status (nbrOfMines=" << nbrOfMines << ")"; } return true; } ++iter; } ERROR_LOG(m_logger) << "Did not find the WirkfeldStatusMinensperre with kennung=" << a_wirkfeldKennung << ", therefore deactivated mine with kennung=" << a_minenKennung << " could not be updated."; return true; } // --------------------------------------------------------------- // Handles the REAKTIVIERUNG_DUS_MINE-telegram for normal- // broadcasts and nachforderungen. // --------------------------------------------------------------- void PrtCnvBroadcastMgr::reactivateDusMine(const std::shared_ptr &a_radioTelegram) { // take all DUS-mines to be reactivated out of sammeltelegram of deactivated DUS-mines const uint32_t nbrOfMines = a_radioTelegram->getPayloadDataUInt32(5, 2); // #mines is 2 bytes DEBUG_LOG(m_logger) << "Reactivating DUS-Mines, #mines: " << nbrOfMines; if (0 == nbrOfMines) { // move all mines from sammeltelegram deactivation to activation for (int dusMineId : qAsConst(m_deactivatedBcIndex2DusMine)) { removeDusMineDeactivationFromBroadcastMap(dusMineId); addToActivatedDusMine(dusMineId); } m_deactivatedDusMine2bcIndex.clear(); m_deactivatedBcIndex2DusMine.clear(); } else { // normal-case: reactivate some DUS-mines for (uint32_t i = 0; i < nbrOfMines; i++) { const int dusMineId = a_radioTelegram->getPayloadDataUInt32(7 + 2 * i, 2); DEBUG_LOG(m_logger) << "Reactivating DUS-Mines, mindId: " << dusMineId; removeDusMineDeactivationFromBroadcastMap(dusMineId); removeFromDeactivatedDusMine(dusMineId); addToActivatedDusMine(dusMineId); } } auto pred = [](const std::shared_ptr &tele) -> bool { return (tele->getTelID() == GEF_REAKTIVIERUNG_DUS_MINE); }; removeFromBroadcastMapFunc(pred); m_BroadcastMap.insert(std::make_pair(a_radioTelegram->getBroadcastIndex(), a_radioTelegram)); } // --------------------------------------------------------------- // adds the wirkfeld-telegram to the by time and by kennung // maps // --------------------------------------------------------------- void PrtCnvBroadcastMgr::addWirkfeldTelegram(const std::shared_ptr &a_radioTelegram, const uint16_t a_wirkfeldKennung, const gef_telegramm_id_et a_telId, const uint16_t a_minenKennung) { // remove an old already existing wirkfeld-telegram with this kennung auto iter = m_WirkfeldTelegramMapByKennung.find(a_wirkfeldKennung); while (iter != m_WirkfeldTelegramMapByKennung.end()) { if (iter->first != a_wirkfeldKennung) break; auto radioTelegram = iter->second; // same mineindex (only for "Deaktivierung_Einzelmine")? bool isSameMine = true; if (0 != a_minenKennung) { if (GEF_DEAKTIVIERUNG_EINZELMINE == a_radioTelegram->getTelID()) { if (GEF_DEAKTIVIERUNG_EINZELMINE == radioTelegram->getTelID()) { uint16_t mineIndex = radioTelegram->getPayloadDataUInt32(11, 2); isSameMine = mineIndex == a_minenKennung; INFO_LOG(m_logger) << "Mine with kennung=" << a_minenKennung << " found: " << isSameMine; } } else ERROR_LOG(m_logger) << "Internal error: Only telegrams 'Deaktivierung_Einzelmine' must be called with a_mineKennung. I proceed without respecting mine-kennung..."; } // same telegram-id, or opposite telegram (used for stop- and fortsetzen dauerfeuer and deaktivierung einzelmine)? if ((radioTelegram->getTelID() == a_radioTelegram->getTelID() || radioTelegram->getTelID() == a_telId) && isSameMine) { // remove the old telegram from wirkfeld- and broadcast-map removeFromWirkfeldAndBroadcastMaps(iter); // iterator is corrupt after calling this function ... iter = m_WirkfeldTelegramMapByKennung.find(a_wirkfeldKennung); } else ++iter; } // now add the new wirkfeld-telegram INFO_LOG(m_logger) << "Add kennung: " << a_wirkfeldKennung << " telId=" << a_radioTelegram->getTelID() << " BC=" << a_radioTelegram->getBroadcastIndex(); m_WirkfeldTelegramMapByKennung.insert(std::make_pair(a_wirkfeldKennung, a_radioTelegram)); time_t expireTime = a_radioTelegram->getExpireTime(); if (expireTime > 0) m_WirkfeldTelegramMapByTime.insert(std::make_pair(expireTime, a_radioTelegram)); } // --------------------------------------------------------------- // Removes the wirkfeld-telegrams with this kennung from the // by time and by kennung maps. // Replace this telegrams with Nop in BC-list. // Finally the telegram will be deleted. // --------------------------------------------------------------- void PrtCnvBroadcastMgr::removeWirkfeldTelegrams(const uint16_t a_kennung) { INFO_LOG(m_logger) << "Removing all Wirkfeld-Telegrams with kennung=" << a_kennung; // search all wirkfeld-telegrams with this kennung auto iter = m_WirkfeldTelegramMapByKennung.find(a_kennung); while (iter != m_WirkfeldTelegramMapByKennung.end()) { if (iter->first != a_kennung) break; removeFromWirkfeldAndBroadcastMaps(iter); iter = m_WirkfeldTelegramMapByKennung.find(a_kennung); } } // --------------------------------------------------------------- // Removes the telegram with a_funktionsraumKennung from // funktionsraum-map and from the broadcast-map. // If the telegram is a attached BC-only-telegram, it will be // detached. // The telegram itself will be deleted. // ATTENTION: iterators to m_FunktionsraumTelegramMap could be // invalid after calling this function // --------------------------------------------------------------- void PrtCnvBroadcastMgr::removeFromFunktionsAndBroadcastMaps(const uint16_t a_funktionsraumKennung) { auto iter = m_FunktionsraumTelegramMap.find(a_funktionsraumKennung); if (iter != m_FunktionsraumTelegramMap.end()) { auto radioTelegram = iter->second; // get the BC-Index for removing of broadcast-map // remove the telegram from funktionsraum-map INFO_LOG(m_logger) << "Removing Funktionsraum-telegram with kennung=" << a_funktionsraumKennung << " and BC=" << radioTelegram->getBroadcastIndex(); m_FunktionsraumTelegramMap.erase(iter); removeFromBroadcastMap(radioTelegram); } } // --------------------------------------------------------------- // Removes the telegram with a_funktionsraumKennung from // funktionsraum-map and from the broadcast-map. // If the telegram is a attached BC-only-telegram, it will be // detached. // The telegram itself will be deleted. // ATTENTION: iterators to m_FunktionsraumTelegramMap could be // invalid after calling this function // --------------------------------------------------------------- void PrtCnvBroadcastMgr::removeFromTechnikFunktionsAndBroadcastMaps(const uint16_t a_funktionsraumKennung) { auto iter = m_TechnikFunktionsraumTelegramMap.find(a_funktionsraumKennung); if (iter != m_TechnikFunktionsraumTelegramMap.end()) { auto radioTelegram = iter->second; // get the BC-Index for removing of broadcast-map // remove the telegram from funktionsraum-map INFO_LOG(m_logger) << "Removing Funktionsraum-telegram with kennung=" << a_funktionsraumKennung << " and BC=" << radioTelegram->getBroadcastIndex(); m_TechnikFunktionsraumTelegramMap.erase(iter); removeFromBroadcastMap(radioTelegram); } } // --------------------------------------------------------------- // Removes the telegram where // wirkfeldTelegramMapByKennungIterator points to from // wirkfeld-maps and from the broadcast-map. // If the telegram is a attached BC-only-telegram, it will be // detached. // The telegram itself will be deleted. // ATTENTION: iterators to m_WirkfeldTelegramMapByKennung and // m_WirkfeldTelegramMapByTime could be invalid after calling // this function // --------------------------------------------------------------- void PrtCnvBroadcastMgr::removeFromWirkfeldAndBroadcastMaps(const std::multimap>::iterator &a_wirkfeldTelegramMapByKennungIterator) { auto radioTelegram = a_wirkfeldTelegramMapByKennungIterator->second; INFO_LOG(m_logger) << "Removing wirkfeld-telegram with telId=" << radioTelegram->getTelID() << ", kennung=" << a_wirkfeldTelegramMapByKennungIterator->first << " and BC=" << radioTelegram->getBroadcastIndex() << " from byWirkfeldKennung-map"; // remove the telegram from by kennung map m_WirkfeldTelegramMapByKennung.erase(a_wirkfeldTelegramMapByKennungIterator); // remove the telegram from by time map time_t expireTime = radioTelegram->getExpireTime(); if (0 != expireTime) { auto iter = m_WirkfeldTelegramMapByTime.find(expireTime); while (iter != m_WirkfeldTelegramMapByTime.end()) { if (iter->second == radioTelegram) { INFO_LOG(m_logger) << "Removing wirkfeld-telegram with telId=" << radioTelegram->getTelID() << " also from byTime-map"; m_WirkfeldTelegramMapByTime.erase(iter); break; } ++iter; } } removeFromBroadcastMap(radioTelegram); } // --------------------------------------------------------------- // Removes the telegram a_radioTelegram from the broadcast-map. // If the a_radioTelegram has an attached telegram, the // attached telegram will be detached and added to broadcast- // map as broadcast-only telegram. // If the telegram is an attached BC-only-telegram, it will be // detached. // ATTENTION: iterators to m_BroadcastMap could be invalid // after calling this function // --------------------------------------------------------------- void PrtCnvBroadcastMgr::removeFromBroadcastMap(const std::shared_ptr &a_radioTelegram) { // remove from broadcast-map if (!a_radioTelegram) return; auto iter = m_BroadcastMap.find(a_radioTelegram->getBroadcastIndex()); if (iter != m_BroadcastMap.end()) { if (iter->second == a_radioTelegram) { m_BroadcastMap.erase(iter); detachTelegram(a_radioTelegram); } else if (iter->second->getBroadcastonlyTelegram() == a_radioTelegram) iter->second->setBroadcastonlyTelegram(nullptr); } } void PrtCnvBroadcastMgr::removeFromBroadcastMapFunc(const std::function &)> &pred) { auto it = m_BroadcastMap.begin(); while (it != m_BroadcastMap.end()) { const auto &p = *it; if (pred(p.second)) it = m_BroadcastMap.erase(it); else ++it; } } void PrtCnvBroadcastMgr::removeDusMineDeactivationFromBroadcastMap(int dusMineId) { auto pred = [dusMineId](const std::shared_ptr &tele) -> bool { return (tele->getTelID() == GEF_DEAKTIVIERUNG_EINZELMINE && 0 == tele->getPayloadDataUInt32(9, 2) && uint32_t(dusMineId) == tele->getPayloadDataUInt32(11, 2)); }; removeFromBroadcastMapFunc(pred); } template void removeFromMap(T &cont, const std::shared_ptr &a_radioTelegram) { auto iter = cont.begin(); while (iter != cont.end()) { if (iter->second == a_radioTelegram) iter = cont.erase(iter); else ++iter; } } // --------------------------------------------------------------- // Removes the telegram a_radioTelegram from all maps // (broadcast-, wirkfeld-, funktionsraum- and lastTelegram- // map). // If the telegram is a attached BC-only-telegram, it will be // detached. // ATTENTION: iterators to all maps could be invalid after // calling this function // --------------------------------------------------------------- void PrtCnvBroadcastMgr::removeFromAllMaps(const std::shared_ptr &a_radioTelegram) { INFO_LOG(m_logger) << "Remove telegram with telId=" << a_radioTelegram->getTelID() << " BC=" << a_radioTelegram->getBroadcastIndex(); removeFromBroadcastMap(a_radioTelegram); // remove from byWirkfeldKennung-map removeFromMap(m_WirkfeldTelegramMapByKennung, a_radioTelegram); // remove from byWirkfeldTime-map removeFromMap(m_WirkfeldTelegramMapByTime, a_radioTelegram); // remove from funktionsraum-map removeFromMap(m_FunktionsraumTelegramMap, a_radioTelegram); // remove from last-telegram-map removeFromMap(m_LastTelegramMap, a_radioTelegram); } // --------------------------------------------------------------- // used for telegrams that must only exists once in BC-list // --------------------------------------------------------------- void PrtCnvBroadcastMgr::replaceLastTelegramAndAddAtEnd(const std::shared_ptr &a_newRadioTelegram) { // do we have a last telegram? auto iter = m_LastTelegramMap.find(a_newRadioTelegram->getTelID()); if (iter != m_LastTelegramMap.end()) { auto lastRadioTelegram = iter->second; // yes => replace the last telegram with NOP INFO_LOG(m_logger) << "remove last telegram with telId=" << lastRadioTelegram->getTelID() << " and BC=" << lastRadioTelegram->getBroadcastIndex(); removeFromBroadcastMap(lastRadioTelegram); m_LastTelegramMap.erase(iter); } // insert the new telegram into BC-list m_BroadcastMap.insert(std::make_pair(a_newRadioTelegram->getBroadcastIndex(), a_newRadioTelegram)); // and insert into last-telegram-list m_LastTelegramMap.insert(std::make_pair(a_newRadioTelegram->getTelID(), a_newRadioTelegram)); INFO_LOG(m_logger) << "New last telegram with with telId=" << a_newRadioTelegram->getTelID() << " and BC=" << a_newRadioTelegram->getBroadcastIndex(); } // --------------------------------------------------------------- // Moves a_radioTelegram to a_newBroadcastIndex in broadcast- // map. // If a_radioTelegram has an attached broadcast-only-telegram, // the broadcastOnly-telegram will be detached and kept on the // old broadcast-index position. // If a_attachTelegram in not NULL, this telegram will be // attached to a_radioTelegram. // --------------------------------------------------------------- void PrtCnvBroadcastMgr::moveTelegramInBroadcastMap(const std::shared_ptr &a_radioTelegram, const uint16_t a_newBroadcastIndex, const std::shared_ptr &a_attachTelegram) { // remove the old telegram from map (and detach) INFO_LOG(m_logger) << "Move telId=" << a_radioTelegram->getTelID() << " from BC=" << a_radioTelegram->getBroadcastIndex() << " to BC=" << a_newBroadcastIndex; removeFromBroadcastMap(a_radioTelegram); // insert telegram a end a_radioTelegram->setBroadcastIndex(a_newBroadcastIndex); m_BroadcastMap.insert(std::make_pair(a_newBroadcastIndex, a_radioTelegram)); // attach a_attachTelegram if (a_attachTelegram && (a_attachTelegram->getTelID() != GEF_REAKTIVIERUNG_DUS_MINE)) { INFO_LOG(m_logger) << "Attach telID=" << a_attachTelegram->getTelID() << " to telID=" << a_radioTelegram->getTelID() << " BC=" << a_newBroadcastIndex; a_radioTelegram->setBroadcastonlyTelegram(a_attachTelegram); a_attachTelegram->setBroadcastIndex(a_newBroadcastIndex); } } // --------------------------------------------------------------- // Returns the actual broadcast-index for normal broadcasts. // --------------------------------------------------------------- uint16_t PrtCnvBroadcastMgr::getBroadcastIndex() const { return m_BroadcastIndex; } // --------------------------------------------------------------- // Returns the broadcastindex for the next broadcast // --------------------------------------------------------------- uint16_t PrtCnvBroadcastMgr::getNextBroadcastIndex() { return ++m_BroadcastIndex; } // --------------------------------------------------------------- // Returns a NOP-telegramm filled with the desired values. // --------------------------------------------------------------- std::shared_ptr PrtCnvBroadcastMgr::getNopTelegram(const uint16_t a_from, const uint16_t a_to) { auto nopTelegram(std::make_shared(GEF_NOP, 7, 0)); nopTelegram->setPayloadDataWithIndex(3, a_from, 2); // Feld Anfang nopTelegram->setPayloadDataWithIndex(5, a_to, 2); // Feld Ende return nopTelegram; } // --------------------------------------------------------------- // Adds the telegram a_broadcastOnlyTelegram (which must have // set already the broadcast-index) to the broadcast-map. The // telegram will be attached to an already existing broadcast- // telegram with same broadcast-index in the map, otherwise it // will be marked as broadcast-only. // If the there is already an broadcast-only-telegram attached, // nothing will be done. // --------------------------------------------------------------- void PrtCnvBroadcastMgr::addBroadcastOnlyTelegramToMap(const std::shared_ptr &a_broadcastOnlyTelegram) { uint16_t broadcastIndex = a_broadcastOnlyTelegram->getBroadcastIndex(); auto iter = m_BroadcastMap.find(broadcastIndex); if (m_BroadcastMap.end() == iter) { // insert as braodcast-only marked telegram INFO_LOG(m_logger) << "Insert telId=" << a_broadcastOnlyTelegram->getTelID() << " as broadcastonly telegram with BC=" << broadcastIndex; a_broadcastOnlyTelegram->setBroadcastOnly(); m_BroadcastMap.insert(std::make_pair(broadcastIndex, a_broadcastOnlyTelegram)); } else { // attach to an already existing broadcast-telegram INFO_LOG(m_logger) << "Add telId=" << a_broadcastOnlyTelegram->getTelID() << " as broadcastonly telegram to telegramm with BC=" << iter->second->getBroadcastIndex(); iter->second->setBroadcastonlyTelegram(a_broadcastOnlyTelegram); } } // --------------------------------------------------------------- // Detaches an existing radio-telegram and adds it to the // broadcast-map // --------------------------------------------------------------- void PrtCnvBroadcastMgr::detachTelegram(const std::shared_ptr &a_radioTelegram) { auto broadcastOnlyTelegram = a_radioTelegram->getBroadcastonlyTelegram(); if (broadcastOnlyTelegram) { uint16_t broadcastIndex = a_radioTelegram->getBroadcastIndex(); // do some checks INFO_LOG(m_logger) << "detach bconly-telegram : telId=" << a_radioTelegram->getTelID() << " BC=" << broadcastIndex; if (broadcastOnlyTelegram->getBroadcastIndex() != broadcastIndex) ERROR_LOG(m_logger) << "Internal error: BC=" << broadcastOnlyTelegram->getBroadcastIndex() << " of attached telegram is not equal BC=" << broadcastIndex; auto iter = m_BroadcastMap.find(broadcastIndex); if (iter != m_BroadcastMap.end()) ERROR_LOG(m_logger) << "Internal error: m_BroadcastMap-entry with BC=" << broadcastIndex << " must not exists"; else { INFO_LOG(m_logger) << "Insert broadcast-only BC=" << broadcastOnlyTelegram->getBroadcastIndex(); broadcastOnlyTelegram->setBroadcastOnly(); m_BroadcastMap.insert(std::make_pair(broadcastIndex, broadcastOnlyTelegram)); } a_radioTelegram->setBroadcastonlyTelegram(nullptr); } } // --------------------------------------------------------------- // Removes all Nop out of broadcast-list and numerates the // broadcast-index of kept broadcast-telegrams new. // --------------------------------------------------------------- void PrtCnvBroadcastMgr::handleOverrunOfBroadcastCounter(const bool a_doRestore) { WARNING_LOG(m_logger) << "Broadcast-counter reaches maximum => reduce broadcast-list and increment reset-counter"; // first we must wait until all data are stored in DB, otherwise we will loose // the broadcast-telegrams, which were sent the 1st time, but not yet stored permanent if (!a_doRestore) { m_DbThread->waitUntilDbWritten(); getAllFinishedBroadcastsFromDb(); } // reduce the broadcast-list uint16_t newBroadcastCounter = 0; std::map> newBroadcastMap; auto iter = m_BroadcastMap.begin(); while (iter != m_BroadcastMap.end()) { auto radioTelegram = iter->second; if (radioTelegram->isBroadcastOnly()) { // delete all broadcast-only telegrams ++iter; // removeFromAllMaps() will destroy the iterator => we increment it before removing removeFromAllMaps(radioTelegram); } else { radioTelegram->setBroadcastIndex(++newBroadcastCounter); auto broadcastOnlyTelegram = radioTelegram->getBroadcastonlyTelegram(); if (broadcastOnlyTelegram) { radioTelegram->setBroadcastonlyTelegram(nullptr); ++iter; // removeFromAllMaps() will destroy the iterator => we increment it before removing removeFromAllMaps(broadcastOnlyTelegram); } else { // all pending normal broadcast should no longer be sent as normal broadcasts, because all TNEs // will anyway ask for nachforderung bool wasLast; if (radioTelegram->mustBeSentAsBroadcast(std::numeric_limits::max(), wasLast, false)) { do { radioTelegram->mustBeSentAsBroadcast(std::numeric_limits::max(), wasLast, true); } while (!wasLast); } ++iter; } INFO_LOG(m_logger) << "New BC-index for broadcast-telegram with telId=" << radioTelegram->getTelID() << " BC=" << newBroadcastCounter; newBroadcastMap.insert(std::make_pair(newBroadcastCounter, radioTelegram)); } } m_BroadcastMap.clear(); m_BroadcastMap = newBroadcastMap; // stop actual nachforderungen and increment reset-counter cleanAndInit(false); if (!a_doRestore) m_DbThread->setResetCounter(m_DbThread->getResetCounter() + 1); WARNING_LOG(m_logger) << "New broadcast-counter=" << newBroadcastCounter << " new reset-counter=" << (int)m_DbThread->getResetCounter(); m_BroadcastIndex = newBroadcastCounter; } // --------------------------------------------------------------- // Restores data after crash for seamless proceeding of work. // --------------------------------------------------------------- void PrtCnvBroadcastMgr::restoreAfterCrash() { getAllFinishedBroadcastsFromDb(true); } // --------------------------------------------------------------- // dumps important values, only for debugging // --------------------------------------------------------------- void PrtCnvBroadcastMgr::dump() { // dump the broadcast-list for (const auto &it : m_BroadcastMap) dumpTelegram(m_loggerBcRequest, it.first, it.second.get(), "m_BroadcastMap, "); for (const auto &it : m_WirkfeldTelegramMapByKennung) dumpTelegram(m_loggerBcRequest, it.first, it.second.get(), "m_WirkfeldTelegramMapByKennung, "); for (const auto &it : m_WirkfeldTelegramMapByTime) dumpTelegram(m_loggerBcRequest, it.first, it.second.get(), "m_WirkfeldTelegramMapByTime, "); for (const auto &it : m_FunktionsraumTelegramMap) dumpTelegram(m_loggerBcRequest, it.first, it.second.get(), "m_FunktionsraumTelegramMap, "); for (const auto &it : m_TechnikFunktionsraumTelegramMap) dumpTelegram(m_loggerBcRequest, it.first, it.second.get(), "m_TechnikFunktionsraumTelegramMap, "); for (const auto &it : m_LastTelegramMap) dumpTelegram(m_loggerBcRequest, it.first, it.second.get(), "m_LastTelegramMap, "); } // --------------------------------------------------------------- // Increments the tick-counter of broadcast-manager and // informs the broadcast-manager about the traffic-load. // --------------------------------------------------------------- void PrtCnvBroadcastMgr::incTickAndSetTrafficLoad(const bool a_isHighLoad, const bool a_isFullLoad) { if (e_ST_COLLECT == m_State) ++m_TickCounter; m_IsFullLoad = a_isFullLoad; if (a_isHighLoad) m_ActClaimWindow = m_HighLoadFactor * m_ClaimWindow; else m_ActClaimWindow = m_ClaimWindow; } // --------------------------------------------------------------- // Checks if the given id is in m_deactivatedAesArea2bcIndex // --------------------------------------------------------------- bool PrtCnvBroadcastMgr::isEffectSimulationIdDeactivated(const uint16_t effectsimulationId) const { INFO_LOG(m_logger) << "Checking deactivation for Id " << effectsimulationId; return m_deactivatedAesArea2bcIndex.contains(effectsimulationId); } } // namespace PrtCnv