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

452 lines
17 KiB
C++
Executable File

#pragma once
#include "tln_abstract_event_factory.h"
#include "tln_participant_track.h"
#include "tln_party_track.h"
#include "tln_timeline_delegate.h"
#include "EventTypes/Block/tln_block_item.h"
#include <LtsTimeline/item.h>
#include <LtsTimeline/timeline_model.h>
#include <LtsTimeline/timeline_utils.h>
#include <LtsTimeline/timeline_view.h>
#include <LtsWidgets/lts_column_filter_proxy_model.h>
#include <ParticipantManagement/ppm.hpp>
#include <Plugins/AudioVideoManagement/avm_data_plugin_interface.h>
#include <Plugins/AudioVideoManagement/avm_menu_edit_gui_plugin_interface.h>
#include <AudioVideoManagement/Shared/stream_list_result.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QMutex>
#include <QPainter>
#include <QSqlDatabase>
#include <QStyleOptionViewItem>
#include <QUuid>
#include <memory> // unique ptr
#include <functional>
#include <type_traits> // std::enable_if => type
namespace LtsSession { class LtsSession; }
namespace ParticipantManagement { namespace Interface { class Dtc; } }
namespace MarkerManagement
{
namespace Interface { class Dtc; }
class Marker;
}
namespace InfrastructureManagement { namespace Interface { class Dtc; } }
namespace LtsTimeline { class TimelineView; }
namespace TimeLineManagement { class ForeignDataInterface; }
namespace ExerciseEventManagement { class Event; }
using Ticks = LtsTimeline::Ticks;
using namespace LtsTimeline;
typedef QVector<QVariantMap> Rows;
typedef QVariantMap Row;
namespace Timeline
{
class TimelineDialog;
class TimelineModelUpdate;
class TimelineProxyModel;
class BuildingHitsTrack;
class TimelineModel;
/*
class GetOfflineVideosProcess : public QObject
{
Q_OBJECT
public:
GetOfflineVideosProcess(const QMap<QString, QString> &streams, TimelineModel *model, QObject *parent);
private slots:
void next();
signals:
void signalDone();
private:
QMap<QString, QString> m_streams;
TimelineModel *m_model;
QMap<QString, QString>::const_iterator m_streamsIt;
int m_open { 0 };
};
*/
class TimelineTableInfos
{
public:
static TimelineTableInfos *getInstance();
void insertTable(const QString &tableName)
{
m_tableNames += tableName;
}
void removeTable(const QString &tableName)
{
m_tableNames.removeAll(tableName);
}
const QStringList &tableNames() const { return m_tableNames; }
private:
friend struct TimelineTableInfosHandler;
QStringList m_tableNames;
};
class TimelineModel : public LtsTimeline::TimelineModel
{
Q_OBJECT
friend class TimelineModelUpdate;
friend class TimelineSelectHandler;
// friend class GetOfflineVideosProcess;
public:
static QString s_eemEventColumns;
static const int s_maxRows{1000};
static int getInt(const Row &row, const QString &column) noexcept(false);
static qint64 getLongLong(const Row &row, const QString &column) noexcept(false);
static QJsonObject getJsonObject(const Row &row, const QString &column, bool ignoreError = false) noexcept(false);
static double getJsonDouble(const Row &row, const QString &column) noexcept(false);
static const EventItem &getEventItem(const ItemPtrNCVRef &itemPtr) { return static_cast<const EventItem &>(*itemPtr.get()); }
static const EventItem &getEventItem(const EventItemPtr &eventItemPtr) { return *eventItemPtr; }
explicit TimelineModel(LtsSession::LtsSession *session, TimelineDialog *dialog, qint64 exerciseStart, qint64 exerciseEnd, QObject *parent = nullptr) noexcept(false);
~TimelineModel();
void initEventFactories(qint64 exerciseStart, qint64 exerciseEnd);
void clearUnusedTempTables();
bool canDropItem(const LtsTimeline::Item &item, int trackId, qint64 start) const override;
bool canMoveItem(const LtsTimeline::Item &item, qint64 start) const override;
bool canResizeItem(const LtsTimeline::Item &item, qint64 start, int duration) const override;
TrackType trackType(const LtsTimeline::Track &t) const;
TrackType trackType(int trackId) const;
bool isBlockItem(int itemId) const;
bool hasBlockItems(const QVector<int> &itemIds) const;
EventItemPtr createItem(int id, qint64 sampleTime, QJsonObject properties /* TODO: copy elision? */,
int eventTypeId, int participantId, int partyId, int eqId, double posX, double posY, double posZ) noexcept(false);
EventItemPtr createItemFromRow(const Row &row);
std::vector<EventItemPtr> createItemsFromRows(const Rows &rows);
std::vector<EventItemPtr> getMarkerItems(const Filters &filters, bool rangeOnly = false, int limit = s_maxRows) noexcept(false);
int findTrack(const AbstractEventItem &_eventItem);
int handleCopyCollision(const Track &t, const int copyItemId, const int itemId, const qint64 start, const qint64 duration) noexcept(false);
int handleCollision(Track &t, ItemPtr &&itemPtr, const int itemId, const qint64 start, const qint64 duration) noexcept(false);
void insertCopyItem(Track &t, const int copyItemId) noexcept(false);
void insertItem(Track &t, ItemPtr &&itemPtr) noexcept(false);
void insertItem(ItemPtr &&itemPtr);
void insertItems(EventItemPtrs &eventItemPtrs);
QString cnrStream(int id) const { return m_cnrStreams.value(id); }
QMap<int, QString> cnrStreams() { return m_cnrStreams;}
QString tvcStream(int id) const { return m_tvcStreams.value(id); }
QMap<int, QString> tvcStreams() { return m_tvcStreams;}
/**
* @brief getEventsByFilter NOTE: ignores filter's active state!
* @param filters
* @return
*/
std::vector<EventItemPtr> getEventsByFilter(const QVector<AbstractEventFilter *> &filters) noexcept(false);
// // :'(
// std::vector<EventItemPtr> getEventsBehindBlockItemBuildings(int tickId, const QVector<AbstractEventFilter *> &filters) noexcept(false); // HitBuilding only (for *Root*-Buildings-Track only!!!)
// std::vector<EventItemPtr> getEventsBehindBlockItemBuilding(int tickId, int mountingPointId, const QString &streamName, const QVector<AbstractEventFilter *> &filters) noexcept(false); // HitBuilding only (for specific Building's Track!!!)
// std::vector<EventItemPtr> getEventsBehindBlockItemTVC(int tickId, const QVector<AbstractEventFilter *> &filters) noexcept(false); // ROOT
// std::vector<EventItemPtr> getEventsBehindBlockItemTVC(int tickId, const QString &streamName, const QVector<AbstractEventFilter *> &filters) noexcept(false); // SPECIFIC
// std::vector<EventItemPtr> getEventsBehindBlockItemCNR(int tickId, const QVector<AbstractEventFilter *> &filters) noexcept(false); // ROOT
// std::vector<EventItemPtr> getEventsBehindBlockItemCNR(int tickId, const QString &streamName, const QVector<AbstractEventFilter *> &filters) noexcept(false); // SPECIFIC
// std::vector<EventItemPtr> getEventsBehindBlockItemBuildingsVideo(int tickId, const QVector<AbstractEventFilter *> &filters) noexcept(false); // the "Video" track
// std::vector<EventItemPtr> getEventsBehindBlockItemBuildingVideo(int tickId, int msei, const QVector<AbstractEventFilter *> &filters) noexcept(false); // the Building track
// std::vector<EventItemPtr> getEventsBehindBlockItemCameraVideo(int tickId, int mountingPointId, const QString &streamName, const QVector<AbstractEventFilter *> &filters) noexcept(false); // the Stream track
// std::vector<EventItemPtr> getEventsBehindBlockItemHitBuildingsRoot(int tickId, const QVector<AbstractEventFilter *> &filters) noexcept(false); // the "Buildings" track
// std::vector<EventItemPtr> getEventsBehindBlockItemHitBuilding(int tickId, int msei, const QVector<AbstractEventFilter *> &filters) noexcept(false); // the Building track
// std::vector<EventItemPtr> getEventsBehindBlockItemDefaultParty(int tickId, int partyId, const QVector<AbstractEventFilter *> &filters) noexcept(false); // the rest ... ROOT
// std::vector<EventItemPtr> getEventsBehindBlockItemDefault(int tickId, int participantId, const QVector<AbstractEventFilter *> &filters) noexcept(false); // the rest ... PARTICIPANT
// // </ :'( >
// std::vector<EventItemPtr> getEventsBehindBlockItem(int itemId) noexcept(false);
qint64 currentEventWidthTime() const { return m_zeroDurationItemWidthTime; }
void test_getEventsByFilter();
void clearAlmostAllItems();
QHash<EventType, AbstractEventFactory *> &eventFactories() { return m_eventFactories; }
AbstractEventFactory *eventFactory(EventType eventType) { return m_eventFactories.value(eventType, nullptr); }
QHash<EventType, AbstractEventFilter *> &filters() { return m_filter; }
AbstractEventFilter *filter(EventType eventType) { return m_filter.value(eventType, nullptr); }
AbstractEventFilter *tempFilter(EventType eventType) { return m_tempFilter.value(eventType, nullptr); }
void update(bool fake = false);
const QVector<qint64> markerLines() const { return m_modelState.markerTimes; }
bool knownRange(qint64 &startOut, qint64 &endOut) const;
bool isCurrentlyUpdating() const { return m_isCurrentlyUpdating; }
void queueUpdate();
void setLive(bool live) { m_isLive = live; if (!live) m_forceRebuildFilter = true; }
bool isLive() const { return m_isLive; }
public slots:
void onRangeChanged(qint64 start, qint64 end);
void onCurrentTimeChanged(qint64 time);
void onZoomChanged(LtsTimeline::ZoomFactor zoom);
void onUpdate() { update(); }
void onFilterChanged();
signals:
void updateStatusChanged(bool running);
void errorCountExceeded();
void thingy(int i, bool result);
void initializationFinished(); // NOTE: emit queued, you will have enough time to connect to this signal
//#warning // TODO: private
/*private*/ public slots:
void onMarkerChanged(const MarkerManagement::Marker &marker, DataModification mod);
void onLiveEvent(const ExerciseEventManagement::Event &eemEvent);
private slots:
void onStreamListReceived(const QMap<QString, QString> &streamList);
private:
struct Party
{
int id;
int trackId;
Party(int id, int trackId) : id(id), trackId(trackId) {}
};
struct Participant
{
int participantId { 0 };
int trackId { 0 };
int superiorId { 0 };
int equipmentNumber { 0 };
int partyId { 0 };
QString text;
QVector<int> inferiorIds;
Participant() = default;
Participant(int participantId, int equipmentNumber, int partyId, const QString &text, int trackId = 0, int superiorId = 0, const QVector<int> &inferiorIds = {})
: participantId(participantId)
, trackId(trackId)
, superiorId(superiorId)
, equipmentNumber(equipmentNumber)
, partyId(partyId)
, text(text)
, inferiorIds(inferiorIds)
{}
};
struct Range
{
qint64 start { 0 };
qint64 end { 0 };
};
struct ModelState
{
QString publicTableName;
//#warning // TODO: ! publish to singleton thingy
QString dbUuid = QUuid::createUuid().toString(QUuid::Id128);
// the event view is a temporary database table holding filtered EEM-Event data
qint64 tempRangeStart { 0 };
qint64 tempRangeEnd { 0 };
// start and end correspond to what time interval can currently be seen on the timeline
qint64 start { 0 };
qint64 end { 0 };
// knownStart and knownEnd is the time interval for which data is inside the model
//QVector<Range> knownRanges;
qint64 knownStart { 0 };
qint64 knownEnd { 0 };
int markerTrackId { -1 }; // TODO: move me and others into a "ModelState" class, so I can be reset with only a recreation of the class !!
QVector<qint64> markerTimes;
QHash<int/*participantId*/, int/*trackId*/> participantTracks;
QMap<int/*participantId*/, Participant> participants;
QMap<int/*partyId*/, Party> partys;
int hitBuildingsRootTrackId { -1 };
int buildingRootTrackId { -1 };
QMap<int /* buildingId (MSEI) */, int /* trackId */> buildingTrackIds;
//QMap<QPair<int /* buildingId (MSEI) */, QString /* internal stream name */>, int /* trackId */> buildingStreamsTrackIds;
// QMap<QPair<int /* buildingId */, QString /* internalStreamName */>, int /* trackId */> buildingStreamTracks;
// int cnrRootTrackId { -1 };
// QMap<QString /* internalStreamName */, int /* trackId */> cnrTrackIds;
// int tvcRootTrackId { -1 };
// QMap<QString /* internalStreamName */, int /* trackId */> tvcTrackIds;
};
void updateFilterTable();
void addParticipantTrack(Participant &participant, int superiorTrackId);
void getParticipantTracks();
//void getBuildingTracks();
void getRadioTracks();
QVector<QVariantMap> /* aka. Rows */ executeQuery(const QString &query, bool ignoreError = false) const noexcept(false);
QVector<QVariantMap> /* aka. Rows */ executeQuery(const QString &query, const QSqlDatabase &db, bool ignoreError = false) const noexcept(false);
bool isCollision(qint64 startTime, qint64 previousStartTime, qint64 previousDuration);
void tryUpdate();
QJsonObject eventPropertiesFromMarker(const MarkerManagement::Marker &marker);
void reChannelSumEvents(int trackId, int onlyThisTick) noexcept(false);
void backwardsChannelSumEvents(const Track &t);
void channelSumEvents();
void sumEvents(Track &track);
//int megaParent(int trackId) noexcept(false);
//QModelIndex megaParentModelIndex(int trackId) noexcept(false);
QVector<int> findItemIdsForDbId(int dbId) noexcept(false);
EventItem &itemWithMarkerDbId(int dbId) noexcept(false);
// because bad deduction by compiler
template <typename ItemClass>/*, typename = typename std::enable_if<std::is_base_of<LtsTimelineItem, ItemClass>::value, ItemClass>::type>*/
int addItem(int parentTrackId, ItemClass &&item) noexcept(false)
{
return LtsTimeline::TimelineModel::addItem(parentTrackId, std::move(item));
}
void getMarkers() noexcept(false);
void getOfflineVideoTracks() ; //Same than the other but give back list of added streams
// void getOfflineVideos() noexcept(false);
// void addOfflineVideosToTimeline();
BuildingHitsTrack createBuildingHitsTrack();
void getBuildings();
void getBuildingVideoStreams();
QVector<int /* msei */> m_mainStructureElements;
QMap<int /* mpi */, int /* msei */> m_mountingPointsToMainStructureElementMapping;
QVector<int /* mpi */> m_buildingVideoStreams;
QMap<QPair<int /* buildingId (MSEI) */, QString /* internal stream name */>, int /* trackId */> m_buildingStreamsTracks;
// offline videos
QMap<QString, QString> m_offlineVideoDisplayNames;
QMap<QString /* internal stream name */, int> m_offlineVideoTracks;
// Outdoor Cameras
int m_outdoorCameraRootTrackId { -1 };
QMap<int/*mounting point id*/, QString/*human name*/> m_outdoorCameraStreams;
QMap<QString/*internal stream name*/, int/*track id*/> m_outdoorCameraTracks;
int m_videosRootTrackId { -1 };
QMap<QString /* stream name */, int /* track id */> m_videoTracks;
// CNR
int m_cnrRootTrackId { -1 };
QMap<QString /* __display__ stream name */, int /* trackId */> m_cnrTrackIds; // yea, avs saves the display name to db, not the internal name ... :)
QMap<int /* channel id */, QString /* display name */> m_cnrChannelDisplayNames;
//QMap<QString /* display name */, int /* channel id */> m_cnrChannelDisplayNamesLookup;
// TVC
int m_tvcRootTrackId { -1 };
QMap<QString /* internalStreamName */, int /* trackId */> m_tvcTrackIds;
QMap<int /* channel id */, QString /* display name */> m_tvcChannelDisplayNames;
template <typename First, typename Second>
static QString keyToString(const QPair<First, Second> &key)
{
return QStringLiteral("%1/%2").arg(key.first).arg(key.second);
}
template <typename Key>
static QString keyToString(const Key &key)
{
return QStringLiteral("%1").arg(key);
}
template <typename ReturnType, typename Container, typename Key>
static ReturnType constFinder(const Container &container, const Key &key) noexcept(false)
{
auto it = container.constFind(key);
if (it == container.constEnd())
throw QStringLiteral("constFinder: cannot find '%1' in container").arg(keyToString(key));
return it.value();
}
LtsSession::LtsSession *m_session { nullptr };
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc { nullptr };
std::shared_ptr<MarkerManagement::Interface::Dtc> m_mrkDtc { nullptr };
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc { nullptr };
std::shared_ptr<TimeLineManagement::ForeignDataInterface> m_eemForeign { nullptr };
std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_avmDtc { nullptr };
std::shared_ptr<AudioVideoManagement::ForeignMenuEditGuiInterface> m_avmIfc { nullptr };
QHash<EventType, AbstractEventFactory *> m_eventFactories;
QHash<EventType, AbstractEventFilter *> m_filter;
public:
QHash<EventType, AbstractEventFilter *> m_tempFilter;
QMutex m_mutex;
private:
QMultiMap<QString, QPair<qint64, qint64>> m_multiMapClipsForStream;
QMap<QString, qint64> m_lastStartTimePerStream;
QString m_filterString;
QSqlDatabase m_db;
ModelState m_modelState;
QTimer *m_updateTimer { nullptr };
QTimer *m_offlineVideoTimer { nullptr };
bool m_updateMutex { false };
bool m_updateResult { true };
bool m_updateQueued { false };
bool m_queueItemDeletion { false }; // if e.g. zoom is changed, all events have to be refetched, but events should only be deleted if the update is actually executing, as queued updates are not guaranteed to be executed
int m_fakeId { 0 };
qint64 m_zeroDurationItemWidthTime { 0 };
static int s_minThreadCount;
static int s_maxThreadCount;
bool m_isCurrentlyUpdating { false };
int m_threads { 1 };
bool m_firstUpdate { true };
int *m_lockyMcLockLock { nullptr };
Rows **m_sharedThing { nullptr };
bool m_forceRebuildFilter { false };
TimelineDialog *m_dialog;
bool m_isLive { false };
bool m_trackFetchedInLiveDone { false };
QMap<int, QString> m_cnrStreams;
QMap<int, QString> m_tvcStreams;
NGL::INGLLogger &m_logger;
};
} // namespace Timeline