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

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