/** * @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 #include #include 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& contToAdd, int parentId, const QVector &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 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 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 HandlerMenuStruct::loadFromDb(bool &status) { status = false; QVector 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 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