955 lines
33 KiB
C++
Executable File
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
|