455 lines
15 KiB
C++
Executable File
455 lines
15 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* GUI Plugin for SituationReplay - Replay Control
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
* Created by Patric Karwar, SLSE, 2016-07-19
|
|
*/
|
|
|
|
#include "replay_control_gui_plugin.h"
|
|
#include "replay_control_widget.h"
|
|
#include "replay_control_dialog.h"
|
|
#include "TopicEditor/topic_editor.h"
|
|
|
|
#include <LtsSession/lts_session.h>
|
|
#include <LtsSession/lts_session_data.h>
|
|
#include <SituationReplay/srp_operator_rights.h>
|
|
#include <PresentationManagement/Gui/Script/Common/TakeHomePackageGenerator/export.h>
|
|
#include <Plugins/SituationMapPlugin/situation_map_script_filter_gui_plugin_interface.h>
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
|
namespace SituationReplay
|
|
{
|
|
std::shared_ptr<GuiPluginBase::Interface> GuiPlugin::createGuiInterface(LtsSession::LtsSession *session)
|
|
{
|
|
return std::make_shared<InterfaceImpl>(session);
|
|
}
|
|
|
|
InterfaceImpl::InterfaceImpl(LtsSession::LtsSession *session)
|
|
: GuiPluginBase::Interface(session, getLogger("Plugins.Gui.Srp.ReplayControl"))
|
|
, m_dtc(nullptr)
|
|
, m_ftc(nullptr)
|
|
, m_mrkDtc(nullptr)
|
|
, m_playbackStartTime(-1)
|
|
, m_playbackEndTime(-1)
|
|
, m_playbackSpeed(-1)
|
|
, m_replayWidget(nullptr)
|
|
, m_sitMapIfc(nullptr)
|
|
{}
|
|
|
|
|
|
bool InterfaceImpl::fetchInterfaces()
|
|
{
|
|
if (!m_session->getDtc<DataInterface>(m_dtc))
|
|
{
|
|
DEBUG_LOG(m_logger) << "No Srp Dtc in ReplayControl GUI Plugin with domainId " << m_session->domainId()
|
|
<< " partition " << m_session->partitionId()
|
|
<< " type " << m_session->partitionType();
|
|
return false;
|
|
}
|
|
if (!m_session->getFtc<DataInterface>(m_ftc))
|
|
{
|
|
DEBUG_LOG(m_logger) << "No Srp Ftc in ReplayControl GUI Plugin with domainId " << m_session->domainId()
|
|
<< " partition " << m_session->partitionId()
|
|
<< " type " << m_session->partitionType();
|
|
return false;
|
|
}
|
|
|
|
// Optional:
|
|
if (!m_session->getDtc<MarkerManagement::DataInterface>(m_mrkDtc))
|
|
{
|
|
DEBUG_LOG(m_logger) << "(Optional) No Mrk Data Ifc in ReplayControl GUI Plugin with domainId " << m_session->domainId()
|
|
<< " partition " << m_session->partitionId()
|
|
<< " type " << m_session->partitionType();
|
|
}
|
|
|
|
|
|
LtsSession::Fetcher *fetcher = m_session->fetcher(QStringLiteral("Situation Replay Control"));
|
|
|
|
fetcher->getForeignGuiInterface(m_mrkIfc, QStringLiteral("marker selection in replay control"), true);
|
|
|
|
// if (!m_session->getForeignGuiInterface(m_mrkIfc))
|
|
// {
|
|
// WARNING_LOG(m_logger) << "(Optional) No Mrk Gui Ifc in ReplayControl GUI Plugin with domainId " << m_session->domainId()
|
|
// << " partition " << m_session->partitionId()
|
|
// << " type " << m_session->partitionType();
|
|
// }
|
|
|
|
if (!m_session->getForeignGuiInterface<SituationMap::ForeignGuiInterface>(m_sitMapGuiIfc))
|
|
WARNING_LOG(m_logger) << "No Map Gui Ifc: " << m_session->toString();
|
|
|
|
return fetcher->status();
|
|
}
|
|
|
|
|
|
void InterfaceImpl::clearInterfaces()
|
|
{
|
|
if (m_replayWidget != nullptr)
|
|
m_replayWidget->deleteLater();
|
|
|
|
m_mrkDtc = nullptr;
|
|
m_ftc = nullptr;
|
|
m_dtc = nullptr;
|
|
m_sitMapGuiIfc = nullptr;
|
|
m_mrkIfc = nullptr;
|
|
}
|
|
|
|
|
|
bool InterfaceImpl::startReplay(const qint64 start, const qint64 end, const double speed, const QJsonObject &attributes)
|
|
{
|
|
const QJsonValue mapConfig = attributes[QStringLiteral("settings")];
|
|
if (mapConfig.isNull())
|
|
{
|
|
DEBUG_LOG(m_logger) << "Note: Situation Map Script Filter settings is empty";
|
|
}
|
|
|
|
createReplayDialogIfNeeded();
|
|
|
|
/*DEBUG*/ERROR_LOG(m_logger) << "start = " << start << ", end = " << end << ", speed = " << speed << ", attributes: " << QString::fromUtf8(QJsonDocument(attributes).toJson());
|
|
/*DEBUG*/ERROR_LOG(m_logger) << "state = " << m_replayWidget->getReplayState() << ", replay id = " << m_replayWidget->getId();
|
|
|
|
// always stop & play if start was called, otherwise new start/end/speed values are dismissed
|
|
// if (m_replayDialog->getReplayState() == SrpState_def::Paused)
|
|
// m_replayDialog->resumeReplay();
|
|
// else
|
|
return m_replayWidget->startReplay(start, end, speed);
|
|
}
|
|
|
|
|
|
void InterfaceImpl::pauseReplay()
|
|
{
|
|
createReplayDialogIfNeeded(false);
|
|
m_replayWidget->pauseReplay();
|
|
}
|
|
|
|
|
|
void InterfaceImpl::stopReplay()
|
|
{
|
|
createReplayDialogIfNeeded(false);
|
|
m_replayWidget->stopReplay();
|
|
}
|
|
|
|
|
|
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
|
|
{
|
|
QVector<LtsWidgets::PluginData> ret;
|
|
if (m_session->partitionType() != IDL::PartitionType::Replay)
|
|
return ret;
|
|
|
|
LtsWidgets::PluginData plData;
|
|
|
|
plData.menuGroup = menuGroup();
|
|
|
|
LtsSessionData::LtsSessionData *sd = LtsSessionData::LtsSessionData::getInstance();
|
|
|
|
if (sd->debugFlagValue(QStringLiteral("DoNotCreateInstant.ReplayControl")) == QVariant(true) /* HACK */ &&
|
|
(!sd->hasDebugFlag(QStringLiteral("DoNotCreateInstant.ReplayControl.Ignore")) || sd->debugFlagValue(QStringLiteral("DoNotCreateInstant.ReplayControl.Ignore")) == QVariant(false)))
|
|
sd->setDebugFlagValue(QStringLiteral("DoNotCreateInstant.ReplayControl"), false); // HACK: ! - set 'back' to false
|
|
else
|
|
plData.createInstant = LtsWidgets::CreateInstantType::CreateInstantAndAddAction;
|
|
|
|
plData.layoutArea = LtsWidgets::LayoutArea::eDialogTab;
|
|
plData.menuText = tr("Playback Control");
|
|
plData.identifier = QLatin1String("PlaybackControlDialog");
|
|
plData.menuIcon = QIcon(QLatin1String("icon:load.svg"));
|
|
plData.exercisePart = LtsWidgets::ExercisePart::eAfterAction;
|
|
plData.createWidgetFunc = [this](QWidget *parent)
|
|
{
|
|
return onShowReplayControlDialog(parent);
|
|
};
|
|
ret.append(plData);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
QString InterfaceImpl::menuGroup() const
|
|
{
|
|
return tr("Playbacks");
|
|
}
|
|
|
|
|
|
// private
|
|
void InterfaceImpl::createReplayDialogIfNeeded(bool useExistingReplay)
|
|
{
|
|
if (m_replayWidget != nullptr)
|
|
return;
|
|
|
|
m_replayWidget = qobject_cast<SituationReplayControlWidget *>(createControlWidget(useExistingReplay));
|
|
}
|
|
|
|
|
|
QWidget *InterfaceImpl::createControlWidget(bool useExistingReplay)
|
|
{
|
|
auto situationReplayControlWidget = new SituationReplayControlWidget;
|
|
bool r = situationReplayControlWidget->init(m_session, m_dtc, m_ftc, m_mrkDtc, m_sitMapGuiIfc, m_session->partitionId(), useExistingReplay);
|
|
|
|
if (!r)
|
|
{
|
|
situationReplayControlWidget->deleteLater();
|
|
situationReplayControlWidget = nullptr;
|
|
}
|
|
// else
|
|
// {
|
|
// m_replayWidgetList.append(situationReplayControlWidget);
|
|
// connect(situationReplayControlWidget, &QObject::destroyed,
|
|
// this, [this, situationReplayControlWidget]()
|
|
// {
|
|
// m_replayWidgetList.removeAll(situationReplayControlWidget);
|
|
// });
|
|
// }
|
|
|
|
return situationReplayControlWidget;
|
|
}
|
|
|
|
|
|
LtsWidgets::LtsDialog *InterfaceImpl::onShowReplayControlDialog(QWidget *)
|
|
{
|
|
// NOTE: One Instance of this dialog for a session as this IS a replay session. Exactly one session.
|
|
createReplayDialogIfNeeded();
|
|
return new SituationReplayControlDialog(m_replayWidget);
|
|
}
|
|
|
|
|
|
TopicEditorBase::TopicEditor *InterfaceImpl::createTopicEditor(const TopicEditorBase::TopicData &data)
|
|
{
|
|
MarkerManagement::MarkerManagementDialogModel *markerModel = nullptr;
|
|
|
|
if (m_mrkIfc)
|
|
markerModel = m_mrkIfc->createMarkerModel();
|
|
else
|
|
WARNING_LOG(m_logger) << "No Mrk Ifc for SitRep Topic Editor";
|
|
|
|
if (m_essentialsViewSrpIfc == nullptr)
|
|
WARNING_LOG(m_logger) << "essentials view's srp ifc not available - no preview available";
|
|
if (m_applyFilter == nullptr)
|
|
WARNING_LOG(m_logger) << "m_applyFilter == nullptr";
|
|
SituationReplayEditor *editor = new SituationReplayEditor(markerModel, m_applyFilter,
|
|
m_session, nullptr, &m_essentialsViewSrpIfc, &m_essentialsViewSitMapIfc);
|
|
QObject::connect(this, &InterfaceImpl::signalEssentialsViewChanged, editor, &SituationReplayEditor::onEssentialsViewChanged);
|
|
editor->setTopicData(data);
|
|
return editor;
|
|
}
|
|
|
|
|
|
void InterfaceImpl::setSitMapIfc(SituationMap::ForeignGuiInterface *sitMapIfc)
|
|
{
|
|
m_sitMapIfc = sitMapIfc;
|
|
}
|
|
|
|
|
|
void InterfaceImpl::setEssentialsViewInterfaces(ForeignGuiInterface *srpIfc, SituationMap::ForeignGuiInterface *sitMapIfc)
|
|
{
|
|
m_essentialsViewSrpIfc = srpIfc;
|
|
m_essentialsViewSitMapIfc = sitMapIfc;
|
|
emit signalEssentialsViewChanged(m_essentialsViewSrpIfc, m_essentialsViewSitMapIfc);
|
|
}
|
|
|
|
|
|
void InterfaceImpl::setApplyFilter(const QPointer<MarkerManagement::MarkerManagementApplyFilter> &applyFilter)
|
|
{
|
|
DEBUG_LOG(m_logger) << "setApplyFilter";
|
|
m_applyFilter = applyFilter;
|
|
}
|
|
|
|
|
|
bool InterfaceImpl::playbackInit(const QJsonObject& attributes)
|
|
{
|
|
QJsonDocument doc(attributes);
|
|
DEBUG_LOG(m_logger) << "attributes=" << doc.toJson(QJsonDocument::Compact);
|
|
|
|
// load map settings (if any available)
|
|
const QJsonValue mapConfig = attributes[QStringLiteral("settings")];
|
|
|
|
if (!mapConfig.isNull()) // maybe a pure replay topic, not a situation topic which contains map settings, etc.
|
|
{
|
|
//m_oldMapData = m_sitMapIfc->operatorMapSettings();
|
|
m_sitMapIfc->setOperatorMapSettings(mapConfig);
|
|
}
|
|
else
|
|
{
|
|
WARNING_LOG(m_logger) << "Note: map settings is empty";
|
|
}
|
|
|
|
// load map filters (if any available)
|
|
const QJsonObject mapFilters = attributes[QStringLiteral("filters")].toObject();
|
|
|
|
if (m_applyFilter != nullptr && !mapFilters.isEmpty())
|
|
{
|
|
//m_oldFilterData = m_applyFilter->getFilterParamsJson();
|
|
m_applyFilter->applyFilter(mapFilters);
|
|
}
|
|
else
|
|
{
|
|
if (m_applyFilter == nullptr)
|
|
WARNING_LOG(m_logger) << "ApplyFilter missing for ReplayControl TopicReplayInterface";
|
|
else if (mapFilters.isEmpty())
|
|
WARNING_LOG(m_logger) << "Note: filter settings is empty";
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "Replay Control playbackInit";
|
|
|
|
m_playbackStartTime = attributes.value(QStringLiteral("start")).toDouble();
|
|
m_playbackEndTime = attributes.value(QStringLiteral("end")).toDouble();
|
|
m_playbackSpeed = attributes.value(QStringLiteral("speed")).toDouble();
|
|
|
|
bool result = (m_playbackStartTime != 0 && m_playbackEndTime != 0 && m_playbackSpeed != 0);
|
|
if (!result)
|
|
WARNING_LOG(m_logger) << "Invalid data for topic replay: start " << m_playbackStartTime
|
|
<< " end " << m_playbackEndTime
|
|
<< " speed " << m_playbackSpeed;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
bool InterfaceImpl::playbackStart(int secondsOffset)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Playback start: m_playbackStartTime = " << m_playbackStartTime << ", offset = " << secondsOffset << ", speed = " << m_playbackSpeed;
|
|
return startReplay(m_playbackStartTime + ((secondsOffset * m_playbackSpeed) * 1000), 0 /* infinite */, m_playbackSpeed);
|
|
}
|
|
|
|
|
|
bool InterfaceImpl::playbackPause()
|
|
{
|
|
pauseReplay();
|
|
return true;
|
|
}
|
|
|
|
|
|
//void InterfaceImpl::resetMap() <= Do not reset map as map filters are not going to be reversed once set, because the transition from one to another filter would otherwise be very ugly - kind of a hacky workaround
|
|
//{
|
|
// if (m_sitMapIfc != nullptr)
|
|
// {
|
|
// if (!m_oldMapData.isNull() && !m_oldMapData.isUndefined())
|
|
// m_sitMapIfc->setOperatorMapSettings(m_oldMapData); // reset map settings
|
|
// }
|
|
// else
|
|
// {
|
|
// WARNING_LOG(m_logger) << "SitMapIfc missing for SitMapScriptFilter TopicReplayInterface";
|
|
// }
|
|
|
|
|
|
// if (m_applyFilter != nullptr)
|
|
// {
|
|
// if (!m_oldFilterData.isNull() && !m_oldFilterData.isUndefined())
|
|
// m_applyFilter->applyFilter(m_oldFilterData.toObject()); // reset map filters
|
|
// }
|
|
// else
|
|
// {
|
|
// WARNING_LOG(m_logger) << "ApplyFilter missing for ReplayControl TopicReplayInterface";
|
|
// }
|
|
//}
|
|
|
|
|
|
|
|
// TopicReplayInterface
|
|
InterfaceImpl::TopicReplayInterface::TopicReplayInterface(TopicEditorBase::TopicType type, InterfaceImpl *replayControl) // 1:1
|
|
: m_replayControl(replayControl)
|
|
, m_type(type)
|
|
{}
|
|
|
|
|
|
InterfaceImpl::TopicReplayInterface::~TopicReplayInterface()
|
|
{
|
|
m_replayControl->stopReplay();
|
|
//m_replayControl->resetMap();
|
|
}
|
|
|
|
|
|
// Script Playback Controls:
|
|
bool InterfaceImpl::TopicReplayInterface::playbackInit(const QJsonObject &attributes)
|
|
{
|
|
return m_replayControl->playbackInit(attributes);
|
|
}
|
|
|
|
|
|
bool InterfaceImpl::TopicReplayInterface::playbackStart(int secondsOffset)
|
|
{
|
|
return m_replayControl->playbackStart(secondsOffset);
|
|
}
|
|
|
|
|
|
bool InterfaceImpl::TopicReplayInterface::playbackPause()
|
|
{
|
|
return m_replayControl->playbackPause();
|
|
}
|
|
|
|
|
|
bool InterfaceImpl::TopicReplayInterface::exportTopic(const QJsonObject &attributes,
|
|
const QString &folderPath,
|
|
TopicEditorBase::ExportNotifier *pExportNotifier)
|
|
{
|
|
qint64 playbackStartTime_ms = attributes.value(QStringLiteral("start")).toDouble();
|
|
qint64 playbackEndTime_ms = attributes.value(QStringLiteral("end")).toDouble();
|
|
|
|
// filter?
|
|
if (playbackStartTime_ms == 0 && playbackEndTime_ms == 0)
|
|
{
|
|
pExportNotifier->setExportFailed();
|
|
return false;
|
|
}
|
|
|
|
const QString attributesFileName = QStringLiteral("sitreplay.json");
|
|
QFile f(folderPath + attributesFileName);
|
|
f.open(QIODevice::WriteOnly);
|
|
f.write(QJsonDocument(attributes).toJson(QJsonDocument::Indented));
|
|
f.close();
|
|
|
|
const QString strFileName = QStringLiteral("sitreplay.sqlite");
|
|
bool success = TakeHomePackageGenerator::exportSituationMapData(
|
|
m_replayControl->getSession()->exerciseId(), folderPath + strFileName, playbackStartTime_ms, playbackEndTime_ms
|
|
);
|
|
if (success)
|
|
pExportNotifier->setExportSuccessful(strFileName);
|
|
else
|
|
pExportNotifier->setExportFailed();
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
QVector<TopicEditorBase::TopicData> InterfaceImpl::TopicReplayInterface::additionalTrackEntries(const QJsonObject &attributes, bool &keepOriginalTopic) const
|
|
{
|
|
keepOriginalTopic = true;
|
|
|
|
if (m_type != TopicEditorBase::TopicType::Situation) // combined topic, including map filter settings that - filter settings need to be processed by its parent gui's additionalTrackEntries method
|
|
return {}; // no additional tes if this is a simple replay topic
|
|
|
|
keepOriginalTopic = false; // as the situation topic is not a real topic by itself, don't keep it!
|
|
|
|
// the 'situation' topic is a 3 in one topic - map, replay, filter - so the other 2 have to be returned here for thp to recognize
|
|
|
|
QVector<TopicEditorBase::TopicData> result;
|
|
|
|
// first, add the simple map topic:
|
|
TopicEditorBase::TopicData data;
|
|
data.topicType = TopicEditorBase::TopicType::SituationMap;
|
|
result.append(data);
|
|
|
|
|
|
// get processed filter topics
|
|
std::shared_ptr<SituationMap::ScriptFilterForeignGuiInterface> filterIfc;
|
|
if (!m_replayControl->m_session->getForeignGuiInterface<SituationMap::ScriptFilterForeignGuiInterface>(filterIfc))
|
|
{
|
|
ERROR_LOG(m_replayControl->m_logger) << "sitmap filter ifc missing - map filters for thp will be missing!";
|
|
return result;
|
|
}
|
|
|
|
QScopedPointer<TopicReplay::Interface> topicIfc(filterIfc->createInterface(TopicEditorBase::TopicType::SituationMapFilter));
|
|
if (topicIfc == nullptr)
|
|
{
|
|
ERROR_LOG(m_replayControl->m_logger) << "sitmap filter topic replay interface cannot be created - map filters will not be available in thp!";
|
|
return result;
|
|
}
|
|
|
|
result += topicIfc->additionalTrackEntries(attributes);
|
|
return result;
|
|
}
|
|
|
|
} // namespace SituationReplay
|