/** * FrequencyPlanningHandler * $Id: combat_net_radio_handler.cpp 34946 2017-08-21 14:13:32Z baeuerle_h $ * $HeadURL: svn://slssvn/TDS/branches/product/devel/trunk/Lts/Services/AudioVideoManagement/combat_net_radio_handler.cpp $ * $Author: baeuerle_h $ * Created by Hannes Baeuerle, SLSE, 2017-08-15 */ #include "frequency_planning_handler.h" #include #include "frequency_set_handler.h" #include "cnr_handler.h" #include "active_exercises.h" #include "exercise_context.h" #include #include #include #include #include namespace AudioVideoManagement { namespace { FrequencyPlanningData toDds(const Database::AvmFrequencyPlanningData &dbEntry) { FrequencyPlanningData ddsEntry; ddsEntry.cnrConfigId(dbEntry.cnrConfigId); ddsEntry.frequencyPlanningId(dbEntry.frequencyPlanningId); ddsEntry.properties(dbEntry.properties.toStdString()); return ddsEntry; } FrequencyPlanning toDds(const Database::AvmFrequencyPlanning &dbPlan, const std::vector &dbEntries) { FrequencyPlanning ddsPlan; ddsPlan.id(dbPlan.id); ddsPlan.date(dbPlan.dateOfCreation); if (!dbPlan.exerciseId.isNull()) ddsPlan.exerciseId(dbPlan.exerciseId.value()); ddsPlan.frequencySetId(dbPlan.frequencySetId); ddsPlan.name(dbPlan.name.toStdString()); std::vector ddsEntries; ddsEntries.reserve(dbEntries.size()); for (const Database::AvmFrequencyPlanningData &dbEntry : dbEntries) { ddsEntries.push_back(toDds(dbEntry)); } ddsPlan.data(ddsEntries); return ddsPlan; } ActiveFrequencyPlanning toDds(const Database::AvmActiveFrequencyPlanning &dbData) { int exerciseId = dbData.exerciseId.isNull() ? 0 : dbData.exerciseId.value(); return ActiveFrequencyPlanning(exerciseId, dbData.frequencyPlanningId); } Database::AvmFrequencyPlanning toDb(const ModifyFrequencyPlanningRequest &request) { Database::AvmFrequencyPlanning result; result.id = request.id(); result.name = QString::fromStdString(request.name()); if(request.exerciseId() != 0) result.exerciseId = request.exerciseId(); result.dateOfCreation = request.date(); result.frequencySetId = request.frequencySetId(); return result; } Database::AvmFrequencyPlanning toDb(const FrequencyPlanning &ddsPlan) { Database::AvmFrequencyPlanning dbPlan; dbPlan.id = ddsPlan.id(); if (ddsPlan.exerciseId().is_set()) dbPlan.exerciseId = ddsPlan.exerciseId().get(); dbPlan.dateOfCreation = ddsPlan.date(); dbPlan.frequencySetId = ddsPlan.frequencySetId(); return dbPlan; } Database::AvmFrequencyPlanningData toDb(const FrequencyPlanningData &data, int32_t frequencyPlanningId) { Database::AvmFrequencyPlanningData result; result.frequencyPlanningId = frequencyPlanningId; result.cnrConfigId = data.cnrConfigId(); result.properties = QString::fromStdString(data.properties()); return result; } } // anonymous namespace FrequencyPlanningHandler::FrequencyPlanningHandler(Interface::Dts *dts, Interface::Fts *fts, DatabaseInterface *db, FrequencySetHandler *frequencySetHandler, ActiveExercises *activeExercises) : ModifyFrequencyPlanningRequestHandler(nullptr) , RemoveFrequencyPlanningRequestHandler(nullptr) , ModifyActiveFrequencyPlanningRequestHandler(nullptr) , RemoveActiveFrequencyPlanningRequestHandler(nullptr) , m_dts(dts) , m_fts(fts) , m_db(db) , m_frequencySetHandler(frequencySetHandler) , m_activeExercises(activeExercises) , m_logger(getLogger("FrequencyPlanningHandler")) { Q_ASSERT(dts); Q_ASSERT(fts); Q_ASSERT(db); Q_ASSERT(frequencySetHandler); Q_ASSERT(activeExercises); m_fts->setModifyFrequencyPlanningRequestHandler(this); m_fts->setRemoveFrequencyPlanningRequestHandler(this); m_fts->setModifyActiveFrequencyPlanningRequestHandler(this); m_fts->setRemoveActiveFrequencyPlanningRequestHandler(this); connect(m_db, &DatabaseInterface::notifyDeleteFrequencyPlanningData, this, &FrequencyPlanningHandler::onDbDeletedFrequencyPlanningData); } FrequencyPlanningHandler::~FrequencyPlanningHandler() { m_fts->setModifyFrequencyPlanningRequestHandler(nullptr); m_fts->setRemoveFrequencyPlanningRequestHandler(nullptr); m_fts->setModifyActiveFrequencyPlanningRequestHandler(nullptr); m_fts->setRemoveActiveFrequencyPlanningRequestHandler(nullptr); } void FrequencyPlanningHandler::setCnrHandler(CnrHandler *cnrHandler) { Q_ASSERT(cnrHandler); Q_ASSERT(!m_cnrHandler); m_cnrHandler = cnrHandler; connect(m_cnrHandler, &CnrHandler::cnrConfigRemoved, this, &FrequencyPlanningHandler::onCnrConfigRemoved); } bool FrequencyPlanningHandler::init() { Q_ASSERT(m_cnrHandler); QVector frequencyPlannings; if (!m_db->select(frequencyPlannings)) { ERROR_LOG(m_logger) << "Error while selecting frequency plannings from db"; return false; } for (const Database::AvmFrequencyPlanning &planContainerDb : qAsConst(frequencyPlannings)) collectAndPublish(planContainerDb); auto cbAfp = [this] (const Database::AvmActiveFrequencyPlanning ¤tDbData) { int exerciseId = currentDbData.exerciseId.isNull() ? 0 : currentDbData.exerciseId.value(); m_activePlannings.insert(exerciseId, currentDbData); if (!m_dts->publish(ActiveFrequencyPlanning(exerciseId, currentDbData.frequencyPlanningId))) ERROR_LOG(m_logger) << "Error while publishing ActiveFrequencyPlanning with ID " << exerciseId; }; if (!m_db->select(cbAfp)) { ERROR_LOG(m_logger) << "Error while selecting AvmActiveFrequencyPlannings from db"; return false; } return true; } void FrequencyPlanningHandler::onModifyFrequencyPlanningRequest(const rti::core::SampleIdentity &cookie, const ModifyFrequencyPlanningRequest &request) { auto ret = request.id() > 0 ? handleModify(request) : handleCreate(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_fts->modifyFrequencyPlanningReply(cookie, ret); } void FrequencyPlanningHandler::onRemoveFrequencyPlanningRequest(const rti::core::SampleIdentity &cookie, const Core::RemoveElementRequest &request) { auto ret = handleRemove(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_fts->removeFrequencyPlanningReply(cookie, ret); } void FrequencyPlanningHandler::onModifyActiveFrequencyPlanningRequest(const rti::core::SampleIdentity &cookie, const ModifyActiveFrequencyPlanningRequest &request) { auto ret = modifyActiveFrequencyPlanningRequest(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_fts->modifyActiveFrequencyPlanningReply(cookie, ret); } void FrequencyPlanningHandler::onRemoveActiveFrequencyPlanningRequest(const rti::core::SampleIdentity &cookie, const RemoveActiveFrequencyPlanningRequest &request) { auto ret = removeActiveFrequencyPlanningRequest(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_fts->removeActiveFrequencyPlanningReply(cookie, ret); } bool FrequencyPlanningHandler::hasActivePlan(int32_t exerciseId) const { return m_activePlannings.contains(exerciseId); } bool FrequencyPlanningHandler::isReferencedByActivePlan(int32_t exerciseId, int32_t cnrConfigId) const { auto found = m_activePlannings.constFind(exerciseId); if (found == m_activePlannings.constEnd()) return false; Q_ASSERT(m_cache.contains(found->frequencyPlanningId)); const FrequencyPlanning &activePlan = m_cache.value(found->frequencyPlanningId); for (const FrequencyPlanningData &entry : activePlan.data()) { if (entry.cnrConfigId() == cnrConfigId) return true; } return false; } Core::FunctionReplyObject FrequencyPlanningHandler::handleCreate(const ModifyFrequencyPlanningRequest &request) { Q_ASSERT(request.id() <= 0); Core::FunctionReplyObject reply = validateRequest(request); if (reply.error()) return reply; Database::AvmFrequencyPlanning dbPlanning = toDb(request); DBTransaction trans(m_db->database()); if (!m_db->insert(dbPlanning)) return reply.setStr(tr_lts("dds", "Error inserting new FrequencyPlanning data")); reply.setId(dbPlanning.id); bool status = false; std::vector dbEntries; for(auto data = request.data().begin(); data != request.data().end(); data++) { Database::AvmFrequencyPlanningData dbEntry = toDb((*data), dbPlanning.id); status = m_db->insert(dbEntry); if (!status) { WARNING_LOG(m_logger) << "Failed to insert new FrequencyPlanningData into DB"; break; } dbEntries.push_back(dbEntry); } trans.commit(); addPlan(toDds(dbPlanning, dbEntries)); if(request.exerciseId() == 0) //set global as active { ModifyActiveFrequencyPlanningRequest modRequest(request.exerciseId() , dbPlanning.id); modifyActiveFrequencyPlanningRequest(modRequest); } return Core::FunctionReplyObject(dbPlanning.id); } Core::FunctionReplyObject FrequencyPlanningHandler::handleModify(const ModifyFrequencyPlanningRequest &request) { Q_ASSERT(request.id() > 0); Core::FunctionReplyObject reply = validateRequest(request); if (reply.error()) return reply; Database::AvmFrequencyPlanning dbPlanning = toDb(request); DBTransaction trans(m_db->database()); if (!m_db->removeAvmFrequencyPlanningDataByFrequencyPlanningId(request.id()) || !m_db->update(dbPlanning)) return reply.setStr(tr_lts("dds", "Error updating FrequencyPlanning data with id %1")).arg(request.id()); bool status = false; std::vector dbEntries; for(auto data = request.data().begin(); data != request.data().end(); data++) { Database::AvmFrequencyPlanningData dbEntry = toDb((*data), dbPlanning.id); status = m_db->insert(dbEntry); if (!status) { WARNING_LOG(m_logger) << "Failed to insert new FrequencyPlanningData into DB"; break; } dbEntries.push_back(dbEntry); } trans.commit(); updatePlan(toDds(dbPlanning, dbEntries)); return Core::FunctionReplyObject(dbPlanning.id); } Core::FunctionReplyObject FrequencyPlanningHandler::handleRemove(const Core::RemoveElementRequest &request) { Core::FunctionReplyObject reply = validateRequest(request); if (reply.error()) return reply; DBTransaction trans(m_db->database()); // first remove the data // removeAvmFrequencyPlanningDataByFrequencyPlanningId not really needed to due FK if (!m_db->removeAvmFrequencyPlanningDataByFrequencyPlanningId(request.id()) || !m_db->removeAvmFrequencyPlanning(request.id())) return reply.setStr(tr_lts("dds", "Error removing FrequencyPlanningRequest data with id %1 from database")).arg(request.id()); trans.commit(); removePlan(request.id()); return reply; } Core::FunctionReplyObject FrequencyPlanningHandler::validateRequest(const ModifyFrequencyPlanningRequest &request) const { Core::FunctionReplyObject reply; if (request.id() > 0) { reply = validateModifyRequest(request); if (reply.error()) return reply; } else { // create } // Validate that the FrequencySet-entries actually belong to the referenced FrequencySet. for (const FrequencyPlanningData &entry : request.data()) { QSet frequencySetEntries = AudioVideoManagement::frequencySetDataIds(entry.properties()); for(const int &setEntry : qAsConst(frequencySetEntries)) { if (setEntry != 0 && !m_frequencySetHandler->isValid(request.frequencySetId(), setEntry)) return reply.setStr(tr_lts("dds", "FrequencySetData %1 does not belong to FrequencySet %2")).arg(setEntry).arg(request.frequencySetId()); } } return reply; } /** * Validate the parts of the request which are specific to a modify-request (i.e. not create-request). */ Core::FunctionReplyObject FrequencyPlanningHandler::validateModifyRequest(const ModifyFrequencyPlanningRequest &request) const { Q_ASSERT(request.id() > 0); Core::FunctionReplyObject reply(request.id()); auto found = m_cache.constFind(request.id()); if (found == m_cache.constEnd()) return reply.setStr(tr_lts("dds", "FrequencyPlan %1 does not exist")).arg(request.id()); const FrequencyPlanning &oldPlan = *found; if ((oldPlan.exerciseId().is_set() && oldPlan.exerciseId().get() != request.exerciseId()) || (!oldPlan.exerciseId().is_set() && request.exerciseId() != 0)) return reply.setStr(tr_lts("dds", "Exercise may not be changed")); if (request.frequencySetId() != oldPlan.frequencySetId()) return reply.setStr(tr_lts("dds", "FrequencySet may not be changed")); // In case the plan is already active (in an active Exercise), we need to check for conflicts with active FrequencyPlans of other active Exercises. if (!m_activeExercises->isActive(ExerciseContext(request.exerciseId()))) return reply; auto foundActivePlan = m_activePlannings.constFind(request.exerciseId()); if (foundActivePlan == m_activePlannings.constEnd()) return reply; bool currentlyActive = foundActivePlan->frequencyPlanningId == request.id(); if (!currentlyActive) return reply; const std::vector activePlans = otherActivePlans(request.exerciseId()); return testActivation(request.data(), activePlans, request.id()); } Core::FunctionReplyObject FrequencyPlanningHandler::validateRequest(const Core::RemoveElementRequest &request) const { Q_ASSERT(request.id() > 0); Core::FunctionReplyObject reply(request.id()); auto foundPlan = m_cache.constFind(request.id()); if (foundPlan == m_cache.constEnd()) return reply.setStr(tr_lts("dds", "FrequencyPlanning %1 does not exist")).arg(request.id()); const FrequencyPlanning &plan = *foundPlan; int exerciseId = plan.exerciseId().is_set() ? plan.exerciseId().get() : 0; auto foundActivePlan = m_activePlannings.constFind(exerciseId); bool isActive = foundActivePlan != m_activePlannings.constEnd() && foundActivePlan->frequencyPlanningId == plan.id(); if (isActive) return reply.setStr(tr_lts("dds", "FrequencyPlanning %1 cannot be removed because it is active")).arg(request.id()); return reply; } std::vector FrequencyPlanningHandler::insert(const std::vector &ddsEntries, qint64 planId, bool &status) { status = false; std::vector dbEntries; dbEntries.reserve(ddsEntries.size()); for (const FrequencyPlanningData ¤tEntry : ddsEntries) dbEntries.push_back(toDb(currentEntry, planId)); status = m_db->insert(dbEntries); if (!status) return {}; return dbEntries; } Core::FunctionReplyObject FrequencyPlanningHandler::modifyActiveFrequencyPlanningRequest(const ModifyActiveFrequencyPlanningRequest &request) { DEBUG_LOG(m_logger) << "ModifyActiveFrequencyPlanningRequest: " << request; Core::FunctionReplyObject reply = validateRequest(request); if (reply.error()) return reply; Database::AvmActiveFrequencyPlanning dbData; dbData.frequencyPlanningId = request.frequencyPlanningId(); if(request.exerciseId() != 0) dbData.exerciseId = request.exerciseId(); if (!m_activePlannings.contains(request.exerciseId())) // create { if (!m_db->insert(dbData)) return reply.setStr(tr_lts("dds", "Error inserting ActiveFrequencyPlanning data with id %1 into DB")).arg(request.exerciseId()); } else // modify { dbData.id = m_activePlannings.value(request.exerciseId()).id; if (!m_db->update(dbData)) return reply.setStr(tr_lts("dds", "Error updating ActiveFrequencyPlanning data with id %1 in DB")).arg(request.exerciseId()); } int exerciseId = dbData.exerciseId.isNull() ? 0 : dbData.exerciseId.value(); m_activePlannings[exerciseId] = dbData; if (!m_dts->publish(toDds(dbData))) return reply.setStr(tr_lts("dds", "Error publishing ActiveFrequencyPlanning data with id %1 to DDS")).arg(request.exerciseId()); emit planningChanged(); return reply; } Core::FunctionReplyObject FrequencyPlanningHandler::removeActiveFrequencyPlanningRequest(const RemoveActiveFrequencyPlanningRequest &request) { DEBUG_LOG(m_logger) << "RemoveActiveFrequencyPlanningRequest: " << request; Core::FunctionReplyObject reply(request.exerciseId()); if (!m_activePlannings.contains(request.exerciseId())) return reply.setStr(tr_lts("dds", "No ActiveFrequencyPlanning for exercise %1")).arg(request.exerciseId()); Database::AvmActiveFrequencyPlanning activePlanning = m_activePlannings.value(request.exerciseId()); if (activePlanning.exerciseId != request.exerciseId()) return reply.setStr(tr_lts("dds", "Error removing ActiveFrequencyPlanning data with exercise id %1 - not found")).arg(request.exerciseId()); if (!m_dts->removeActiveFrequencyPlanningByKey(request.exerciseId())) return reply.setStr(tr_lts("dds", "Error removing ActiveFrequencyPlanning data with exercise id %1 from DDS")).arg(request.exerciseId()); if (!m_db->removeAvmActiveFrequencyPlanning(activePlanning.id)) return reply.setStr(tr_lts("dds", "Error removing ActiveFrequencyPlanning data with exercise id %1 from DB")).arg(request.exerciseId()); m_activePlannings.remove(request.exerciseId()); emit planningChanged(); return reply; } /** * Check that the FrequencyPlanning to be activated does not conflict with active FrequencyPlannings of other Exercises. */ Core::FunctionReplyObject FrequencyPlanningHandler::validateRequest(const ModifyActiveFrequencyPlanningRequest &request) const { Core::FunctionReplyObject reply(request.exerciseId()); auto foundPlan = m_cache.constFind(request.frequencyPlanningId()); if (foundPlan == m_cache.constEnd()) return reply.setStr(tr_lts("dds", "FrequencyPlanning %1 does not exist")).arg(request.frequencyPlanningId()); if (!m_activeExercises->isActive(ExerciseContext(request.exerciseId()))) return reply; const FrequencyPlanning &planToActivate = *foundPlan; const std::vector activePlans = otherActivePlans(request.exerciseId()); if (activePlans.empty()) return reply; if (!hasSameFrequencySet(planToActivate, *m_cache.constFind(activePlans.front()))) return reply.setStr(tr_lts("dds", "FrequencyPlanning %1 cannot be activated because another FrequencyPlanning with a different FrequencySet is active already")).arg(request.frequencyPlanningId()); return testActivation(planToActivate.data(), activePlans, request.exerciseId()); } Core::FunctionReplyObject FrequencyPlanningHandler::testActivation(const std::vector &entries, const std::vector &activePlans, int requestId) const { Core::FunctionReplyObject reply(requestId); if (activePlans.empty()) return reply; // Check that the FrequencyPlanning does not use a global CnrConfig which is used by some other active FrequencyPlanning. const std::vector takenCnrConfigs = takenGlobalCnrConfigs(activePlans); for (const FrequencyPlanningData &entry : entries) { const CnrConfig &cnrConfig = m_cnrHandler->config(entry.cnrConfigId()); bool isGlobal = !cnrConfig.exerciseId().is_set(); if (!isGlobal) continue; auto found = std::find(takenCnrConfigs.begin(), takenCnrConfigs.end(), cnrConfig.id()); if (found != takenCnrConfigs.end()) return reply.setStr(tr_lts("dds", "FrequencyPlanning cannot be activated because CnrConfig \"%1\" is used by some other active FrequencyPlanning")).arg(cnrConfig.radioCircuit()); } // Check that the FrequencyPlanning does not reference an entry from the FrequencySet wich is already referenced by some other active FrequencyPlanning. QSet takenSetEntries = takenFrequencySetEntries(activePlans); for (const FrequencyPlanningData &fpdEntry : entries) { QSet frequencySetEntries = AudioVideoManagement::frequencySetDataIds(fpdEntry.properties()); for(const int &setEntry : qAsConst(frequencySetEntries)) { if(takenSetEntries.contains(setEntry)) { const CnrConfig &cnrConfig = m_cnrHandler->config(fpdEntry.cnrConfigId()); return reply.setStr(tr_lts("dds", "FrequencyPlanning cannot be activated because CnrConfig \"%1\" uses an entry in the FrequencySet which is used by some other active FrequencyPlanning")).arg(cnrConfig.radioCircuit()); } } } return reply; } /** * Return all active Exercises except the given Exercise. */ std::vector FrequencyPlanningHandler::otherActiveExercises(int withoutThisExercise) const { Q_ASSERT(m_activeExercises->isActive(ExerciseContext(withoutThisExercise))); const auto &allContexts = m_activeExercises->allContexts(); std::vector result; for (const ExerciseContext &context : allContexts) if (context.id() != withoutThisExercise) result.push_back(context.id()); return result; } /** * Return the active FrequencyPlannings of other active Exercises. */ std::vector FrequencyPlanningHandler::otherActivePlans(int withoutThisExercise) const { auto otherExercises = otherActiveExercises(withoutThisExercise); std::vector result; for (auto exerciseId : otherExercises) { auto found = m_activePlannings.find(exerciseId); if (found == m_activePlannings.end()) continue; result.push_back(found->frequencyPlanningId); } return result; } /** * Return the global CnrConfigs which are referenced by the given plans. */ std::vector FrequencyPlanningHandler::takenGlobalCnrConfigs(const std::vector &plans) const { std::vector result; for (auto planId : plans) { const FrequencyPlanning &plan = m_cache.value(planId); Q_ASSERT(plan.id() == planId); for (const FrequencyPlanningData &entry : plan.data()) { const CnrConfig &cnrConfig = m_cnrHandler->config(entry.cnrConfigId()); bool isGlobal = !cnrConfig.exerciseId().is_set(); if (isGlobal) result.push_back(cnrConfig.id()); } } return result; } /** * Return the entries of the FrequencySet which are referenced by the given plans. */ QSet FrequencyPlanningHandler::takenFrequencySetEntries(const std::vector &plans) const { QSet result; for (auto planId : plans) { const FrequencyPlanning &plan = m_cache.value(planId); Q_ASSERT(plan.id() == planId); for (const FrequencyPlanningData &entry : plan.data()) { result += AudioVideoManagement::frequencySetDataIds(entry.properties()); } } return result; } bool FrequencyPlanningHandler::hasSameFrequencySet(const FrequencyPlanning &plan1, const FrequencyPlanning &plan2) const { return plan1.frequencySetId() == plan2.frequencySetId(); } bool FrequencyPlanningHandler::collectAndPublish(const Database::AvmFrequencyPlanning &planDb, bool isReload) { Q_ASSERT(planDb.id > 0); if (isReload) Q_ASSERT(m_cache.contains(planDb.id)); else Q_ASSERT(!m_cache.contains(planDb.id)); QVector dbEntries; bool status = m_db->selectByFrequencyPlanningId(dbEntries, planDb.id); if (!status) { ERROR_LOG(m_logger) << "Error selecting AvmFrequencyPlanningData from db"; return false; } // Todo: Not nice.... FrequencyPlanning ddsPlan = toDds(planDb, std::vector(dbEntries.begin(), dbEntries.end())); if (isReload) updatePlan(ddsPlan); else addPlan(ddsPlan); return true; } void FrequencyPlanningHandler::addPlan(const FrequencyPlanning &plan) { Q_ASSERT(plan.id() > 0); Q_ASSERT(!m_cache.contains(plan.id())); m_cache.insert(plan.id(), plan); bool status = m_dts->publish(plan); if (!status) ERROR_LOG(m_logger) << QStringLiteral("Failed to publish 'FrequencyPlanning' data with id %1 (create)").arg(plan.id()); emit planningChanged(); } void FrequencyPlanningHandler::updatePlan(const FrequencyPlanning &plan) { Q_ASSERT(plan.id() > 0); auto found = m_cache.find(plan.id()); Q_ASSERT(found != m_cache.end()); *found = plan; bool status = m_dts->publish(plan); if (!status) ERROR_LOG(m_logger) << QStringLiteral("Failed to publish 'FrequencyPlanning' data with id %1 (modify)").arg(plan.id()); emit planningChanged(); } void FrequencyPlanningHandler::removePlan(int32_t planId) { auto found = m_cache.find(planId); Q_ASSERT(found != m_cache.end()); m_cache.erase(found); bool status = m_dts->removeFrequencyPlanningByKey(planId); if (!status) ERROR_LOG(m_logger) << QStringLiteral("Failed to publish 'FrequencyPlanning' with id %1 (remove)").arg(planId); emit planningChanged(); } /** * Will be called whenever a row from avm_frequency_planning_data is removed. * The remove-operation itself can be a result of the following: * 1. we ourselves are about to remove a row as a result of a RemoveRadioCircuitsRequest * 2. one of our foreign key dependencies was removed (exercise or operator) and our row * was removed as a result of a cascading delete * 3. someone removed the row by hand. * Besides these, this method can also be called as a result of a ModifyFrequencyPlanningRequest. * This is because the modify is implemented via deletion of all previous entries * and insertion of all new entries. * * @param planId The id of the row in avm_frequency_planning where the deleted row * belongs to, not the primary key of the deleted row itself. */ void FrequencyPlanningHandler::onDbDeletedFrequencyPlanningData(const QVariant &planId) { bool couldConvert = false; qint64 dbId = planId.toLongLong(&couldConvert); Q_ASSERT(couldConvert); if (!m_cache.contains(dbId)) return; // We don't have the id in our cache. We probably just removed the Row ourselves. => nothing to do. // We still have the id in our cache. Someone else must have deleted this row. // We need to reload the whole plan and publish the modification. // In case the plan is now empty, we need to delete the plan and publish the removal. Database::AvmFrequencyPlanning dbPlan = toDb(m_cache.value(dbId)); const bool isReload = true; collectAndPublish(dbPlan, isReload); } void FrequencyPlanningHandler::onCnrConfigRemoved(int cnrConfigId) { for (FrequencyPlanning &fp : m_cache) { ModifyFrequencyPlanningRequest req; req.data() = fp.data(); auto found = std::find_if(req.data().begin(), req.data().end(), [cnrConfigId](const FrequencyPlanningData &data) { return data.cnrConfigId() == cnrConfigId; }); if (found == req.data().end()) continue; // In this Frequency Planning, the (removed) cnrConfig ID is present. Therefore, we need to delete it also: req.data().erase(found); req.date() = fp.date(); req.exerciseId() = fp.exerciseId().is_set() ? fp.exerciseId().get() : 0; req.frequencySetId() = fp.frequencySetId(); req.id() = fp.id(); req.name() = fp.name(); handleModify(req); } } } // namespace AudioVideoManagement