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

1056 lines
42 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @RDE_HEADER@
* HMI Plugin for ExerciseManangement - Exercise Overview Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#include "snapshot_dialog.h"
#include "ui_snapshot_dialog.h"
#include "snapshot_dialog_model.h"
#include "mrk_create_snapshot.h"
#include <LtsUtilsCore/dds_vector.h>
#include <LtsUtilsCore/dds_vector_to_pixmap.h>
#include <LtsSession/lts_session_data.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <LtsAppManager/lts_app_manager.h>
#include <LtsPrinting/printing.h>
#include <Plugins/PlanEditor/PlanEditorPlugin/plan_editor_gui_plugin_interface.h>
#include <Plugins/MarkerManagement/Gui/Common/mrk_apply_filter.h>
// PlanOverlay
#include <Plugins/PlanOverlays/plan_overlays_plugin_interface.h>
#include <Services/PlanEditor/ped_operator_rights.h>
#include <FilterManagement/flt_data_plugin_aes_display_filter.h>
#include <Map/operator_map_settings.h>
#include <QImage>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QTimer>
#include <ngltools/ngltools_dtg.h>
namespace MarkerManagement
{
const int snapShotOverlayId = -4711;
//-----------------------------------------------------------------------------------------------------
// SnapshotDialog
//-----------------------------------------------------------------------------------------------------
SnapshotDialog::SnapshotDialog(const QString &menuGroup, LtsSession::LtsSession *session, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_menuGroup(menuGroup)
, m_model(nullptr)
, m_ui(new Ui::SnapshotDialog)
, m_session(session)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_planEditorHasUpdatedThePlan(false)
, m_planEditorApp(nullptr)
, m_fileId(-1)
, m_logger(getLogger("SnapshotDialog"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_ui->setupUi(this);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Show,
this, &SnapshotDialog::onShow);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Edit,
this, &SnapshotDialog::onEditMetaData);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Remove,
this, &SnapshotDialog::onRemoveSnapshots);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Save,
this, &SnapshotDialog::onSaveSettings);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Load,
this, &SnapshotDialog::onEditPlan);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print,
this, &SnapshotDialog::onPrint);
//m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setEnabled(false);
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setText(tr("Save Settings"));
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->setText(tr("Edit Plan"));
connect(m_ui->widget, &LtsDataView::LtsDataView::signalDoubleClicked,
this, &SnapshotDialog::onEditMetaData);
// connect(m_ui->widget, &LtsDataView::LtsDataView::signalSelectionChanged,
// this, &SnapshotDialog::onSelectionChanged);
setOnlineHelpFilename(QStringLiteral("Situation-Snapshots"));
m_ui->widget->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
session->getForeignGuiInterface<FileManagement::ForeignGuiInterface>(m_flmGui);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
//-----------------------------------------------------------------------------------------------------
SnapshotDialog::~SnapshotDialog()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
// plan editor app delete-flagged in aboutToClose
delete m_ui;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::retranslate()
{
translate(m_ui, tr("Snapshots"));
}
/*
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::onSelectionChanged()
{
const QModelIndex idx = m_ui->widget->currentIndex();
const MarkerManagement::Snapshot & snapshot = m_model->getRowData(idx.row());
if (snapshot.id() != m_currentSnapshot.id())
{
m_buttonBox->getButton(LtsWidgets::DialogButton::Save)->setEnabled(false);
}
}
*/
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::onShow()
{
const QModelIndex &idx = m_ui->widget->currentIndex();
const MarkerManagement::Snapshot &snapshot = m_model->getRowData(idx.row());
if (snapshot.id() <= 0)
return;
m_currentSnapshot = snapshot; // fileContendId is the fileId of previewImage
showSnapshot();
}
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::onRefreshCurrent(const Snapshot &snapshot)
{
INFO_LOG(m_logger) << LOGFUNCTION << " current SnapshotId: " << snapshot.id();
// show Snapshot only if current snapshot changed from outside
if (snapshot.id() != m_currentSnapshot.id())
return;
m_currentSnapshot = snapshot;
showSnapshotInternal(m_currentSnapshot, false); // applyFilter
}
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::showSnapshot()
{
INFO_LOG(m_logger) << LOGFUNCTION << " current SnapshotId: " << m_currentSnapshot.id();
showSnapshotInternal(m_currentSnapshot, true); // applyFilter
enaDisa();
}
//-----------------------------------------------------------------------------------------------------
bool SnapshotDialog::applyFilter(const Snapshot &snapshot)
{
const QByteArray filterContent(QByteArray::fromRawData(snapshot.filter().c_str(), snapshot.filter().size()));
if (filterContent.isEmpty())
{
ERROR_LOG(m_logger) << "filterContent is empty, snapshot.id()="<< snapshot.id();
return false;
}
// 1) shortcut for our session/widget:
MarkerManagementApplyFilter appFilter(m_session, m_fltParticipantIfc, m_fltAesIfc, m_fltBuildingComponentIfc);
if (!appFilter.applyFilter(filterContent))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Error while applying filters";
return false;
}
// 2) shortcut for session/widget of PlanEditor :
// but only if snapshot.id and PlanEditor.m_currentSnapshot.id match:
if (m_planEditorApp && (m_currentSnapshot.id() == snapshot.id()))
{
const int partitionId = m_session->partitionId();
auto pe_session = m_planEditorApp->getSession(IDL::PartitionType::Replay, partitionId);
if (pe_session)
{
std::shared_ptr<FilterManagement::ForeignFltParticipantGuiInterface> pe_fltParticipantIfc;
std::shared_ptr<FilterManagement::ForeignAesGuiInterface> pe_fltAesIfc;
std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> pe_fltBuildingComponentIfc;
pe_session->getForeignGuiInterface<FilterManagement::ForeignFltParticipantGuiInterface>(pe_fltParticipantIfc); // not in global
pe_session->getForeignGuiInterface<FilterManagement::ForeignAesGuiInterface>(pe_fltAesIfc); // not in global
pe_session->getForeignGuiInterface<FilterManagement::ForeignFltBuildingComponentGuiInterface>(pe_fltBuildingComponentIfc); // not in global
if (pe_fltParticipantIfc && pe_fltAesIfc)
{
MarkerManagementApplyFilter appFilter2(pe_session, pe_fltParticipantIfc, pe_fltAesIfc, pe_fltBuildingComponentIfc);
if (!appFilter2.applyFilter(filterContent))
{
ERROR_LOG(m_logger) << LOGFUNCTION << "Error while applying filters";
return false;
}
} else
ERROR_LOG(m_logger) << LOGFUNCTION << "getForeignGuiInterface failed";
} else
ERROR_LOG(m_logger) << LOGFUNCTION << "Internal error - can not get previously created session!";
}
// : shortcut for session/widget of PlanEditor
return true;
}
void SnapshotDialog::tableColumnOrganiserSetting()
{
//LtsColumnOrganiser
m_ui->widget->setColumnsOrganiserEnabled(identifier(), true);
}
//-----------------------------------------------------------------------------------------------------
bool SnapshotDialog::showSnapshotInternal(const Snapshot &snapshot, bool bApplyFilter)
{
auto spSitMapGuiIfc = m_sitMapGuiIfc.lock();
SituationMap::SitMapUpdatesEnabledHelper sitMapHelper(spSitMapGuiIfc);
const int partitionId = m_session->partitionId();
INFO_LOG(m_logger) << LOGFUNCTION << " current Snapshot: " << snapshot
<< ", replayPartition: " << partitionId
<< ", applyFilter=" << bApplyFilter;
SituationReplay::ChangeReplayStateRequest req;
req.id(0);
req.replayPartitionId(partitionId);
req.state(SituationReplay::SrpState::Started);
req.beginTime(snapshot.timeStamp());
req.endTime(snapshot.timeStamp()); // snapshot
req.pace(0);
if (!m_requestHelper->startSingleRequest([req, this]() { return m_srpFtc->changeReplayStateRequest(req, this); }))
{
ERROR_LOG(m_logger) << "Replay could not be started for partition id = " << partitionId;
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Replay could not be started for partition with id %1").arg(partitionId));
return false;
}
bool success = true;
// Filter:
if (bApplyFilter)
if (!applyFilter(snapshot))
{
ERROR_LOG(m_logger) << "applyFilter failed, partition id = " << partitionId;
success = false;
}
if (spSitMapGuiIfc)
{
std::shared_ptr<PlanOverlaysPlugin::ForeignGuiInterface> ovrDtc;
if (m_session->getForeignGuiInterface<PlanOverlaysPlugin::ForeignGuiInterface>(ovrDtc))
{
INFO_LOG(m_logger) << LOGFUNCTION << " unloadAllPlans via PlanOverlaysPlugin to update dialog";
ovrDtc->unloadAllPlans();
}
// SitMapForeignInterface => set Settings
{
const QByteArray ba(QByteArray::fromRawData(snapshot.mapSettings().c_str(), snapshot.mapSettings().size()));
spSitMapGuiIfc->setOperatorMapSettingsJson(ba);
const QByteArray planOverlay(snapshot.planOverlay().c_str(), snapshot.planOverlay().size());
if (!planOverlay.isEmpty())
{
// this is editable Plan/Overlay of snapshot:
{
const bool bIsMapReady = spSitMapGuiIfc->isMapReady();
INFO_LOG(m_logger) << LOGFUNCTION << " bIsMapReady = " << bIsMapReady;
if (bIsMapReady)
{
if (!spSitMapGuiIfc->planOverlaySetContent(snapShotOverlayId, tr("Snapshot-PlanOverlay"), planOverlay, true))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Error add planOverlay to snapshot, SnapshotId: " << snapshot.id()
<< " planOverlay.size()=" << planOverlay.size()
<< " planOverlay=" << planOverlay;
success = false;
}
} else {
// GUEZE-811 3D Lage Filter werde nicht korrekt abgebaut... wenn 2D-Snap
// mit SnapshotPlan auf 3D-Topic folgt...
// Szenario: 2D-Snapshuss wird wiedergegeben (App Snapshot|Script)
// und SitMap muss dafür von 3D auf 2D wechseln.
// Dabei werden die Overlays via MapOperatorSettings zunächst vorbereitet.
// Dabei wurde aber in MapImpl::restoreOperatorMapSettings der SNAPSHOT-Overlay
// als herkömmlicher Overlay vorbereitet.
// Beim nachfolgendem OverlayHandlerImpl::setOverlay usw. ergaben sich Fehler
// wg. LayerType::SnapshotOverlay vs. LayerType::Overlay.
// Mit dem FixPart1 wird in restoreOperatorMapSettings der snapShotOverlayId == -4711
// als isSnapshot==true eintragen.
// Mit FixPart2 bei SnapshotDialog::showSnapshotInternal ein
// setMapReadyCallback genutzt, weil die SitMap noch nicht mapReady ist.
// Mit FixPart3 bei InterfaceImpl::snapshotAdjustSitMap ebenfalls ein
// setMapReadyCallback genutzt, weil die SitMap noch nicht mapReady ist.
// dirty hack: try again
// QTimer::singleShot(1000, this, [this, snapshot, bApplyFilter]() {
// showSnapshotInternal(snapshot, bApplyFilter);
// });
// QTimer::singleShot(1000, this, [this, spSitMapGuiIfc, snapShotOverlayId, planOverlay, snapshot]() {
// if (!spSitMapGuiIfc->planOverlaySetContent(snapShotOverlayId, tr("Snapshot-PlanOverlay"), planOverlay, true))
// {
// ERROR_LOG(m_logger) << LOGFUNCTION << " Error add planOverlay to snapshot, SnapshotId: " << snapshot.id()
// << " planOverlay.size()=" << planOverlay.size()
// << " planOverlay=" << planOverlay;
// }
// });
const int snapshotId = snapshot.id();
auto onMapIsReadyFn = [this, spSitMapGuiIfc, planOverlay, snapshotId]() {
INFO_LOG(m_logger) << LOGFUNCTION << " try again: do planOverlaySetContent...";
if (!spSitMapGuiIfc->planOverlaySetContent(snapShotOverlayId, tr("Snapshot-PlanOverlay"), planOverlay, true))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Error add planOverlay to snapshot, SnapshotId: " << snapshotId
<< " planOverlay.size()=" << planOverlay.size()
<< " planOverlay=" << planOverlay;
}
};
if (!spSitMapGuiIfc->setMapReadyCallback(this, onMapIsReadyFn))
ERROR_LOG(m_logger) << LOGFUNCTION << ", setMapReadyCallback failed, SnapshotId: " << snapshotId;
}
} // : this is editable Plan/Overlay of snapshot
}
}
// SitMapForeignInterface => restore MovementTracks
{
const QByteArray ba(QByteArray::fromRawData(snapshot.movementTracks().c_str(), snapshot.movementTracks().size()));
if (!spSitMapGuiIfc->setMovementTrackManagerSettingsJSon(ba))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Error setMovementTrackManagerSettingsJSon, SnapshotId: " << snapshot.id();
success = false;
}
}
}
QString name = tr("Snapshot '%1'").arg(QString::fromStdString(m_currentSnapshot.name()));
m_session->baseLayout()->setCustomText(name);
spSitMapGuiIfc->showExerciseTimeString(true);
return success;
}
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::onEditPlan()
{
// the user-selected snapshot should be displayed in the viewer while the according plan is loaded by the planEditor:
// (but don´t reload if already loaded to not discard changes to center and scale)
const QModelIndex idx = m_ui->widget->currentIndex();
const MarkerManagement::Snapshot &snapshot = m_model->getRowData(idx.row());
// show snapshot if necessary
// another than the current is requested to be modified with planEditor
if (snapshot.id() != m_currentSnapshot.id())
onShow();
if (m_currentSnapshot.id() <= 0)
return;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ToDo: showing message like the following
*
* const QString & msg = tr("Launching PlanEditor to edit snapshot plan. Saving the plan to snap shot will be done automatically at finish of planeditor.");
*
* LtsWidgets::LtsMessageDialog::showMessage(this, msg, tr("Launching PlanEditor"), LtsWidgets::Behaviour::Popup );
*
* with the difference that the message should be shown only for a short time interval and will be removed from screen without any user interaction.
* Eventually it would be nice to add the name of snapshot to the message.
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
// Keep in mind operator rights of plan editor here. Has the user the required rights to start the plan editor?
if (!LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::PlanEditor::TacticalPlansEditSnapShot))
{
QString msg = tr("Operator right to launching Plan Editor to edit snapshot plans is missing");
LtsWidgets::LtsMessageDialog::showMessage(this, msg, tr("Launching Plan Editor is forbidden"), LtsWidgets::Behaviour::Popup );
return;
}
m_planEditorHasUpdatedThePlan = false;
LtsSession::DdsConnectionParams connParams(m_session->connParams());
m_planEditorApp = LtsAppManager::LtsAppManager::getInstance()->createApp(LtsAppDescriptor::App::PlanEditor, connParams);
if (!m_planEditorApp)
{
// In case of failure createApp will show an error message
ERROR_LOG(m_logger) << "Creating Plan Editor-Snapshot failed.";
return;
}
// (re-) enable btn 'start PlanEditor' if planEditor is finished
// QueuedConnection since this signal is fired during the destruction process and
// onPlaneditorFinished creates a new local eventloop...
connect(m_planEditorApp.data(), &QObject::destroyed, this, &SnapshotDialog::onPlaneditorFinished, Qt::QueuedConnection);
LtsSession::LtsSession *session = m_planEditorApp->getSession(IDL::PartitionType::Replay, connParams.partitionId);
session->presetBaseLayoutAsSecondaryWidget(); // see https://devhub.defence-elec.de/confluence/x/yLjeDg 13.1
// session->setDependency(m_session); // prevents mmm to let that secondary widget be placed above this app => jetzt doch: GUEZE-2619
if (session->isInitializationDone())
slotReplaySessionForPlanEditorStarted();
else
connect(session, &LtsSession::LtsSession::signalDoneInitializingSession,
this, &SnapshotDialog::slotReplaySessionForPlanEditorStarted); // wait until the new replay session is initialized
enaDisa();
}
void SnapshotDialog::onPlaneditorFinished()
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " onPlaneditorFinished"
<< " m_planEditorHasUpdatedThePlan=" << m_planEditorHasUpdatedThePlan;
// User has has saved the Plan for snapshot before PE is finished.
// Due to onRefreshCurrent the data is updated. (and ReplayStartRequested)
// So an additional showSnapshot will cause an additonal ReplayStartRequested
// The ReplayProcess doesn´t like to requests with such a small amount of time (~300ms)
// so skip showSnapshot here:
// showSnapshot();
if (m_planEditorHasUpdatedThePlan)
{
const QString &title = QString::fromStdString(m_currentSnapshot.name());
saveSnapshotPreviewInLibrary(title);
}
enaDisa();
}
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::slotReplaySessionForPlanEditorStarted()
{
const int partitionId = m_session->partitionId();
auto session = m_planEditorApp->getSession(IDL::PartitionType::Replay, partitionId);
if (!session)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Internal error - can not get previously created session!";
return;
}
disconnect(session, &LtsSession::LtsSession::signalDoneInitializingSession,
this, nullptr);
if (!m_planEditorApp)
return;
if (!m_srpFtc)
return;
std::shared_ptr<SituationMap::ForeignGuiInterface> sitMapGuiIfc;
std::shared_ptr<PlanEditorPlugin::ForeignGuiInterface> planEditorIfc;
session->getForeignGuiInterface<SituationMap::ForeignGuiInterface>(sitMapGuiIfc);
session->getForeignGuiInterface<PlanEditorPlugin::ForeignGuiInterface>(planEditorIfc);
if (!sitMapGuiIfc || !planEditorIfc)
{
WARNING_LOG(m_logger) << "Can not create plan editor app - found not all required interfaces";
m_planEditorApp->deleteLater();
m_planEditorApp = nullptr;
return;
}
// get Settings from SitMap => SitMapForeignInterface
DEBUG_LOG(m_logger) << LOGFUNCTION << " current SnapshotId: " << m_currentSnapshot.id()
<< ", PlanEditor - partitionId = " << partitionId;
SituationReplay::ChangeReplayStateRequest req;
req.id(0);
req.replayPartitionId(partitionId);
req.state(SituationReplay::SrpState::Started);
req.beginTime(m_currentSnapshot.timeStamp());
req.endTime(m_currentSnapshot.timeStamp());
req.pace(0);
if (!m_requestHelper->startSingleRequest([req, this]() { return m_srpFtc->changeReplayStateRequest(req, this); }))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Replay could not be started for partition id = " << partitionId;
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Replay could not be started for partition with id %1").arg(partitionId));
m_planEditorApp->deleteLater();
return;
}
#define PlanEditorForSnapshot3D
#ifdef PlanEditorForSnapshot3D
{
const QByteArray ba(QByteArray::fromRawData(m_currentSnapshot.mapSettings().c_str(), m_currentSnapshot.mapSettings().size()));
QJsonParseError parserError;
const QJsonDocument jDoc(QJsonDocument::fromJson(ba, &parserError));
if (parserError.error != QJsonParseError::NoError)
{
ERROR_LOG(m_logger) << "Error parsing json document: " << parserError.errorString();
return;
}
SituationMap::OperatorMapSettings oms;
oms.fromJsonObject(jDoc.object());
if (oms.mode == SituationMap::Mode::Mode_3D)
{
DEBUG_LOG(m_logger) << " PlanEditor in 3D not supported yet, use 2D";
oms.mode = SituationMap::Mode::Mode_2D;
sitMapGuiIfc->setOperatorMapSettingsJson(oms.toJson()); // SitMapForeignInterface => set Settings
} else {
sitMapGuiIfc->setOperatorMapSettingsJson(ba); // SitMapForeignInterface => set Settings
}
}
#else
const QByteArray ba(QByteArray::fromRawData(m_currentSnapshot.mapSettings().c_str(), m_currentSnapshot.mapSettings().size()));
sitMapGuiIfc->setOperatorMapSettingsJson(ba); // SitMapForeignInterface => set Settings
#endif // PlanEditorForSnapshot3D
applyFilter(m_currentSnapshot);
planEditorIfc->setPlanToEdit( QByteArray::fromStdString(m_currentSnapshot.planOverlay()) ); // read plan from snapshot
planEditorIfc->addSaveSnapshotAction(QIcon(), tr("Save Plan to Snapshot"), [this](const QByteArray & planXMLContent) -> bool
{
return saveSnapshotPlan(planXMLContent);
});
// const QByteArray filterContent(QByteArray::fromRawData(snapshot.filter().c_str(), snapshot.filter().size()));
// if (filterContent.isEmpty())
// ERROR_LOG(m_logger) << "filterContent is empty, snapshot.id()="<< snapshot.id();
// std::shared_ptr<FilterManagement::ForeignAesGuiInterface> pe_fltIfc;
// session->getForeignGuiInterface<FilterManagement::ForeignAesGuiInterface>(pe_fltIfc); // not in global
// if (pe_fltIfc)
// {
// MarkerManagementApplyFilter appFilter(session, pe_fltIfc);
// if (!appFilter.applyFilter(filterContent))
// {
// ERROR_LOG(m_logger) << LOGFUNCTION << " Error while applying filters";
// }
// } else
// ERROR_LOG(m_logger) << LOGFUNCTION << " getForeignGuiInterface failed";
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->setEnabled(false); // start PlanEditor only once
}
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::prepareIndex(const QModelIndex &idx)
{
const MarkerManagement::Snapshot &snapshot = m_model->getRowData(idx.row());
DEBUG_LOG(m_logger) << "Prepare index for snapshot with id = " << snapshot.id();
const int fileId = snapshot.fileContentId();
if (fileId > 0) // found the snapshot in db
{
DEBUG_LOG(m_logger) << "Loading file content with id = " << fileId;
QByteArray content;
if (m_flmGui->loadContent(fileId, content) && content.size() > 0)
{
QString timeStr = DTG::toStringMSec(snapshot.timeStamp());
QString name = QString::fromStdString(snapshot.name());
m_descriptions.append( QString::fromStdString(snapshot.description()));
m_imageTitles.append( name + QStringLiteral(" (")+ timeStr + QStringLiteral(")"));
m_images.append(QImage::fromData(content));
DEBUG_LOG(m_logger) << " title=" << m_imageTitles.last()
<< " description=" << m_descriptions.last()
<< " content.size=" << content.size();
}
else
{
ERROR_LOG(m_logger) << "Cannot retrieve file content for fileId = " << fileId;
}
}
else
WARNING_LOG(m_logger) << "Not found: file id = 0 for snapshot id = " << snapshot.id();
}
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::onPrint()
{
const QModelIndexList selectedRows = m_ui->widget->selectedRows();
m_descriptions.clear();
m_imageTitles.clear();
m_images.clear();
DEBUG_LOG(m_logger) << "selectedRows.size()=" << selectedRows.size();
for (const QModelIndex &idx : selectedRows)
prepareIndex(idx);
if (m_images.size() > 0)
LtsPrinting::printImages(m_session, this, tr("Snapshots"), m_images, m_imageTitles, m_descriptions);
else
ERROR_LOG(m_logger) << "No snapshots selected or no valid images to print";
}
//-----------------------------------------------------------------------------------------------------
bool SnapshotDialog::saveSnapshotPlan(const QByteArray &planXMLContent)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " save snapshot";
// store planXMLContent in snapshot
m_currentSnapshot.planOverlay(planXMLContent.constData());
bool ok = m_requestHelper->startSingleRequest([this]()
{
return m_mrkFtc->snapshotModifyRequest(m_currentSnapshot,this);
});
if (!ok)
{
ERROR_LOG(m_logger) << "Error while updating snapshot plan overlay";
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Error while updating snapshot plan overlay"), tr("Saving Plan to Snapshot") );
}
else
{
m_planEditorHasUpdatedThePlan = true;
}
return ok;
}
//-----------------------------------------------------------------------------------------------------
bool SnapshotDialog::init(const std::shared_ptr<MarkerManagement::Interface::Dtc> &mrkDtc,
const std::shared_ptr<MarkerManagement::Interface::Ftc> &mrkFtc,
const std::shared_ptr<FileManagement::Interface::Dtc> &flmDtc,
const std::shared_ptr<FileManagement::Interface::Ftc> &flmFtc,
const std::shared_ptr<OperatorManagement::Interface::Dtc> &opmDtc,
const std::shared_ptr<FilterManagement::ForeignFltParticipantGuiInterface> &fltParticipipantIfc,
const std::shared_ptr<FilterManagement::ForeignAesGuiInterface> &fltAesIfc,
const std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> &fltBuildingComponentIfc
)
{
m_mrkDtc = mrkDtc;
m_mrkFtc = mrkFtc;
m_flmDtc = flmDtc;
m_flmFtc = flmFtc;
m_opmDtc = opmDtc;
m_fltParticipantIfc = fltParticipipantIfc;
m_fltAesIfc = fltAesIfc;
m_fltBuildingComponentIfc = fltBuildingComponentIfc;
m_model = new SnapshotDialogModel(this);
m_model->initModel(m_mrkDtc, m_opmDtc, m_flmDtc, m_flmGui);
auto proxy = new SnapshotDialogFilterProxyModel(this);
proxy->setSourceModel(m_model);
m_ui->widget->setModel(proxy);
m_ui->widget->hideColumn(SnapshotDialogModel::Columns::ColImage);
m_ui->widget->setInitialSearchContent(SnapshotDialogModel::ColOperator,
LtsSessionData::LtsSessionData::getInstance()->getOperatorId());
connect(m_ui->widget, &LtsDataView::LtsDataView::signalSelectionChanged,
this, &SnapshotDialog::enaDisa);
connect(m_model, &SnapshotDialogModel::modelChanged,
this, &SnapshotDialog::onRefreshCurrent);
m_session->getFtc<SituationReplay::DataInterface>(m_srpFtc);
if (!m_srpFtc)
return false;
std::shared_ptr<SituationMap::ForeignGuiInterface> spSitMapGuiIfc;
m_session->getForeignGuiInterface<SituationMap::ForeignGuiInterface>(spSitMapGuiIfc);
if (!spSitMapGuiIfc)
return false;
m_sitMapGuiIfc = spSitMapGuiIfc;
// pay attention to mapReady:
// A small delay is required. Otherwise SituationMapInit does not exists:
QTimer::singleShot(1000, this, [this, spSitMapGuiIfc]() {
if (!spSitMapGuiIfc->setMapReadyCallback(this, [this]() { enaDisa(); }))
ERROR_LOG(m_logger) << LOGFUNCTION << " setMapReadyCallback failed"
<< ", m_session->toString " << m_session->toString()
<< ", this=" << this;
});
enaDisa();
return true;
}
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::aboutToClose()
{
DEBUG_LOG(m_logger) << "aboutToClose - start";
if (m_planEditorApp)
{
m_planEditorApp->disconnect(this); // disconnect me from m_planEditorApp
//We get the session and "close the baseLayout" which will result in a perfect cleanup of the PlanEditor
const int partitionId = m_session->partitionId();
auto pe_session = m_planEditorApp->getSession(IDL::PartitionType::Replay, partitionId);
if (pe_session)
{
pe_session->baseLayout()->close();
}
m_planEditorApp->deleteLater();
}
m_planEditorApp = nullptr;
}
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::enaDisa()
{
m_buttonBox->setToolTip(LtsWidgets::DialogButton::Load, QString());
// do not change selected row, if PlanEditor is running.
m_ui->widget->setEnabled(m_planEditorApp.isNull());
QModelIndexList selectedRows = m_ui->widget->selectedRows();
const int cntSelectedRows = selectedRows.size();
// BTN Edit
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Edit, (cntSelectedRows == 1));
// BTN Remove
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, (cntSelectedRows > 0));
// BTN Print
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Print, (cntSelectedRows > 0));
// BTN Save (Save Settings)
{
const QModelIndex &idx = m_ui->widget->currentIndex();
const MarkerManagement::Snapshot &userSelSnapshot = m_model->getRowData(idx.row());
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Save, (m_currentSnapshot.id() > 0) && (userSelSnapshot.id() == m_currentSnapshot.id()));
}
// show btn:
bool mapReady = false;
bool snapshotIn2D = false;
auto spSitMapGuiIfc = m_sitMapGuiIfc.lock();
if (spSitMapGuiIfc != nullptr)
mapReady = spSitMapGuiIfc->isMapReady();
const QModelIndex idx = m_ui->widget->currentIndex();
const MarkerManagement::Snapshot &snapshot = m_model->getRowData(idx.row());
QJsonParseError error;
QJsonDocument doc(QJsonDocument::fromJson(QByteArray::fromRawData(snapshot.mapSettings().c_str(),
snapshot.mapSettings().size()), &error));
if (error.error == QJsonParseError::NoError)
{
QJsonObject root(doc.object());
const int mode = root[QStringLiteral("mode")].toInt(-1);
snapshotIn2D = (mode == int(SituationMap::MapMode::MapMode_2D));
}
QStringList errors;
if (cntSelectedRows != 1)
errors += tr("Select one snapshot to edit");
if (!spSitMapGuiIfc)
errors += tr("Map interface missing");
if (m_planEditorApp)
errors += tr("Plan editor already open");
if (error.error != QJsonParseError::NoError)
errors += tr("Snapshot is missing critical map informations");
if (!snapshotIn2D)
errors += tr("Unable to edit a snapshot that was taken in 3D mode - edit the snapshot and save it while in 2D mode");
if (!mapReady)
errors += tr("Situation isn't ready yet. Please try again later.");
m_buttonBox->setToolTip(LtsWidgets::DialogButton::Load, errors.join(QStringLiteral("<br />")));
// BTN Show
bool bSelectedSnapshotActuallyInEditor = true;
if (m_planEditorApp)
bSelectedSnapshotActuallyInEditor = snapshot.id() == m_currentSnapshot.id();
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Show, (cntSelectedRows == 1) && mapReady && bSelectedSnapshotActuallyInEditor);
// BTN Load (Edit Plan)
const bool bHasPlanEditor =
LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::PlanEditor::TacticalPlansEditSnapShot) &&
LtsAppManager::LtsAppManager::getInstance()->hasApp(LtsAppDescriptor::App::PlanEditor);
m_buttonBox->setButtonVisible(LtsWidgets::DialogButton::Load, bHasPlanEditor);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Load, (cntSelectedRows == 1) && mapReady && m_planEditorApp.isNull() /*&& snapshotIn2D*/);
// BTN Remove
const QVector<int> &selectedSnapIds = selectedSnapshotIds();
if (selectedSnapIds.isEmpty())
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, false);
else
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Remove, !selectedSnapIds.contains(m_currentSnapshot.id()));
}
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::onEditMetaData()
{
const QModelIndex idx = m_ui->widget->currentIndex();
const MarkerManagement::Snapshot snapshot = m_model->getRowData(idx.row());
if (snapshot.id() <= 0)
return;
INFO_LOG(m_logger) << "current SnapshotId: " << snapshot.id();
if (snapshot.operatorId() == LtsSessionData::LtsSessionData::getInstance()->getOperatorId())
{ // If snapshot is owned by current operator
addDialog(LtsWidgets::initDialog(new SnapshotCreateDialog(this),
m_mrkFtc, m_flmDtc, m_flmFtc, m_sitMapGuiIfc, m_fltParticipantIfc, m_fltAesIfc, m_fltBuildingComponentIfc, m_session, snapshot),
m_menuGroup, QStringLiteral("EditSnapshot%1").arg(snapshot.id()));
}
else
{ // If snapshot belongs to an other operator
// Show message 'Want to copy?' then do nothing or copy
switch (LtsWidgets::LtsMessageDialog::showMessage(this,
tr("You are not allowed to edit the snapshots of other operators.\nDo you want to make a copy of it?"),
QString(),
LtsWidgets::Behaviour::Modal,
LtsWidgets::Button::Yes | LtsWidgets::Button::No))
{
case LtsWidgets::Button::No:
// Do nothing
break;
case LtsWidgets::Button::Yes:
// Make a new snap with old data
{
Snapshot req(snapshot);
req.id(0);
req.name((tr("Copy - %1").arg(QString::fromStdString(snapshot.name()))).toStdString());
req.operatorId(LtsSessionData::LtsSessionData::getInstance()->getOperatorId());
req.fileContentId(0); //no preview image when copying a snapshot, later by edit metadata when we change the name, a new image will be saved
m_requestHelper->startSingleRequest([req, this]()
{
return m_mrkFtc->snapshotModifyRequest(req, this);
});
}
break;
default:
// Can't happen but satisfies the compiler
break;
}
}
}
//-----------------------------------------------------------------------------------------------------
QVector<int> SnapshotDialog::selectedSnapshotIds() const
{
const QModelIndexList selectedRows = m_ui->widget->selectedRows();
QVector<int> selectedSnapshotIds;
selectedSnapshotIds.reserve(selectedRows.size());
for (const QModelIndex &idx : selectedRows)
{
if (!idx.isValid())
continue;
const MarkerManagement::Snapshot snapshot = m_model->getRowData(idx.row());
if (snapshot.id() <= 0)
continue;
selectedSnapshotIds.push_back(snapshot.id());
} // for
return selectedSnapshotIds;
}
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::onRemoveSnapshots()
{
const QModelIndexList selectedRows = m_ui->widget->selectedRows();
for (const QModelIndex &idx : selectedRows)
{
if (!idx.isValid())
continue;
const MarkerManagement::Snapshot &snapshot = m_model->getRowData(idx.row());
if (snapshot.id() <= 0)
continue;
Core::RemoveElementRequest req(snapshot.id());
m_requestHelper->addRequest([req, this]()
{
return m_mrkFtc->snapshotRemoveRequest(req, this);
});
if (snapshot.fileContentId() > 0)
{
Core::RemoveElementRequest req1(snapshot.fileContentId());
m_requestHelper->addRequest([req1, this]()
{
return m_flmFtc->fileContentRemoveRequest(req1, this);
});
}
}
m_requestHelper->startRequests();
}
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::saveSnapshotFilter()
{
// Save MapSetting Changes
auto spSitMapGuiIfc = m_sitMapGuiIfc.lock();
if (!spSitMapGuiIfc)
{
ERROR_LOG(m_logger) << LOGFUNCTION << ", no m_sitMapGuiIfc, return";
return;
}
// Save Filter, Filter = {operatorMapSettings, AesFilter, SymbolAppearance, (possibly modified) MovementTracks}
// 1) operatorMapSettings/OMS
const QByteArray mapSettings = spSitMapGuiIfc->operatorMapSettingsJson();
Snapshot req(m_currentSnapshot);
req.mapSettings(mapSettings.data());
req.operatorId(LtsSessionData::LtsSessionData::getInstance()->getOperatorId());
DEBUG_LOG(m_logger) << "File content id (of preview image) = " << m_currentSnapshot.fileContentId();
req.fileContentId(m_fileId);
// 2,3) AesFilter, SymbolAppearance, BuildingComponents
MarkerManagementApplyFilter appFilter(m_session, m_fltParticipantIfc, m_fltAesIfc, m_fltBuildingComponentIfc);
const QByteArray &filterParams = appFilter.getFilterParams();
req.filter(filterParams.constData());
// 4) MovementTracks
const QByteArray &movementTracks = spSitMapGuiIfc->movementTrackManagerSettingsJson();
req.movementTracks(movementTracks.constData());
// 'planOverlay' is editable Plan/Overlay of snapshot:
// it can be empty. A size is 0 will be ignored, means old value will remain at svc :-)
if (!m_requestHelper->startSingleRequest([req, this]() { return m_mrkFtc->snapshotModifyRequest(req, this);}))
ERROR_LOG(m_logger) << "Saving snapshot filter failed";
else
{
__unchange__();
// Do not update m_currentSnapshot. It´s done via onRefreshCurrent. Avoid race condition
// m_currentSnapshot = req;
}
}
//-----------------------------------------------------------------------------------------------------
void SnapshotDialog::onSaveSettings()
{
// Update in Library
const MarkerManagement::Snapshot &snapshot = m_currentSnapshot;
const QString title = QString::fromStdString(snapshot.name());
m_fileId = snapshot.fileContentId();
saveSnapshotPreviewInLibrary(title);
}
// TODO: dup code, same in mrk_create_snapshot
//------------------------------------------------------------------------------------------------------------------------
void SnapshotDialog::saveSnapshotPreviewInLibrary(const QString &snapshotTitle)
{
QString dtg = DTG::toStringMSec(QDateTime::currentMSecsSinceEpoch());
dtg.replace(QLatin1Char(' '), QLatin1Char('_'));
const QString title = QStringLiteral("Snapshot_%1_%2_%3")
.arg(dtg)
.arg(LtsSessionData::LtsSessionData::getInstance()->getOperatorId())
.arg(snapshotTitle.left(20));
DEBUG_LOG(m_logger) << "saveSnapshotPreviewInLibrary: " << title;
auto spSitMapGuiIfc = m_sitMapGuiIfc.lock();
if (!spSitMapGuiIfc->isMapReady())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " sitMapGuiInterface not ready";
return;
}
LtsWidgets::LtsBaseLayoutWidget *blw = m_session->baseLayout();
bool menuVisible = blw->isMenuVisible();
bool topBarVisible = blw->isTopBarVisible();
// make room for a full sized snapshot by removing the top bar, the detail bar and the menu - reshow after the image was successfully created
blw->setMenuVisible(false);
blw->setTopBarVisible(false);
blw->hideVisibleLayouts();
std::function<void(const QImage &)> onImage = [this, title, menuVisible, topBarVisible](const QImage & image)
{
int initialFolderId = 1;
int snapshotFolderId = 1;
// folder_id = 1 -> Global documents as initial folder for admin session
if (m_session->exerciseId() > 0)
{
initialFolderId = m_flmDtc->getExerciseFolderId(m_session->exerciseId());
snapshotFolderId = m_flmDtc->findFolder(initialFolderId, QStringLiteral("Snapshots"));
}
DEBUG_LOG(m_logger) << "Initial folder Id = " << initialFolderId
<< " snapshotFolderId =" << snapshotFolderId;
const int folderId = snapshotFolderId > 0 ? snapshotFolderId : initialFolderId;
FileManagement::FileContentModifyRequest request;
request.id(m_currentSnapshot.fileContentId());
request.name(title.toStdString());
request.isTemplate(false);
request.folderId(folderId);
request.fileCategory(FileManagement::FileCategory::Exercise);
request.fileStatus(FileManagement::FileStatus::Valid);
request.operatorId(LtsSessionData::LtsSessionData::getInstance()->getOperatorId());
bool bIsPng;
request.content() = LtsUtils::fromImage(image, &bIsPng);
request.fileType(bIsPng ? FileManagement::FileType::Png : FileManagement::FileType::Jpg);
request.isUsedBy(true);
request.usedBy(1);
request.lastModified(QDateTime::currentMSecsSinceEpoch());
if (m_session->exerciseId() > 0)
request.exerciseId(m_session->exerciseId());
if (m_requestHelper->startSingleRequest([this, request]()
{
return m_flmFtc->fileContentModifyRequest(request, this);
}))
{
m_fileId = m_requestHelper->getRepliedId();
DEBUG_LOG(m_logger) << LOGFUNCTION << " Saved file in library with id = " << m_fileId;
saveSnapshotFilter();
if (m_currentSnapshot.fileContentId() != m_fileId)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " One more request needed!"; // should never happen at snapshot_dialog.cpp/modification
m_currentSnapshot.fileContentId(m_fileId);
}
} else {
ERROR_LOG(m_logger) << LOGFUNCTION << " Saved file in library with id = " << m_fileId << " failed";
}
// done!
LtsWidgets::LtsBaseLayoutWidget *blw = m_session->baseLayout();
blw->setMenuVisible(menuVisible);
blw->setTopBarVisible(topBarVisible);
blw->resetLayoutVisibility();
};
if (!spSitMapGuiIfc->toImage(this, onImage)) // => async
{
// reset directly after toImage failed
blw->setMenuVisible(menuVisible);
blw->setTopBarVisible(topBarVisible);
blw->resetLayoutVisibility();
return;
}
}
} // namespace MarkerManagement