Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+954
@@ -0,0 +1,954 @@
|
||||
/**
|
||||
**
|
||||
** @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
|
||||
Reference in new issue
Block a user