#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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 srpIfc; if (this->m_replayAppSession == nullptr || !m_replayAppSession->getForeignGuiInterface(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 &session, const QPointer &replayAppSession, const std::shared_ptr &avmIfc, const std::shared_ptr &mrkIfc, const std::shared_ptr &fltParticipantIfc, const std::shared_ptr &fltAesIfc, const std::shared_ptr &fltBuildingComponentIfc, bool isTimelineApp, QWidget *pseudoParent ) { m_itemPtr = std::move(itemPtr); m_item = m_itemPtr.get(); m_isApp = isTimelineApp; m_session = session; std::shared_ptr srpIfc; std::shared_ptr sitMapIfc; if (!m_session->getDtc(m_mrkDtc)) WARNING_LOG(m_logger) << "!m_session->getDtc(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 (%2)").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(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()) { 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("%1").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 srpIfc, std::shared_ptr 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 srpIfc; if (!m_replayAppSession->getForeignGuiInterface(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 sitMapIfc; if (!m_replayAppSession->getForeignGuiInterface(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 sitMapIfc; if (!m_replayAppSession->getForeignGuiInterface(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(m_item->position2D().x())) && qFuzzyIsNull(static_cast(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 &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 srpIfc; std::shared_ptr sitMapIfc; initReplayAppSession(replayAppSession, &srpIfc, &sitMapIfc); enableDisableButtons(srpIfc, sitMapIfc); onStartReplay(); timer->deleteLater(); }); } void ItemInfoDialog::initReplayAppSession(const QPointer &replayAppSession, std::shared_ptr *srpIfc, std::shared_ptr *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(*srpIfc)) WARNING_LOG(m_logger) << "missing SRP-IFC, no replay in TimelineItemInfoDialog"; if (!replayAppSession->getForeignGuiInterface(*sitMapIfc)) WARNING_LOG(m_logger) << "missing SitMap-IFC, no focusing on replay in TimelineItemInfoDialog"; } } } // namespace Timeline