/** ** ** @RDE_HEADER@ ** plan_editor_gui_plugin.cpp ** $Id$ ** $HeadURL$ ** $Author$ ** ** ----------------------------------------------------------------------------------------------- ** ** Hardware-Environment: Intel-PC ** Software-Environment: Windows 7, Qt 5.7. ArcGIS 100 ** ** Language ........: C++ 11 ** ** ----------------------------------------------------------------------------------------------- ** ** Author ...........: Jörg Turla, SPD G, Tel. 3132 ** ** ----------------------------------------------------------------------------------------------- ** ** Notes .........: ** ** 24.04.2017, JT : First edition for use with Qt5 ** ** ----------------------------------------------------------------------------------------------- */ #include "plan_overlays_gui_plugin.h" #include "plan_overlay_model.h" #include "plan_overlay_dialog.h" #include "TopicEditor/topic_editor.h" #include "flm_reply_handler.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace PlanOverlaysPlugin { std::shared_ptr PlanOverlaysGuiPlugin::createGuiInterface(LtsSession::LtsSession * session) { return std::make_shared(session); } static NGL::INGLLogger &getLogger() { try { return NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("PlanOverLaysPlugin"); } catch (const std::exception &ex) { fprintf(stderr, "Error retrieving logger: %s", ex.what()); abort(); } } class InterfaceImpl::InternalData { public: FileManagement::FileContent m_planContent; std::shared_ptr m_dtc; std::shared_ptr m_ftc; std::shared_ptr m_opmDtc; std::shared_ptr m_exmDtc; LtsUtils::RequestHelper * m_requestHelper; FileManagementReplyHandler * m_flmReplyHandler; PlanOverlayModel * m_overlayModel; std::shared_ptr m_situationMapGui; SituationMap::ForeignGuiInterface * m_foreignSitmapIfc; QString m_messageBoxTitle; InternalData() : m_requestHelper(nullptr) , m_flmReplyHandler(nullptr) , m_overlayModel(nullptr) , m_foreignSitmapIfc(nullptr) {} // InternalData ~InternalData() = default; }; InterfaceImpl::InterfaceImpl(LtsSession::LtsSession * session) : QObject() , GuiPluginBase::Interface(session, getLogger()) , m_internal(new InternalData()) , m_foreignIfc(nullptr) { m_internal->m_requestHelper = new LtsUtils::RequestHelper(this); m_internal->m_flmReplyHandler = new FileManagementReplyHandler(m_internal->m_requestHelper); m_internal->m_messageBoxTitle = tr("Plan Overlays"); } bool InterfaceImpl::fetchInterfaces() { // Initialize file mamangement if (!m_session->getDtc(m_internal->m_dtc)) { DEBUG_LOG(m_logger) << "getDtc for FileManagement::DataInterface failed."; return false; } if (!m_session->getDtc(m_internal->m_opmDtc)) { DEBUG_LOG(m_logger) << "getDtc for OperatorManagement::DataInterface failed."; return false; } if (!m_session->getDtc(m_internal->m_exmDtc)) { DEBUG_LOG(m_logger) << "getDtc for ExerciseManagement::DataInterface failed."; return false; } if (!m_session->getFtc(m_internal->m_ftc)) { DEBUG_LOG(m_logger) << "getFtc for FileManagement::DataInterface failed."; return false; } if (!m_session->getForeignGuiInterface(m_internal->m_situationMapGui)) { DEBUG_LOG(m_logger) << "getForeignGuiInterface for SituationMap::ForeignGuiInterface failed."; return false; } m_internal->m_foreignSitmapIfc = m_internal->m_situationMapGui.get(); delete m_internal->m_overlayModel; m_internal->m_overlayModel = new PlanOverlayModel(); connect(m_internal->m_overlayModel, &PlanOverlayModel::requestLoadPlan , this, &InterfaceImpl::loadPlanByUser, Qt::ConnectionType::DirectConnection); connect(m_internal->m_overlayModel, &PlanOverlayModel::requestUpdatePlan , this, &InterfaceImpl::onUpdatePlan , Qt::ConnectionType::DirectConnection); connect(m_internal->m_overlayModel, &PlanOverlayModel::requestRemovePlan , this, QOverload::of(&InterfaceImpl::removePlan) , Qt::ConnectionType::DirectConnection); connect(m_internal->m_overlayModel, &PlanOverlayModel::planMinScaleChanged , this, &InterfaceImpl::onChangePlanMinScaleOnMap , Qt::ConnectionType::DirectConnection); connect(m_internal->m_overlayModel, &PlanOverlayModel::planMaxScaleChanged , this, &InterfaceImpl::onChangePlanMaxScaleOnMap , Qt::ConnectionType::DirectConnection); connect(m_internal->m_overlayModel, &PlanOverlayModel::planOpacityChanged , this, &InterfaceImpl::onChangePlanOpacityOnMap , Qt::ConnectionType::DirectConnection); connect(m_internal->m_overlayModel, &PlanOverlayModel::planOrderChanged , this, &InterfaceImpl::onChangePlanOrderOnMap , Qt::ConnectionType::DirectConnection); connect(m_internal->m_overlayModel, &PlanOverlayModel::requestReloadPlanByName, this, &InterfaceImpl::onReloadPlanByName, static_cast(Qt::DirectConnection|Qt::UniqueConnection)); m_internal->m_ftc->enableFileContentModify(); m_internal->m_ftc->enableGetFileContentData(); connect(m_internal->m_dtc.get(), &FileManagement::Interface::Dtc::fileContentChanged, this, &InterfaceImpl::onPlanContentChanged, Qt::UniqueConnection); connect(m_internal->m_exmDtc.get(), &ExerciseManagement::Interface::Dtc::exerciseChanged, this, &InterfaceImpl::onExerciseChanged, Qt::UniqueConnection); return true; } void InterfaceImpl::setForeignSitMapGuiIfc(SituationMap::ForeignGuiInterface *foreignSitmapIfc) { //qWarning() << (void *)this << "setSitMapGuiIfc"; m_internal->m_foreignSitmapIfc = foreignSitmapIfc; } void InterfaceImpl::setForeignPlanOverlaysGuiIfc(ForeignGuiInterface *ifc) { m_foreignIfc = ifc; } void InterfaceImpl::clearInterfaces() { if (!m_internal) return; m_internal->m_dtc.reset(); m_internal->m_opmDtc.reset(); m_internal->m_exmDtc.reset(); m_internal->m_ftc.reset(); m_internal->m_situationMapGui.reset(); m_internal->m_foreignSitmapIfc = nullptr; // it's just a raw pointer } PlanOverlayModel *InterfaceImpl::planOverlayModel() { return m_internal->m_overlayModel; } bool InterfaceImpl::loadPlanByUser(int planFileId) { return loadPlan(planFileId, true, nullptr); } bool InterfaceImpl::loadPlan(int planFileId) { return loadPlan(planFileId, QJsonObject()); } bool InterfaceImpl::loadPlan(int planFileId, const QJsonObject &planItemObject) { return loadPlan(planFileId, false, nullptr, planItemObject); } bool InterfaceImpl::loadPlan(int planFileId, bool applyMinMaxScaleOfMetadata, SituationMap::ForeignGuiInterface* overrideSitMapIfc, const QJsonObject &planItemObject) { // Important: the parameter planFileId is the file id of the plan in FMS and NOT the planKeyId !!!!! if (overrideSitMapIfc == nullptr) overrideSitMapIfc = m_internal->m_foreignSitmapIfc; if (overrideSitMapIfc == nullptr) // still paranoid { ERROR_LOG(m_logger) << "no sit map ifc to load a plan on!"; return false; } // NLDEVCTRL-1414: // Pläne/Overlays wiederherstellen schlug fehl nach Aus-/Einloggen m_internal->m_dtc->waitForHistoricalDataFileContent(5000); FileManagement::FileContent fileContent = m_internal->m_dtc->fileContent(planFileId); // TODO: FIXME: but normally 0 is a valid id ?! if (fileContent.id() <= 0) // not found { ERROR_LOG(m_logger) << "Could not find plan with id " << planFileId << " in filemanagement - removing"; return false; } QString nameOfPlan = QString::fromStdString(fileContent.name()); std::function handleContent = [this, fileContent, applyMinMaxScaleOfMetadata, nameOfPlan, overrideSitMapIfc, planItemObject](const Core::FunctionReply &) { const QByteArray &planXMLContent = LtsUtils::toQByteArray(m_internal->m_flmReplyHandler->getContent()); int planFileId = fileContent.id(); // use 'planId' as 'id' (internal name: key) if (!overrideSitMapIfc->planOverlaySetContent(planFileId, nameOfPlan, planXMLContent)) { ERROR_LOG(m_logger) << "no sit map ifc to load a plan on!"; return false; } PlanOverlaysPlugin::PlanOverlayModel::PlanItem planItem = m_internal->m_overlayModel->findPlanItemByFileId(planFileId); if (!planItem.isValid()) planItem = m_internal->m_overlayModel->planItem(-1); planItem.m_folderId = fileContent.folderId(); if (fileContent.exerciseId().is_set()) { planItem.m_exerciseId = fileContent.exerciseId().get(); if (fileContent.phaseId().is_set()) planItem.m_phaseId = fileContent.phaseId().get(); } planItem.m_fileId = planFileId; planItem.m_fileName = nameOfPlan; if (applyMinMaxScaleOfMetadata) { QScopedPointer geoModel(SituationMap::GeometryModelFactory::singleton()->create()); if (geoModel->fromByteArray(planXMLContent)) { const QString &minScaleStr = geoModel->getModelAttribute(SituationMap::GeometryModel::Attribute::MetaDataMinScale).toString(); const QString &maxScaleStr = geoModel->getModelAttribute(SituationMap::GeometryModel::Attribute::MetaDataMaxScale).toString(); bool ok = false; double minScale = minScaleStr.toDouble(&ok); if (ok) planItem.m_minScale = minScale; else planItem.m_minScale = 0.0; double maxScale = maxScaleStr.toDouble(&ok); if (ok) planItem.m_maxScale = maxScale; else planItem.m_maxScale = 0.0; DEBUG_LOG(m_logger) << " load plan and set minMaxScale:" << " minScaleStr = " << minScaleStr << " maxScaleStr = " << maxScaleStr << " planItem.m_minScale = " << planItem.m_minScale << " planItem.m_maxScale = " << planItem.m_maxScale; overrideSitMapIfc->setPlanOverlayMinScale(planFileId, planItem.m_minScale); overrideSitMapIfc->setPlanOverlayMaxScale(planFileId, planItem.m_maxScale); } } if (planItemObject.isEmpty()) { // Get plan settings from SituationMap and store them as initial value in plan item // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ planItem.m_opacity = overrideSitMapIfc->planOverlayOpacity(planFileId); planItem.m_maxScale = overrideSitMapIfc->planOverlayMaxScale(planFileId); planItem.m_minScale = overrideSitMapIfc->planOverlayMinScale(planFileId); planItem.m_visibility = overrideSitMapIfc->planOverlayVisibility(planFileId); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ } else { planItem.m_opacity = planItemObject[QStringLiteral("opacity")].toDouble(0.); planItem.m_maxScale = planItemObject[QStringLiteral("maxScale")].toDouble(0.); planItem.m_minScale = planItemObject[QStringLiteral("minScale")].toDouble(0.); planItem.m_visibility = planItemObject[QStringLiteral("visibility")].toBool(true); overrideSitMapIfc->setPlanOverlayOpacity(planFileId, planItem.m_opacity); overrideSitMapIfc->setPlanOverlayMaxScale(planFileId, planItem.m_maxScale); overrideSitMapIfc->setPlanOverlayMinScale(planFileId, planItem.m_minScale); overrideSitMapIfc->setPlanOverlayVisibility(planFileId, planItem.m_visibility); } m_internal->m_overlayModel->updatePlanItem(planItem); // Write back plan item to model return true; }; bool ok = m_internal->m_requestHelper->startSingleRequest([this, planFileId]() { FileManagement::GetFileContentDataRequest req; req.fileContentId(planFileId); return m_internal->m_ftc->getFileContentDataRequest(req, m_internal->m_flmReplyHandler); }, handleContent); return ok; } bool InterfaceImpl::unloadAllPlans() { DEBUG_LOG(m_logger) << LOGFUNCTION << " START"; const QMap &planStore = m_internal->m_overlayModel->planStore(); QMapIterator mapIt(planStore); while (mapIt.hasNext()) { mapIt.next(); unsigned int planId = mapIt.key(); DEBUG_LOG(m_logger) << LOGFUNCTION << " removeOverlay, planId=" << planId; // update model, signal requestRemovePlan, slot removePlan m_internal->m_overlayModel->removeOverlay(planId); } DEBUG_LOG(m_logger) << LOGFUNCTION << " END"; return true; } void InterfaceImpl::onUpdatePlan(int planId) { PlanOverlaysPlugin::PlanOverlayModel::PlanItem planItem = m_internal->m_overlayModel->planItemValue(planId); if (!planItem.isValid()) { ERROR_LOG(m_logger) << "plan item with id " << planId << " not found in model"; return; } int planFileId = planItem.m_fileId; FileManagement::FileContent fileContent = m_internal->m_dtc->fileContent(planFileId); planItem.m_fileName = QString::fromStdString(fileContent.name()); std::function handleContent = [this,planFileId,planItem](const Core::FunctionReply &baseReply) { const FileManagement::GetFileContentDataReply &reply = static_cast(baseReply); const QByteArray &planXMLContent = LtsUtils::toQByteArray(reply.content()); // use 'planId' as 'id' (internal name: key) if (!m_internal->m_foreignSitmapIfc->planOverlaySetContent(planFileId, planItem.m_fileName, planXMLContent)) { ERROR_LOG(m_logger) << LOGFUNCTION << "Error rendering planContent [" << planItem.m_fileName << "]"; return false; } m_internal->m_overlayModel->renewPlanItem(planItem); return true; }; // std::function bool ok = m_internal->m_requestHelper->startSingleRequest([this, planFileId]() { FileManagement::GetFileContentDataRequest req; req.fileContentId(planFileId); return m_internal->m_ftc->getFileContentDataRequest(req, m_internal->m_flmReplyHandler); }, handleContent); if (!ok) { ERROR_LOG(m_logger) << LOGFUNCTION << "Error reading planContent [" << planItem.m_fileName << "]"; LtsWidgets::LtsMessageDialog::showMessage(m_session->baseLayout(), tr("Reading plan '%1' from database failed").arg(planItem.m_fileName), m_internal->m_messageBoxTitle); } } InterfaceImpl::~InterfaceImpl() { delete m_internal->m_requestHelper; delete m_internal->m_flmReplyHandler; delete m_internal->m_overlayModel; delete m_internal; m_internal = nullptr; } QString InterfaceImpl::menuGroup() const { return tr("Situation Map"); } QVector InterfaceImpl::getActions() { QVector actionVector; { LtsWidgets::PluginData pluginAction; pluginAction.menuGroup = menuGroup(); pluginAction.menuText = tr("Managing Plan Overlays"); pluginAction.menuIcon = QIcon(QStringLiteral("icon:overlay_plans.svg")); pluginAction.identifier = QStringLiteral("manage_plan_overlay"); pluginAction.tooltip = tr("Loading plan overlays, changing order of plan overlays and modifying overlay settings"); pluginAction.operatorRight = OperatorRights::PlanEditor::TacticalPlansShow; pluginAction.exercisePart = LtsWidgets::ExercisePart::eSpecial; pluginAction.directFunc = [this](const LtsWidgets::PluginData &) { if (m_planOverlayDialog) { m_planOverlayDialog->show(); m_planOverlayDialog->raise(); } else { m_planOverlayDialog = openPlanOverlayDialog(); } }; actionVector << pluginAction; } return actionVector; } QWidget *InterfaceImpl::openPlanOverlayDialog(const QJsonArray &planItems, bool removePlansOnClose) { auto doReadFile = [this](int selectedFileId) // note: parameter is fileId@FileManagement { if (selectedFileId > 0) loadPlanByUser(selectedFileId); }; auto doUnloadPlan = [this](int planId) // note: planId != fileId@FileManagement { if (planId > 0) m_internal->m_overlayModel->removeOverlay(planId); }; auto doReloadPlan = [this](int planId) // note: planId != fileId@FileManagement { if (planId > 0) onReloadPlanById(planId); }; for (const QJsonValue &planItemValue : qAsConst(planItems)) { if (!planItemValue.isObject()) { WARNING_LOG(m_logger) << "planItem from json array is not an object! " << planItemValue.type(); continue; } QJsonObject planItemObj(planItemValue.toObject()); int planFileId = planItemObj[QStringLiteral("fileId")].toInt(-1); if (!loadPlan(planFileId, planItemObj)) ERROR_LOG(m_logger) << "could not load plan with file id " << planFileId; } #define USE_QDIALOG 1 #ifdef USE_QDIALOG PlanOverlayDialog * overlayDialog = new PlanOverlayDialog(m_internal->m_foreignSitmapIfc, m_internal->m_overlayModel, m_session->baseLayout(), m_session); #else PlanOverlayDialog * overlayDialog = new PlanOverlayDialog(m_session, sitMapIfc, m_internal->m_overlayModel, m_session->baseLayout()); #endif overlayDialog->dialogWidget()->initFileManagement(m_internal->m_dtc, m_internal->m_opmDtc, m_internal->m_exmDtc, doReadFile, doReloadPlan, doUnloadPlan, appId() == LtsAppDescriptor::App::OC ); overlayDialog->show(); if (removePlansOnClose) { connect(overlayDialog->dialogWidget(), &QObject::destroyed, this, [this]() { auto planStore = m_internal->m_overlayModel->planStore(); for (auto i = planStore.constBegin(); i != planStore.constEnd(); ++i) { //m_internal->m_overlayModel->removeOverlay(i.key()); removePlan(i.key()); } }); } connect(overlayDialog->dialogWidget(), &QObject::destroyed, this, [overlayDialog]() { delete overlayDialog; }); /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * The overlay dialog will be removed automatically when the user close the dialog or when underlying map will be destroyed. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ return overlayDialog->dialogWidget(); } void InterfaceImpl::onReloadPlanById(int planId) // note: planId != fileId@FileManagement { const PlanOverlaysPlugin::PlanOverlayModel::PlanItem & planItem = m_internal->m_overlayModel->planItem(planId); if (planItem.isValid()) { int fileId = planItem.m_fileId; FileManagement::FileContent fileContent = m_internal->m_dtc->fileContent(fileId); QString nameOfPlan = QString::fromStdString(fileContent.name()); std::function handleContent = [this,planId,nameOfPlan](const Core::FunctionReply &baseReply) { const FileManagement::GetFileContentDataReply &reply = static_cast(baseReply); const QByteArray &planXMLContent = LtsUtils::toQByteArray(reply.content()); if (planXMLContent.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << "Error plan [" << nameOfPlan << "] is empty"; return false; } PlanOverlaysPlugin::PlanOverlayModel::PlanItem planItem = m_internal->m_overlayModel->planItem(planId); if (!m_internal->m_foreignSitmapIfc->planOverlaySetContent(planItem.m_fileId, planItem.m_fileName, planXMLContent)) { ERROR_LOG(m_logger) << LOGFUNCTION << "Error rendering planContent [" << nameOfPlan << "]"; return false; } planItem.m_fileName = nameOfPlan; planItem.m_opacity = m_internal->m_foreignSitmapIfc->planOverlayOpacity(planItem.m_fileId); planItem.m_maxScale = m_internal->m_foreignSitmapIfc->planOverlayMaxScale(planItem.m_fileId); planItem.m_minScale = m_internal->m_foreignSitmapIfc->planOverlayMinScale(planItem.m_fileId); planItem.m_visibility = m_internal->m_foreignSitmapIfc->planOverlayVisibility(planItem.m_fileId); m_internal->m_overlayModel->updatePlanItem(planItem); return true; }; bool ok = m_internal->m_requestHelper->startSingleRequest([this,fileId]() -> bool { FileManagement::GetFileContentDataRequest req; req.fileContentId(fileId); return m_internal->m_ftc->getFileContentDataRequest(req, m_internal->m_flmReplyHandler); }, handleContent); if (!ok) { ERROR_LOG(m_logger) << LOGFUNCTION << "Error reading planContent [" << nameOfPlan << "]"; const QString & msg = tr("Reading plan '%1' from database failed").arg(nameOfPlan); LtsWidgets::LtsMessageDialog::showMessage(m_session->baseLayout(), msg, m_internal->m_messageBoxTitle); return; } } } bool InterfaceImpl::removePlan(int planId) { return removePlan(planId, nullptr); } bool InterfaceImpl::removePlan(int planId, SituationMap::ForeignGuiInterface* overrideSitMapIfc) { if (overrideSitMapIfc == nullptr) overrideSitMapIfc = m_internal->m_foreignSitmapIfc; if (overrideSitMapIfc == nullptr) // still paranoid { ERROR_LOG(m_logger) << "no sit map ifc to remove a plan from for planId " << planId; return false; } const PlanOverlaysPlugin::PlanOverlayModel::PlanItem & planItem = m_internal->m_overlayModel->planItem(planId); if (!planItem.isValid()) { ERROR_LOG(m_logger) << "plan item invalid for planId " << planId; return false; } if (!overrideSitMapIfc->planOverlayRemove(planItem.m_fileId)) { // HACK: try removing the playn overlay by planId, finally if (!overrideSitMapIfc->planOverlayRemove(planId)) return false; } return true; } void InterfaceImpl::onChangePlanMinScaleOnMap(int planId, double minScale) { const PlanOverlaysPlugin::PlanOverlayModel::PlanItem & planItem = m_internal->m_overlayModel->planItem(planId); if (planItem.isValid()) m_internal->m_foreignSitmapIfc->setPlanOverlayMinScale(planItem.m_fileId, minScale); } void InterfaceImpl::onChangePlanMaxScaleOnMap(int planId, double maxScale) { const PlanOverlaysPlugin::PlanOverlayModel::PlanItem & planItem = m_internal->m_overlayModel->planItem(planId); if (planItem.isValid()) m_internal->m_foreignSitmapIfc->setPlanOverlayMaxScale(planItem.m_fileId, maxScale); } void InterfaceImpl::onChangePlanOpacityOnMap(int planId, double opacity) { const PlanOverlaysPlugin::PlanOverlayModel::PlanItem & planItem = m_internal->m_overlayModel->planItem(planId); if (planItem.isValid()) m_internal->m_foreignSitmapIfc->setPlanOverlayOpacity(planItem.m_fileId, opacity); } void InterfaceImpl::onChangePlanOrderOnMap(const QVector &newIdOrder) { QVector layerOrder; // The layer in SituationMap layer factory are ordered from bottom to top. To get the correct order // we have to change the direction of ordering in the id list ... layerOrder.reserve(newIdOrder.size()); for (int planId : newIdOrder) { const PlanOverlaysPlugin::PlanOverlayModel::PlanItem & planItem = m_internal->m_overlayModel->planItem(planId); if (planItem.isValid()) { layerOrder.push_front(planItem.m_fileId); } } m_internal->m_foreignSitmapIfc->planOverlayChangeOrder(layerOrder); } void InterfaceImpl::onPlanContentChanged(const FileManagement::FileContent &data, DataModification modType) { if (data.fileType() != FileManagement::FileType::TacticalPlan) return; const PlanOverlaysPlugin::PlanOverlayModel::PlanItem &planItem = m_internal->m_overlayModel->findPlanItemByFileId(data.id()); if (planItem.isValid()) { switch (modType) { case DataModification::eModified : m_internal->m_overlayModel->updateOverlay(planItem.m_keyId); break; case DataModification::eRemoved : m_internal->m_overlayModel->removeOverlay(planItem.m_keyId); break; default: return; } } } void InterfaceImpl::onExerciseChanged(const ExerciseManagement::Exercise &data, DataModification modType) { switch (modType) { case DataModification::eModified: { DEBUG_LOG(m_logger) << "onExerciseChanged - Modified"; m_internal->m_overlayModel->reInitModel(data.id(), data.activePhaseId()); break; } default: break; } } void InterfaceImpl::onReloadPlanByName(const PlanOverlaysPlugin::PlanOverlayModel::PlanItem &planItem, int exerciseId, int phaseId, bool *removeFromModel) { if (removeFromModel) *removeFromModel = false; if (planItem.isValid()) { int planFileId = m_internal->m_dtc->getFileId(FileManagement::FileType::TacticalPlan, planItem.m_fileName, exerciseId, phaseId); DEBUG_LOG(m_logger) << "Plan File Id = " << planFileId; if (planFileId < 1) { if (removeFromModel) *removeFromModel = true; INFO_LOG(m_logger) << "Plan overlay will be removed: name = " << planItem.m_fileName; return; } FileManagement::FileContent fileContent = m_internal->m_dtc->fileContent(planFileId); QString nameOfPlan = QString::fromStdString(fileContent.name()); int planId = planItem.m_keyId; std::function handleContent = [this, planId, nameOfPlan, fileContent](const Core::FunctionReply &baseReply) { const FileManagement::GetFileContentDataReply &reply = static_cast(baseReply); const QByteArray &planXMLContent = LtsUtils::toQByteArray(reply.content()); if (planXMLContent.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << "Error plan [" << nameOfPlan << "] is empty"; return false; } else { PlanOverlaysPlugin::PlanOverlayModel::PlanItem pi = m_internal->m_overlayModel->planItem(planId); // get copy of planItem from model // file id has changed remove the old plan first if (!m_internal->m_foreignSitmapIfc->planOverlayRemove(pi.m_fileId)) { ERROR_LOG(m_logger) << LOGFUNCTION << "Error removing plan overlay from map failed [" << nameOfPlan << "]"; } // now load the new version of the plan if (!m_internal->m_foreignSitmapIfc->planOverlaySetContent(fileContent.id(), nameOfPlan, planXMLContent)) { ERROR_LOG(m_logger) << LOGFUNCTION << "Error rendering planContent [" << nameOfPlan << "]"; return false; } // re-write rest of properties, in case fileId changed, using same file name pi.m_fileName = nameOfPlan; pi.m_fileId = fileContent.id(); pi.m_folderId = fileContent.folderId(); if (fileContent.exerciseId().is_set()) { pi.m_exerciseId = fileContent.exerciseId().get(); if (fileContent.phaseId().is_set()) pi.m_phaseId = fileContent.phaseId().get(); } // don't change the attributes opacity, minScale, maxScale and visibility m_internal->m_overlayModel->updatePlanItem(pi); } return true; }; bool ok = m_internal->m_requestHelper->startSingleRequest([this,planFileId]() -> bool { FileManagement::GetFileContentDataRequest req; req.fileContentId(planFileId); return m_internal->m_ftc->getFileContentDataRequest(req, m_internal->m_flmReplyHandler); }, handleContent); m_internal->m_overlayModel->updateOrderOfPlans(); if (!ok) { ERROR_LOG(m_logger) << LOGFUNCTION << "Error reading planContent [" << nameOfPlan << "]"; const QString & msg = tr("Reading plan '%1' from database failed").arg(nameOfPlan); LtsWidgets::LtsMessageDialog::showMessage(m_session->baseLayout(), msg, m_internal->m_messageBoxTitle); } } } TopicEditorBase::TopicEditor *InterfaceImpl::createTopicEditor(const TopicEditorBase::TopicData &data) { std::shared_ptr flmDtc; if (!m_session->getDtc(flmDtc)) WARNING_LOG(m_logger) << "flm dtc missing - plan topic compatibility mode file name resolution not available"; PlanTopicEditor *editor = new PlanTopicEditor(&m_foreignIfc, flmDtc); editor->setTopicData(data); return editor; } TopicReplay::Interface *InterfaceImpl::createInterface(TopicEditorBase::TopicType) { return new TopicReplayInterface(m_internal->m_foreignSitmapIfc, this); } InterfaceImpl::TopicReplayInterface::TopicReplayInterface(SituationMap::ForeignGuiInterface *foreignSitmapIfc, InterfaceImpl *ovr) : m_ovr(ovr) , m_foreignSitmapIfc(foreignSitmapIfc) , m_logger(getLogger()) {} InterfaceImpl::TopicReplayInterface::~TopicReplayInterface() { playbackStop(); } bool InterfaceImpl::TopicReplayInterface::playbackInit(const QJsonObject &attributes) { if (!m_foreignSitmapIfc) { ERROR_LOG(m_logger) << LOGFUNCTION << "No foreign sitMapIfc, use PlanOverlaysPlugin::ForeignGuiInterface::setForeignSitMapGuiIfc() to define SituationMapGuiPlugin interface. Can't load tactical plan for playback ..."; return false; } // there exist one single plan per topic, get file id of plan from attributes int planFileId = attributes[QStringLiteral("fileId")].toInt(-1); if (planFileId != -1) // compatibility mode { WARNING_LOG(m_logger) << "Plan Overlays topic in compatibility mode!"; DEBUG_LOG(m_logger) << "Loading plan id = " << planFileId; if (!m_ovr->loadPlan(planFileId, false, m_foreignSitmapIfc)) return false; m_loadedPlanFileIds += planFileId; // store planFileId to remove plan when playback stops return true; } else { const auto plans = attributes[QStringLiteral("PlanItems")].toArray(); for (const QJsonValue &planItemValue : plans) { if (!planItemValue.isObject()) { ERROR_LOG(m_logger) << "PlanItem from json array is not an object! " << planItemValue.type(); continue; } const QJsonObject planItemObj(planItemValue.toObject()); planFileId = planItemObj[QStringLiteral("fileId")].toInt(-1); if (!m_ovr->loadPlan(planFileId, planItemObj)) { ERROR_LOG(m_logger) << "could not load plan with file id " << planFileId; continue; } m_loadedPlanFileIds += planFileId; } return true; } } bool InterfaceImpl::TopicReplayInterface::playbackStop() { if (nullptr == m_foreignSitmapIfc) { ERROR_LOG(m_logger) << LOGFUNCTION << "Use PlanOverlaysPlugin::ForeignGuiInterface::setForeignSitMapGuiIfc() to define SituationMapGuiPlugin interface ..."; return false; } while (!m_loadedPlanFileIds.isEmpty()) { int planFileId = m_loadedPlanFileIds.takeFirst(); DEBUG_LOG(m_logger) << " removePlan with file id " << planFileId; if (!m_ovr->removePlan(planFileId, m_foreignSitmapIfc)) // remove plan created in playbackInit() ERROR_LOG(m_logger) << "could not unload plan with file id " << planFileId; } return true; } QVector InterfaceImpl::TopicReplayInterface::additionalTrackEntries(const QJsonObject &attributes, bool &keepOriginalTopic) const { keepOriginalTopic = false; const auto plans = attributes[QStringLiteral("PlanItems")].toArray(); QVector ret; ret.reserve(plans.size()); for (const QJsonValue &ele : plans) { const auto pi = PlanOverlayModel::PlanItem::fromJson(ele.toObject()); if (!pi.isValid()) continue; TopicEditorBase::TopicData data; data.topicType = TopicEditorBase::TopicType::TacticalPlan; data.properties = QJsonObject({{QStringLiteral("FileId"), pi.m_fileId}}); ret += data; } return ret; } } // namespace PlanOverlaysPlugin