Files
qt5-legatus-legacy/Lts/Plugins/PlanOverlays/Gui/plan_overlays_gui_plugin.cpp
T
2026-08-13 18:33:10 +02:00

955 lines
33 KiB
C++
Executable File

/**
**
** @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 <LtsUtilsCore/pgarray.h>
#include <LtsUtilsCore/dds_vector.h>
#include <NGLLogger/INGLLogger.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <LtsWidgets/lts_base_layout_widget.h>
#include <LtsWidgets/lts_message_dialog.h>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <PluginInterfaces/style_plugin_interface.h>
#include <PluginInterfaces/project_plugin_interface.h>
#include <Services/OperatorManagement/opm_dtc.h>
#include <Services/ExerciseManagement/exm_dtc.h>
#include <Services/PlanEditor/ped_operator_rights.h>
#include <Plugins/OperatorManagement/opm_data_plugin_interface.h>
#include <Plugins/ExerciseManagement/exm_data_plugin_interface.h>
#include <Plugins/FileManagement/flm_data_plugin_interface.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
#include <Plugins/FileManagement/Gui/Explorer/TopicEditor/topic_editor.h>
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
#include <Viewer/SituationMap/Map/map.h>
#include <Viewer/SituationMap/GeometryModel/geometry_model_factory.h>
#include <QCoreApplication>
#include <QDateTime>
#include <QFile>
#include <QPointer>
#include <QStandardPaths>
#include <QHBoxLayout>
#include <QMainWindow>
#include <QtGlobal>
#include <QDebug>
#include <iostream>
namespace PlanOverlaysPlugin
{
std::shared_ptr<GuiPluginBase::Interface> PlanOverlaysGuiPlugin::createGuiInterface(LtsSession::LtsSession * session)
{
return std::make_shared<InterfaceImpl>(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<FileManagement::Interface::Dtc> m_dtc;
std::shared_ptr<FileManagement::Interface::Ftc> m_ftc;
std::shared_ptr<OperatorManagement::Interface::Dtc> m_opmDtc;
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
LtsUtils::RequestHelper * m_requestHelper;
FileManagementReplyHandler * m_flmReplyHandler;
PlanOverlayModel * m_overlayModel;
std::shared_ptr<SituationMap::ForeignGuiInterface> 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<FileManagement::DataInterface>(m_internal->m_dtc))
{
DEBUG_LOG(m_logger) << "getDtc for FileManagement::DataInterface failed.";
return false;
}
if (!m_session->getDtc<OperatorManagement::DataInterface>(m_internal->m_opmDtc))
{
DEBUG_LOG(m_logger) << "getDtc for OperatorManagement::DataInterface failed.";
return false;
}
if (!m_session->getDtc<ExerciseManagement::DataInterface>(m_internal->m_exmDtc))
{
DEBUG_LOG(m_logger) << "getDtc for ExerciseManagement::DataInterface failed.";
return false;
}
if (!m_session->getFtc<FileManagement::DataInterface>(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<int>::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::ConnectionType>(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<void(const Core::FunctionReply &baseReply)> 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<SituationMap::GeometryModel> 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<unsigned int, PlanOverlayModel::PlanItem> &planStore = m_internal->m_overlayModel->planStore();
QMapIterator<unsigned int, PlanOverlayModel::PlanItem> 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<void(const Core::FunctionReply &baseReply)> handleContent = [this,planFileId,planItem](const Core::FunctionReply &baseReply)
{
const FileManagement::GetFileContentDataReply &reply = static_cast<const FileManagement::GetFileContentDataReply &>(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<LtsWidgets::PluginData> InterfaceImpl::getActions()
{
QVector<LtsWidgets::PluginData> 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<QDialog> * overlayDialog = new PlanOverlayDialog<QDialog>(m_internal->m_foreignSitmapIfc, m_internal->m_overlayModel, m_session->baseLayout(), m_session);
#else
PlanOverlayDialog<LtsWidgets::LtsDialog> * overlayDialog = new PlanOverlayDialog<LtsWidgets::LtsDialog>(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<void(const Core::FunctionReply &baseReply)> handleContent = [this,planId,nameOfPlan](const Core::FunctionReply &baseReply)
{
const FileManagement::GetFileContentDataReply &reply = static_cast<const FileManagement::GetFileContentDataReply &>(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<int> &newIdOrder)
{
QVector<int> 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<void(const Core::FunctionReply &baseReply)> handleContent = [this, planId, nameOfPlan, fileContent](const Core::FunctionReply &baseReply)
{
const FileManagement::GetFileContentDataReply &reply = static_cast<const FileManagement::GetFileContentDataReply &>(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<FileManagement::Interface::Dtc> flmDtc;
if (!m_session->getDtc<FileManagement::DataInterface>(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<TopicEditorBase::TopicData> InterfaceImpl::TopicReplayInterface::additionalTrackEntries(const QJsonObject &attributes, bool &keepOriginalTopic) const
{
keepOriginalTopic = false;
const auto plans = attributes[QStringLiteral("PlanItems")].toArray();
QVector<TopicEditorBase::TopicData> 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