786 lines
27 KiB
C++
Executable File
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 ¤tDbData)
|
|
{
|
|
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 ¤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<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
|