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

296 lines
8.4 KiB
C++
Executable File

/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#include "handler_menu_struct.h"
#include "main_handler.h"
#include "dds_handler.h"
#include "object_menu_struct.h"
#include "audio_video_handler_impl.h"
#include <AudioVideoManagement/avm_dts.h>
#include <QVector>
#include <tuple>
namespace
{
AudioVideoManagement::MenuStruct toDdsToplevel(const AudioVideoManagement::Database::AvmMenuStruct &dbEntry)
{
Q_ASSERT(dbEntry.parentId == 0);
Q_ASSERT(dbEntry.isGroup);
Q_ASSERT(dbEntry.name.isValid());
AudioVideoManagement::MenuStruct ms;
ms.id(dbEntry.id);
ms.name(dbEntry.name.toString().toStdString());
ms.isAudio(dbEntry.isAudio);
return ms;
}
void insertChildren(std::vector<AudioVideoManagement::AvMenuEntry>& contToAdd,
int parentId,
const QVector<AudioVideoManagement::Database::AvmMenuStruct> &vecMenuStructs)
{
using namespace AudioVideoManagement;
for (const Database::AvmMenuStruct &ms : vecMenuStructs)
if (ms.parentId == parentId)
contToAdd.push_back(HandlerMenuStruct::toDds(ms));
}
bool lessToplevel(const AudioVideoManagement::AvMenuEntry &a,
const AudioVideoManagement::AvMenuEntry &b)
{
return a.id() < b.id();
}
bool lessSublevel(const AudioVideoManagement::AvMenuEntry &a,
const AudioVideoManagement::AvMenuEntry &b)
{
return std::make_tuple(a.parentId(), a.rowId(), a.id()) < std::make_tuple(b.parentId(), b.rowId(), b.id());
}
} // anonymous namespace
namespace AudioVideoManagement
{
AvMenuEntry HandlerMenuStruct::toDds(const Database::AvmMenuStruct &dbEntry)
{
Q_ASSERT(dbEntry.parentId > 0);
AvMenuEntry ddsItem;
ddsItem.id(dbEntry.id);
ddsItem.parentId(dbEntry.parentId);
ddsItem.isGroup(dbEntry.isGroup);
ddsItem.rowId(dbEntry.rowid);
if (dbEntry.isGroup)
{
Q_ASSERT(dbEntry.name.isValid());
ddsItem.name().set(dbEntry.name.toString().toStdString());
}
else
{
// If it's not a group it represents a stream for a given MountignPoint.
// For the name we then use the internal stream name for that MountingPoint.
// That name is not saved in the DB.
// Using the internal stream name here makes it easy for clients to directly
// use those when communicating with AVS.
Q_ASSERT(dbEntry.mountingPointId.isValid());
ddsItem.mountingPointId(dbEntry.mountingPointId.toInt());
ddsItem.name().set(AudioVideoHandlerImpl::internalStreamName(dbEntry.mountingPointId.toInt()).toStdString());
}
return ddsItem;
}
void HandlerMenuStruct::sortEntries(MenuStruct &menu)
{
auto isToplevel = [&menu] (const AvMenuEntry& entry) { return entry.parentId() == menu.id(); };
auto sublevelStart = std::partition(menu.menuEntries().begin(), menu.menuEntries().end(), isToplevel);
std::sort(menu.menuEntries().begin(), sublevelStart, lessToplevel);
std::sort(sublevelStart, menu.menuEntries().end(), lessSublevel);
}
HandlerMenuStruct::HandlerMenuStruct(Interface::Dts *dts, Interface::Fts *fts, DatabaseInterface *db)
: Interface::Fts::ModifyMenuStructRequestHandler(nullptr)
, Interface::Fts::RemoveMenuStructRequestHandler(nullptr)
, m_dts(dts)
, m_fts(fts)
, m_db(db)
, m_logger(getLogger("HandlerMenuStruct"))
{
Q_ASSERT(dts);
Q_ASSERT(fts);
Q_ASSERT(db);
m_fts->setModifyMenuStructRequestHandler(this);
m_fts->setRemoveMenuStructRequestHandler(this);
connect(m_db, &DatabaseInterface::notifyDeleteAvmMenuStruct, this, &HandlerMenuStruct::onDbDeletedAvmMenuStruct);
}
HandlerMenuStruct::~HandlerMenuStruct()
{
m_fts->setModifyMenuStructRequestHandler(nullptr);
m_fts->setRemoveMenuStructRequestHandler(nullptr);
qDeleteAll(m_objMap);
}
bool HandlerMenuStruct::init()
{
return initObjects();
}
bool HandlerMenuStruct::initObjects()
{
bool status = false;
const QList<MenuStruct> dbItems = loadFromDb(status);
if (!status)
return false;
for (const auto &ms : dbItems)
{
ObjectMenuStruct *objMs = new ObjectMenuStruct(m_dts, m_db);
objMs->createFromDb(ms);
m_objMap.insert(objMs->getId(), objMs);
}
return true;
}
/**
* Will be called whenever a row from avm.menu_struct 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 RemoveRequest
* 2. one of our foreign key dependencies was removed (mounting_point) and our row
* was removed as a result of a cascading delete
* 3. someone removed the row by hand.
*/
void HandlerMenuStruct::reload()
{
bool status = false;
const QList<MenuStruct> dbItems = loadFromDb(status);
if (!status)
return;
for (const auto &ms : dbItems)
{
auto found = m_objMap.find(ms.id());
Q_ASSERT(found != m_objMap.end());
(*found)->updateFromDb(ms);
}
}
QList<MenuStruct> HandlerMenuStruct::loadFromDb(bool &status)
{
status = false;
QVector<Database::AvmMenuStruct> vecMenuStruct;
if (!m_db->select(vecMenuStruct))
return {};
auto isToplevel = [] (const Database::AvmMenuStruct& dbEntry) { return dbEntry.parentId == 0; };
auto sublevelStart = std::partition(vecMenuStruct.begin(), vecMenuStruct.end(), isToplevel);
QMap<qint32, MenuStruct> msMap;
for (auto dbEntry = vecMenuStruct.begin(); dbEntry != sublevelStart; ++dbEntry)
{
MenuStruct ms = toDdsToplevel(*dbEntry);
msMap.insert(ms.id(), ms);
}
// Go over all non-toplevel entries and create AvMenuEntries for those
// and connect them to their parent MenuStruct.
for (auto dbEntry = sublevelStart; dbEntry != vecMenuStruct.end(); ++dbEntry)
{
auto it = msMap.find(dbEntry->parentId);
if (it == msMap.end())
continue; // this entry is probably not a direct child of a toplevel entry.
MenuStruct &parent = it.value();
AudioVideoManagement::AvMenuEntry me = toDds(*dbEntry);
parent.menuEntries().push_back(me);
insertChildren(parent.menuEntries(), me.id(), vecMenuStruct);
}
// For each MenuStruct sort its entries.
// Also: Consistency check that for each dbEntry there was created an element.
// Or in other words that the hierarchy is consistent.
int totalCount = msMap.size();
for (auto &ms : msMap)
{
sortEntries(ms);
totalCount += ms.menuEntries().size();
}
Q_ASSERT(totalCount == vecMenuStruct.size());
status = true;
return msMap.values();
}
void HandlerMenuStruct::onModifyMenuStructRequest(const rti::core::SampleIdentity &cookie,
const MenuStruct &request)
{
DEBUG_LOG(m_logger) << "onModifyMenuStructRequest: " << request;
auto ret = request.id() > 0 ? modifyMenuStructRequest(request) : createMenuStructRequest(request);
if (ret.error())
WARNING_LOG(m_logger) << ret();
m_fts->modifyMenuStructReply(cookie, ret);
}
Core::FunctionReplyObject HandlerMenuStruct::createMenuStructRequest(const MenuStruct &request)
{
ObjectMenuStruct *ifs = new ObjectMenuStruct(m_dts, m_db);
DEBUG_LOG(m_logger) << "request.menuEntries.size() : " << request.menuEntries().size();
Core::FunctionReplyObject ret = ifs->createFromDds(request);
if (ret.error())
delete ifs;
else
m_objMap.insert(ifs->getId(), ifs);
return ret;
}
Core::FunctionReplyObject HandlerMenuStruct::modifyMenuStructRequest(const MenuStruct &request)
{
ObjectMenuStruct *ifs = m_objMap.value(request.id());
if (ifs == nullptr)
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "MenuStruct with id %1 not found")).arg(request.id());
auto ret = ifs->updateFromDds(request);
if (ret.error())
return ret;
Q_ASSERT(ifs->getId() == request.id());
return ret;
}
void HandlerMenuStruct::onRemoveMenuStructRequest(const rti::core::SampleIdentity &cookie,
const Core::RemoveElementRequest &request)
{
Core::FunctionReplyObject ret;
INFO_LOG(m_logger) << "onRemoveMenuStructRequest id = " << request.id();
ObjectMenuStruct *ifs = m_objMap.value(request.id());
if (ifs == nullptr)
{
ret = Core::FunctionReplyObject(request.id(), tr_lts("dds", "MenuStruct with id %1 not found")).arg(request.id());
}
else
{
ret = ifs->remove();
if (!ret.error())
{
m_objMap.remove(request.id());
delete ifs;
}
}
m_fts->removeMenuStructReply(cookie, ret);
}
void HandlerMenuStruct::onDbDeletedAvmMenuStruct(const QVariant &id_)
{
int id = id_.toInt();
DEBUG_LOG(m_logger) << "DB deleted AvmMenuStruct with id: " << id;
reload();
}
} // namespace AudioVideoManagement