/** * 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