Files
qt5-legatus-legacy/Lts/Services/AudioVideoManagement/frequency_planning_handler.cpp
2026-08-13 18:33:10 +02:00

786 lines
27 KiB
C++
Executable File

/**
* 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 <Proxies/CombatNetRadio/Helper/cnr_frequency_planning_helper.h>
#include "frequency_set_handler.h"
#include "cnr_handler.h"
#include "active_exercises.h"
#include "exercise_context.h"
#include <avm_dts.h>
#include <ngldb/dbtransaction.h>
#include <algorithm>
#include <vector>
#include <QSet>
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<Database::AvmFrequencyPlanningData> &dbEntries)
{
FrequencyPlanning ddsPlan;
ddsPlan.id(dbPlan.id);
ddsPlan.date(dbPlan.dateOfCreation);
if (!dbPlan.exerciseId.isNull())
ddsPlan.exerciseId(dbPlan.exerciseId.value<int>());
ddsPlan.frequencySetId(dbPlan.frequencySetId);
ddsPlan.name(dbPlan.name.toStdString());
std::vector<FrequencyPlanningData> 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<int>();
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<Database::AvmFrequencyPlanning> 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 &currentDbData)
{
int exerciseId = currentDbData.exerciseId.isNull() ? 0 : currentDbData.exerciseId.value<int>();
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<Database::AvmFrequencyPlanningData> 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<Database::AvmFrequencyPlanningData> 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<int> 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<int> 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<Database::AvmFrequencyPlanningData> FrequencyPlanningHandler::insert(const std::vector<FrequencyPlanningData> &ddsEntries, qint64 planId, bool &status)
{
status = false;
std::vector<Database::AvmFrequencyPlanningData> dbEntries;
dbEntries.reserve(ddsEntries.size());
for (const FrequencyPlanningData &currentEntry : 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<int>();
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<int> 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<FrequencyPlanningData> &entries, const std::vector<int> &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<int> 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<int> takenSetEntries = takenFrequencySetEntries(activePlans);
for (const FrequencyPlanningData &fpdEntry : entries)
{
QSet<int> 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<int> FrequencyPlanningHandler::otherActiveExercises(int withoutThisExercise) const
{
Q_ASSERT(m_activeExercises->isActive(ExerciseContext(withoutThisExercise)));
const auto &allContexts = m_activeExercises->allContexts();
std::vector<int> 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<int> FrequencyPlanningHandler::otherActivePlans(int withoutThisExercise) const
{
auto otherExercises = otherActiveExercises(withoutThisExercise);
std::vector<int> 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<int> FrequencyPlanningHandler::takenGlobalCnrConfigs(const std::vector<int> &plans) const
{
std::vector<int> 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<int> FrequencyPlanningHandler::takenFrequencySetEntries(const std::vector<int> &plans) const
{
QSet<int> 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<Database::AvmFrequencyPlanningData> 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<Database::AvmFrequencyPlanningData>(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