Files
qt5-legatus-legacy/Lts/Plugins/Timeline/Gui/EventTimeline/tln_item_info_dialog.cpp
T
2026-08-13 18:33:10 +02:00

557 lines
20 KiB
C++
Executable File

#include "ui_tln_item_info_dialog.h"
#include "tln_item_info_dialog.h"
#include "tln_abstract_event_filter.h"
#include "tln_av_tracks.h"
#include "logger.h"
#include <LtsWidgets/lts_base.h>
#include <ngltools/ngltools_dtg.h>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <Services/ProjectDataManagement/pdm_translation.h>
#include <AudioVideoManagement/stream_name_mapping.h>
#include <Plugins/MarkerManagement/Gui/Common/mrk_apply_filter.h>
#include <MarkerManagement/mrk_data_plugin_interface.h>
#include <Plugins/SituationReplay/srp_gui_plugin_interface.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include <QTimer>
namespace Timeline
{
/**
* @brief
* @details Shows detail information about current Event
* @param parent
*/
ItemInfoDialog::ItemInfoDialog(QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_ui(new Ui::ItemInfoDialog)
, m_logger(getLogger("TimelineItemInfoDialog"))
{
setOnlineHelpFilename(QStringLiteral("Timeline-Items"));
m_ui->setupUi(this);
setUnchangeable();
m_ui->debugWidget->hide();
connect(m_ui->pbStartReplay, &QPushButton::clicked, this, [this](){
std::shared_ptr<SituationReplay::ForeignGuiInterface> srpIfc;
if (this->m_replayAppSession == nullptr ||
!m_replayAppSession->getForeignGuiInterface<SituationReplay::ForeignGuiInterface>(srpIfc))
{
WARNING_LOG(m_logger) << "missing Replay App. emitting start Situation replay app...";
emit startSituationReplayApp();
}
else {
WARNING_LOG(m_logger) << "Replay App exist; only start replay";
onStartReplay();
}
});//&ItemInfoDialog::onStartReplay);
connect(m_ui->pbShowMarker, &QPushButton::clicked, this, &ItemInfoDialog::onShowMarker);
connect(m_ui->pbAudioPlayback, &QPushButton::clicked, this, [this]() { startPlayback(true); });
connect(m_ui->pbVideoPlayback, &QPushButton::clicked, this, [this]() { startPlayback(false); });
connect(m_ui->pbFocus, &QPushButton::clicked, this, &ItemInfoDialog::onFocusOnMap);
m_ui->lbxBuilding->hide();
m_ui->lblBuilding->hide();
m_ui->tbFocus->hide();
m_ui->tbMarker->hide();
m_ui->tbAudioPlayback->hide();
m_ui->tbVideoPlayback->hide();
m_ui->tbReplay->hide();
#ifndef LTS_DEBUG
if (LtsSessionData::LtsSessionData::getInstance()->debugFlagValue(QStringLiteral("Timeline.Debug")) == QVariant(true))
#endif
{
m_ui->debugWidget->show();
}
}
ItemInfoDialog::~ItemInfoDialog()
{
delete m_ui;
}
///////////////////////////////////////////////////////////////////////////////
// PUBLIC
void ItemInfoDialog::retranslate()
{
translate(m_ui, eventTypeString(m_item->type()), false, tr("Event"));
}
bool ItemInfoDialog::init(EventItemPtr &&itemPtr, int equipmentNumber,
const QPointer<LtsSession::LtsSession> &session,
const QPointer<LtsSession::LtsSession> &replayAppSession,
const std::shared_ptr<AudioVideoManagement::ForeignMenuEditGuiInterface> &avmIfc,
const std::shared_ptr<MarkerManagement::ForeignGuiInterface> &mrkIfc,
const std::shared_ptr<FilterManagement::ForeignFltParticipantGuiInterface> &fltParticipantIfc,
const std::shared_ptr<FilterManagement::ForeignAesGuiInterface> &fltAesIfc,
const std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> &fltBuildingComponentIfc,
bool isTimelineApp, QWidget *pseudoParent
)
{
m_itemPtr = std::move(itemPtr);
m_item = m_itemPtr.get();
m_isApp = isTimelineApp;
m_session = session;
std::shared_ptr<SituationReplay::ForeignGuiInterface> srpIfc;
std::shared_ptr<SituationMap::ForeignGuiInterface> sitMapIfc;
if (!m_session->getDtc<MarkerManagement::DataInterface>(m_mrkDtc))
WARNING_LOG(m_logger) << "!m_session->getDtc<MarkerManagement::DataInterface>(m_mrkDtc)";
m_mrkDtc->enableMarker();
connect(m_mrkDtc.get(), &MarkerManagement::Interface::Dtc::markerChanged,
this, &ItemInfoDialog::updateMarker);
initReplayAppSession(replayAppSession, &srpIfc, &sitMapIfc);
m_avmIfc = avmIfc;
m_mrkIfc = mrkIfc;
m_fltParticipantIfc = fltParticipantIfc;
m_fltAesIfc = fltAesIfc;
m_fltBuildingComponentIfc = fltBuildingComponentIfc;
if (m_avmIfc == nullptr)
WARNING_LOG(m_logger) << "missing AVM-IFC";
if (m_mrkIfc == nullptr)
WARNING_LOG(m_logger) << "missing MRK-IFC";
if (m_fltParticipantIfc == nullptr)
WARNING_LOG(m_logger) << "missing FLT-Part-IFC";
if (m_fltAesIfc == nullptr)
WARNING_LOG(m_logger) << "missing FLT-Aes-IFC";
if (m_fltBuildingComponentIfc == nullptr)
WARNING_LOG(m_logger) << "missing FLT-BuildingComponent-IFC";
enableDisableButtons(srpIfc, sitMapIfc);
// hide equipment id labels if event has no eq id
if (equipmentNumber <= 0)
{
m_ui->lblEquipNo->hide();
m_ui->lblValueEquipNo->hide();
}
else
m_ui->lblValueEquipNo->setText(QString::number(equipmentNumber));
connect(pseudoParent, &QWidget::destroyed, this, &ItemInfoDialog::deleteLater);
QString eventTime;
if (m_logger.isDebugEnabled())
eventTime = QStringLiteral("%1 <font color='#aebac6'>(%2)</font>").arg(DTG::toStringMSec(m_item->startTime()), m_item->startTime());
else
eventTime = DTG::toStringMSec(m_item->startTime());
m_ui->lblValueEventTime->setText(eventTime);
m_formCount = m_ui->formLayout->rowCount();
m_item->addItemInfoRows(m_ui->formLayout);
// special case: the building should be above the event time
if (m_item->type() == EventType::HitBuilding)
{
QLabel *label = qobject_cast<QLabel *>(m_ui->formLayout->itemAt(m_ui->formLayout->count() - 1)->widget()); // label with building name
if (label)
m_ui->lblBuilding->setText(label->text());
m_ui->lbxBuilding->show();
m_ui->lblBuilding->show();
m_ui->formLayout->removeRow(m_ui->formLayout->rowCount() - 1); // remove the row with the building
}
// labels can get very long and make the info window unnecessarily wide, so make labels shorter and give them a tooltip with the whole text... (ugly, but works)
for (QLabel *label : findChildren<QLabel *>())
{
if (label->text().length() <= 50)
continue;
QString text(label->text());
text = text.left(50);
text += QStringLiteral("...");
label->setToolTip(label->text());
label->setText(text);
}
m_ui->lblEventType->setText(QStringLiteral("<b>%1</b>").arg(eventTypeString(m_item->type())));
m_ui->pteDetailDataTest->setPlainText(QString::fromUtf8(QJsonDocument(m_item->properties()).toJson(QJsonDocument::Indented)));
m_ui->lblDbgDbId->setText(QString::number(m_item->dbId()));
//QString pos = "X: " + QString::number(m_item->position().x()) + " Y: " + QString::number(m_item->position().y()) + " Z: " + QString::number(m_item->position().z());
m_ui->lblPos->setText(QString::number(m_item->position().x()));
// QString pos(QStringLiteral("x: %1 - y: %2 - z: %3").arg(m_event->position().x()).arg(m_event->position().y()).arg(m_event->position().z()));
// m_ui->lblPos->setText(pos);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// PRIVATE
void ItemInfoDialog::enableDisableButtons(
std::shared_ptr<SituationReplay::ForeignGuiInterface> srpIfc, std::shared_ptr<SituationMap::ForeignGuiInterface> sitMapIfc
)
{
setButtonEnabled(m_ui->pbFocus, m_ui->tbFocus);//Enable if everything is there
if (sitMapIfc == nullptr)
{
m_ui->pbFocus->setEnabled(false);
m_ui->tbFocus->hide();
}
//setButtonDisabled(m_ui->pbFocus, m_ui->tbFocus, tr("No map available"));
if ((m_item->type() == EventType::CameraVideo ||
m_item->type() == EventType::RadioCnr ||
m_item->type() == EventType::RadioTvc) && (m_item->type() != EventType::Marker &&
!m_item->isPositionValid())) // NOTE: markers get their position from the map settings !
setButtonDisabled(m_ui->pbFocus, m_ui->tbFocus, tr("Event has an invalid position and cannot be focused"));
// TODO: check marker map settings validity
if(m_item->type() == EventType::Marker && !isMarkerSituation())
{
setButtonDisabled(m_ui->pbFocus, m_ui->tbFocus, tr("No position available."));
setButtonDisabled(m_ui->pbStartReplay, m_ui->tbReplay, tr("No position available."));
}
if (m_item->type() != EventType::Marker)
setButtonDisabled(m_ui->pbShowMarker, m_ui->tbMarker, tr("This event is not a marker."), false);
else if (m_mrkIfc == nullptr)
setButtonDisabled(m_ui->pbShowMarker, m_ui->tbMarker, tr("This app has no marker interface available."), false);
if (!isAudioEvent(m_item->type()) && !isMarkerAudio())
setButtonDisabled(m_ui->pbAudioPlayback, m_ui->tbAudioPlayback, tr("This event is not an audio type."), false);
else if (m_avmIfc == nullptr)
setButtonDisabled(m_ui->pbAudioPlayback, m_ui->tbAudioPlayback, tr("This app has no audio interface available."), false);
else if (m_item->type() == EventType::Marker && !isMarkerAudio())
setButtonDisabled(m_ui->pbAudioPlayback, m_ui->tbAudioPlayback, tr("This marker event is not an audio type."), false);
else if (m_item->type() != EventType::Marker && !hasStreamName()) // as marker ids are fed into the av ifc, no replay data needs to be fetched at this point
setButtonDisabled(m_ui->pbAudioPlayback, m_ui->tbAudioPlayback, tr("This event has missing informations about the playback."));
if (!isVideoEvent(m_item->type()) && !isMarkerVideo())
setButtonDisabled(m_ui->pbVideoPlayback, m_ui->tbVideoPlayback, tr("This event is not an Video type."), false);
else if (m_avmIfc == nullptr)
setButtonDisabled(m_ui->pbVideoPlayback, m_ui->tbVideoPlayback, tr("This app has no Video interface available."), false);
else if (m_item->type() == EventType::Marker && !isMarkerVideo())
setButtonDisabled(m_ui->pbVideoPlayback, m_ui->tbVideoPlayback, tr("This marker event is not an Video type."), false);
else if (m_item->type() != EventType::Marker && !hasStreamName()) // as marker ids are fed into the av ifc, no replay data needs to be fetched at this point
setButtonDisabled(m_ui->pbVideoPlayback, m_ui->tbVideoPlayback, tr("This event has missing informations about the playback."));
if ((m_item->type() == EventType::CameraVideo ||
m_item->type() == EventType::RadioCnr ||
m_item->type() == EventType::RadioTvc)) // NOTE: markers get their position from the map settings !
{
setButtonDisabled(m_ui->pbFocus, m_ui->tbFocus, tr("Event has an invalid position and cannot be focused"));
}
Q_UNUSED(srpIfc);
}
void ItemInfoDialog::setButtonDisabled(QPushButton *button, QToolButton *warningButton, const QString &reasonMessage, bool hide)
{
Q_UNUSED(hide)
// if (hide)
// button->setVisible(false);
button->setEnabled(false);
button->setToolTip(reasonMessage);
warningButton->setToolTip(reasonMessage);
warningButton->show();
connect(warningButton, &QToolButton::clicked, warningButton, [this, warningButton]()
{
new LtsWidgets::LtsMessageDialogNew(this, warningButton->toolTip());
});
}
void ItemInfoDialog::setButtonEnabled(QPushButton *button, QToolButton *warningButton)
{
button->setVisible(true);
button->setEnabled(true);
button->setToolTip(QStringLiteral(""));
warningButton->hide();
}
bool ItemInfoDialog::getStreamName(QString &streamName, bool silent) const
{
const QJsonObject obj(m_item->properties());
const QString _streamName = obj[QStringLiteral("streamName")].toString(); // Lts/Services/ExerciseEventManagement/web_socket_avs.cpp:160
if (_streamName.isEmpty())
{
if (!silent)
ERROR_LOG(m_logger) << "stream name is empty: " << QJsonDocument(m_item->properties()).toJson();
return false;
}
streamName = _streamName;
return true;
}
bool ItemInfoDialog::hasStreamName() const
{
QString dummy;
return getStreamName(dummy, true);
}
bool ItemInfoDialog::isMarkerAudio() const
{
const QJsonObject obj(m_item->properties());
return obj[QStringLiteral("IsAudioType")].toBool(false);
}
bool ItemInfoDialog::isMarkerVideo() const
{
const QJsonObject obj(m_item->properties());
return obj[QStringLiteral("IsVideoType")].toBool(false);
}
bool ItemInfoDialog::isMarkerAudioVideo() const
{
return (isMarkerAudio() || isMarkerVideo());
}
bool ItemInfoDialog::isAudioVideoCapable()
{
return isAudioVideoEvent(m_item->type()) || m_item->type() == EventType::Marker;
}
bool ItemInfoDialog::isMarkerSituation()
{
const QJsonObject obj(m_item->properties());
return obj[QStringLiteral("IsSituationType")].toBool(false);
}
void ItemInfoDialog::startPlayback(bool isAudio)
{
if (m_item->type() == EventType::Marker)
{
if (!m_avmIfc->startMarkerAudioVideoPlayback(m_item->markerDbId(), isAudio))
new LtsWidgets::LtsMessageDialogNew(this, tr("Marker playback cannot be started because of an error. Please try again later."));
return;
}
QString streamName;
if (!getStreamName(streamName))
{
new LtsWidgets::LtsMessageDialogNew(this, tr("Cannot start playback as this event's informations are missing."));
return;
}
if (!m_avmIfc->startAudioVideoPlayback(streamName, m_item->startTime(), isAudioEvent(m_item->type())))
new LtsWidgets::LtsMessageDialogNew(this, tr("Playback cannot be started because of an error. Please try again later."));
}
///////////////////////////////////////////////////////////////////////////////
// PRIVATE SLOTS
/**
* @brief
* @details Start the Replay if the Replay App is open
*/
void ItemInfoDialog::onStartReplay()
{
if (m_replayAppSession == nullptr)
{
ERROR_LOG(m_logger) << "missing Replay App, no replay in TimelineItemInfoDialog";
return;
}
std::shared_ptr<SituationReplay::ForeignGuiInterface> srpIfc;
if (!m_replayAppSession->getForeignGuiInterface<SituationReplay::ForeignGuiInterface>(srpIfc))
{
m_ui->pbStartReplay->setToolTip(tr("Situation Replay App not started."));
ERROR_LOG(m_logger) << ">>> missing SRP-IFC, no replay in TimelineItemInfoDialog";
return;
}
std::shared_ptr<SituationMap::ForeignGuiInterface> sitMapIfc;
if (!m_replayAppSession->getForeignGuiInterface<SituationMap::ForeignGuiInterface>(sitMapIfc))
{
m_ui->pbStartReplay->setToolTip(tr("This app has no map to focus this event on."));
ERROR_LOG(m_logger) << ">>> missing SitMap-IFC, no focus on replay in TimelineItemInfoDialog 1";
return;
}
if (m_item->type() != EventType::Marker && !m_item->isPositionValid()) // NOTE: markers get their position from the map settings !
{
m_ui->pbStartReplay->setToolTip(tr("Event has an invalid position, cannot be focused and therefore has no replay capability."));
ERROR_LOG(m_logger) << ">>> Event has an invalid position";
return;
}
// TODO: check marker replay data validity
srpIfc->startReplay(m_item->startTime() - (10 * 1000) /* TODO: offset? */); // TODO: endTime..
if(!isAudioVideoEvent(m_item->type()))
onFocusOnMap();
}
void ItemInfoDialog::onShowMarker()
{
if (!m_mrkIfc || m_item->type() != EventType::Marker)
return;
m_mrkIfc->showMarker(m_item->markerDbId());
}
/**
* @brief
* @details Zoom and focus the position of the event if a map/position is available
*/
void ItemInfoDialog::onFocusOnMap()
{
if (m_replayAppSession == nullptr)
{
WARNING_LOG(m_logger) << "missing Replay App, no focus in TimelineItemInfoDialog";
return;
}
std::shared_ptr<SituationMap::ForeignGuiInterface> sitMapIfc;
if (!m_replayAppSession->getForeignGuiInterface<SituationMap::ForeignGuiInterface>(sitMapIfc))
{
WARNING_LOG(m_logger) << "missing SitMap-IFC, no focus on replay in TimelineItemInfoDialog 1";
return;
}
if (!sitMapIfc)
{
WARNING_LOG(m_logger) << "missing SitMap-IFC, no focus on replay in TimelineItemInfoDialog 2";
return;
}
if (!m_item->isPositionValid() && qFuzzyIsNull(static_cast<float>(m_item->position2D().x())) && qFuzzyIsNull(static_cast<float>(m_item->position2D().y())))
{
WARNING_LOG(m_logger) << "event has no valid position, skip onFocusOnMap";
return;
}
// x == lon ; y == lat
sitMapIfc->setCenterPoint(m_item->position2D());
const QJsonObject obj(m_item->properties());
const QJsonValue mapSettings(obj[QStringLiteral("MapSettings")]);
if (!mapSettings.isUndefined())
sitMapIfc->setOperatorMapSettings(mapSettings);
const QJsonValue filterSettings(obj[QStringLiteral("Filter")]);
if (!filterSettings.isUndefined())
{
MarkerManagement::MarkerManagementApplyFilter applyFilter(m_replayAppSession, m_fltParticipantIfc, m_fltAesIfc, m_fltBuildingComponentIfc);
if(!applyFilter.applyMapSettings(mapSettings.toString().toUtf8()))
WARNING_LOG(m_logger) << "map settings for #" << m_item->id() << " could not be applied";
if (!applyFilter.applyFilter(filterSettings.toObject()))
{
WARNING_LOG(m_logger) << "filters from event #" << m_item->id() << " could not be applied";
return;
}
}
}
void ItemInfoDialog::updateMarker(const MarkerManagement::Marker &data, DataModification modType)
{
// the marker desciption changed
if (m_item->type() != EventType::Marker)
{
return;
}
QJsonObject obj = m_item->properties();
QJsonValue val;
DEBUG_LOG(m_logger) << "ShowDetailsDialog::updateMarker"
<< " id=" << data.id()
<< " id Marke=" << m_item->markerDbId()
<< " note=" << data.note();
for(int i=0 ; i < obj.keys().length() ; i++)
{
DEBUG_LOG(m_logger) << "i=" << i
<< " " << (obj.keys().at(i))
<< " value=" << obj[obj.keys().at(i)].toString();
}
if ( data.id() == m_item->markerDbId())
{
QString str = QString::fromStdString (data.note());
val = QJsonValue(str);
obj.insert (QStringLiteral("MarkerNote"), QJsonValue(val));
DEBUG_LOG(m_logger) << "update MarkerNote:" << str ;
DEBUG_LOG(m_logger) << " value=" << obj[QStringLiteral("MarkerNote")].toString();
m_item->setProperties(obj);
QString result = QString::fromUtf8(QJsonDocument(m_item->properties()).toJson(QJsonDocument::Indented));
DEBUG_LOG(m_logger) << "Result=" << result;
m_ui->pteDetailDataTest->setPlainText(QString::fromUtf8(QJsonDocument(m_item->properties()).toJson(QJsonDocument::Indented)));
////////////////////////
/// Update Marker-Note
////////////////////////
for ( int i = m_ui->formLayout->rowCount() -1 ; i >=m_formCount; i--)
{
DEBUG_LOG(m_logger) << "remove row i=" << i;
m_ui->formLayout->removeRow(i);
}
m_item->addItemInfoRows(m_ui->formLayout);
}
}
/**
* @brief
* @details slot start selected situation replay
*/
void ItemInfoDialog::startSituationPlayback(const QPointer<LtsSession::LtsSession> &replayAppSession)
{
QTimer *timer = new QTimer(this);
timer->singleShot(2000, this, [this, timer,replayAppSession]()
{
WARNING_LOG(m_logger) << ">>>>>>> initialization of the Replay session app.";
std::shared_ptr<SituationReplay::ForeignGuiInterface> srpIfc;
std::shared_ptr<SituationMap::ForeignGuiInterface> sitMapIfc;
initReplayAppSession(replayAppSession, &srpIfc, &sitMapIfc);
enableDisableButtons(srpIfc, sitMapIfc);
onStartReplay();
timer->deleteLater();
});
}
void ItemInfoDialog::initReplayAppSession(const QPointer<LtsSession::LtsSession> &replayAppSession,
std::shared_ptr<SituationReplay::ForeignGuiInterface> *srpIfc,
std::shared_ptr<SituationMap::ForeignGuiInterface> *sitMapIfc)
{
if (replayAppSession != nullptr)
{
m_replayAppSession = replayAppSession;
connect(replayAppSession.data(), &LtsSession::LtsSession::destroyed, this, [this]()
{
setButtonDisabled(m_ui->pbFocus, m_ui->tbFocus, tr("No map available."));
m_replayAppSession = nullptr;
WARNING_LOG(m_logger) << "Session replay app deleted!";
//setButtonDisabled(m_ui->pbStartReplay, m_ui->tbReplay, tr("Situation Replay App not started."));
});
if (!replayAppSession->getForeignGuiInterface<SituationReplay::ForeignGuiInterface>(*srpIfc))
WARNING_LOG(m_logger) << "missing SRP-IFC, no replay in TimelineItemInfoDialog";
if (!replayAppSession->getForeignGuiInterface<SituationMap::ForeignGuiInterface>(*sitMapIfc))
WARNING_LOG(m_logger) << "missing SitMap-IFC, no focusing on replay in TimelineItemInfoDialog";
}
}
} // namespace Timeline