319 lines
10 KiB
C++
Executable File
319 lines
10 KiB
C++
Executable File
/**
|
|
* MainHandlerSqlite for SituationReplay
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
* Created by Hanna E. Rathje, SLSE, 2016-01-13
|
|
*/
|
|
|
|
#include "main_handler_sqlite.h"
|
|
|
|
#include "db_reader.h"
|
|
#include "static_publisher.h"
|
|
#include "handler_situation_replay_sqlite.h"
|
|
#include <DDS/DDSConnector/dds_connector.h>
|
|
#include <ngldb/db_service_handler.h>
|
|
#include <ngldb/nsqlquery.h>
|
|
#include <LtsScriptTimeline/track_entry.h>
|
|
#include <QFile>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QJsonParseError>
|
|
#include <QSet>
|
|
#include <QSqlError>
|
|
|
|
namespace SituationReplay
|
|
{
|
|
MainHandlerSqlite::MainHandlerSqlite()
|
|
: m_curFilterArrayIdx(-1)
|
|
, m_curOverlayArrayIdx(-1)
|
|
, m_handlerSrp(nullptr)
|
|
, m_staticPublisher(nullptr)
|
|
, m_ddsInterface(nullptr)
|
|
, m_dbHandler(nullptr)
|
|
, m_logger(getLogger("MainHandlerSqlite"))
|
|
{}
|
|
|
|
MainHandlerSqlite::~MainHandlerSqlite()
|
|
{
|
|
m_dbSqlite.close();
|
|
m_dbSqlite = QSqlDatabase();
|
|
|
|
m_staticPublisher = nullptr;
|
|
m_dbReader = nullptr;
|
|
m_handlerSrp = nullptr;
|
|
|
|
m_ddsInterface = nullptr;
|
|
m_dbHandler = nullptr;
|
|
}
|
|
|
|
bool MainHandlerSqlite::initDb(const ReplayInformation &replayInformation,
|
|
const QVector<FilterInformation> &filter,
|
|
const QVector<FilterInformation> &overlays)
|
|
{
|
|
if (!m_ddsInterface)
|
|
{
|
|
WARNING_LOG(m_logger) << "dds must be initialized first!";
|
|
return false;
|
|
}
|
|
|
|
m_replay = replayInformation;
|
|
// sort filter & overlays by begin time
|
|
const auto cbSort = [](const FilterInformation &a, const FilterInformation &b)
|
|
{
|
|
if (a.relativeTimeSpan.begin != b.relativeTimeSpan.begin)
|
|
return a.relativeTimeSpan.begin < b.relativeTimeSpan.begin;
|
|
return a.overlayId < b.overlayId;
|
|
};
|
|
m_filter = filter;
|
|
std::sort(m_filter.begin(), m_filter.end(), cbSort);
|
|
m_curFilterArrayIdx = -1;
|
|
|
|
m_overlays = overlays;
|
|
std::sort(m_overlays.begin(), m_overlays.end(), cbSort);
|
|
m_curOverlayArrayIdx = -1;
|
|
|
|
m_staticPublisher = nullptr;
|
|
m_handlerSrp = nullptr;
|
|
m_dbReader = nullptr;
|
|
m_dbHandler = nullptr;
|
|
if (m_dbSqlite.isOpen())
|
|
{
|
|
m_dbSqlite.close();
|
|
m_dbSqlite = QSqlDatabase();
|
|
QSqlDatabase::removeDatabase(QStringLiteral("sqliteDb"));
|
|
}
|
|
// initialize database
|
|
m_dbSqlite = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), QStringLiteral("sqliteDb"));
|
|
m_dbSqlite.setDatabaseName(replayInformation.databaseFilePath);
|
|
if (!m_dbSqlite.open())
|
|
{
|
|
const QString err = m_dbSqlite.lastError().nativeErrorCode();
|
|
FATAL_LOG(m_logger) << "Error opening database '" << replayInformation.databaseFilePath << "'";
|
|
FATAL_LOG(m_logger) << m_dbSqlite.lastError().driverText() << " "
|
|
<< m_dbSqlite.lastError().databaseText() << " " << err;
|
|
return false;
|
|
}
|
|
|
|
const QString stmt(QStringLiteral("SELECT id FROM exercise"));
|
|
NSqlQuery q(stmt, m_dbSqlite);
|
|
const int partitionIdIn = (q.execAndFetch() ? q.value(0).toInt() : 0);
|
|
|
|
m_dbHandler = std::make_shared<DbHandler>();
|
|
if (!m_dbHandler->init(partitionIdIn, m_dbSqlite))
|
|
{
|
|
ERROR_LOG(m_logger) << "Initialisation dbHandler failed.";
|
|
return false;
|
|
}
|
|
DEBUG_LOG(m_logger) << "opening sqlite db successful";
|
|
|
|
m_dbReader = std::make_shared<DbReader>(m_ddsInterface->ddsReplay(), partitionIdIn, false);
|
|
if (!m_dbReader->init(m_dbHandler->db()->database()))
|
|
{
|
|
ERROR_LOG(m_logger) << "Could not initialize database reader";
|
|
return false;
|
|
}
|
|
|
|
// read project specific data from database
|
|
// publish project specific data
|
|
// initialize the specific handler
|
|
if (!initSrpHandler(partitionIdIn, m_ddsInterface->ddsReplay()->getPartitionId(),
|
|
replayInformation.timeSpan.begin, replayInformation.timeSpan.end))
|
|
{
|
|
ERROR_LOG(m_logger) << "Could not initialize situation replay handler";
|
|
return false;
|
|
}
|
|
|
|
if (!initStaticPublisher())
|
|
return false;
|
|
|
|
if (!loadReplayConfig(replayInformation.replayConfigPath))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool MainHandlerSqlite::initDds(const SituationReplay::ConnectionParams &output)
|
|
{
|
|
// initialize DDS
|
|
m_ddsInterface = std::make_shared<DdsHandler>(output);
|
|
if (!m_ddsInterface->init())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
std::shared_ptr<DDSConnector::DdsConnector> MainHandlerSqlite::ddsReplay() const
|
|
{
|
|
return m_ddsInterface->ddsReplay();
|
|
}
|
|
|
|
bool MainHandlerSqlite::initSrpHandler(int partitionIdIn, int partitionIdOut, qint64 beginTime, qint64 endTime)
|
|
{
|
|
m_handlerSrp.reset(new HandlerSituationReplaySqlite(m_ddsInterface, m_dbReader));
|
|
|
|
if (!m_handlerSrp->init(partitionIdIn, partitionIdOut, beginTime, endTime))
|
|
return false;
|
|
|
|
connect(m_handlerSrp.get(), &HandlerSituationReplaySqlite::replayTimeChanged,
|
|
this, &MainHandlerSqlite::onReplayTimeChanged);
|
|
connect(m_handlerSrp.get(), &HandlerSituationReplaySqlite::started,
|
|
this, &MainHandlerSqlite::onReplayTimeMaybeJumped);
|
|
connect(m_handlerSrp.get(), &HandlerSituationReplaySqlite::resumed,
|
|
this, &MainHandlerSqlite::onReplayTimeMaybeJumped);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool MainHandlerSqlite::initStaticPublisher()
|
|
{
|
|
m_staticPublisher.reset(new StaticPublisher(m_ddsInterface, m_dbSqlite));
|
|
connect(m_staticPublisher.get(), &StaticPublisher::fileContentAvailable,
|
|
this, &MainHandlerSqlite::fileContentAvailable);
|
|
return true;
|
|
}
|
|
|
|
bool MainHandlerSqlite::loadReplayConfig(const QString &replayConfigPath)
|
|
{
|
|
QFile f(replayConfigPath);
|
|
if (!f.open(QIODevice::ReadOnly))
|
|
{
|
|
WARNING_LOG(m_logger) << "Could not open '" << replayConfigPath << "' for reading - no replay config loaded";
|
|
return false;
|
|
}
|
|
QJsonParseError error;
|
|
const QJsonDocument doc(QJsonDocument::fromJson(f.readAll(), &error));
|
|
if (error.error != QJsonParseError::NoError)
|
|
{
|
|
WARNING_LOG(m_logger) << "Could not parse replay config loaded json file: " << error.errorString();
|
|
return false;
|
|
}
|
|
const QJsonObject obj = doc.object();
|
|
const double replaySpeed = obj[QStringLiteral("speed")].toDouble(1.0);
|
|
emit initialReplaySpeed(replaySpeed);
|
|
|
|
return true;
|
|
}
|
|
|
|
void MainHandlerSqlite::onReplayTimeChanged(qint64 curTime)
|
|
{
|
|
maybeApplyNextFilter(curTime);
|
|
maybeAddNewOverlay(curTime);
|
|
removeOutdatedOverlays(curTime);
|
|
}
|
|
|
|
void MainHandlerSqlite::onReplayTimeMaybeJumped(qint64 curTime)
|
|
{
|
|
replayTimeMaybeJumpedFilter(curTime);
|
|
replayTimeMaybeJumpedOverlays(curTime);
|
|
}
|
|
|
|
void MainHandlerSqlite::maybeApplyNextFilter(qint64 curTime)
|
|
{
|
|
const int nextIdx = findNextIndex(curTime,
|
|
m_curFilterArrayIdx,
|
|
m_filter);
|
|
if (nextIdx < 0)
|
|
return;
|
|
m_curFilterArrayIdx = nextIdx;
|
|
emit applyCurentFilter(m_filter.at(m_curFilterArrayIdx));
|
|
}
|
|
|
|
void MainHandlerSqlite::replayTimeMaybeJumpedFilter(qint64 curTime)
|
|
{
|
|
const qint64 curTimeRelative = curTime - m_replay.timeSpan.begin;
|
|
m_curFilterArrayIdx = -1;
|
|
for (int i = 0; i < m_filter.size(); ++i)
|
|
{
|
|
const auto &filter = m_filter.at(i);
|
|
if (filter.relativeTimeSpan.begin > curTimeRelative)
|
|
break;
|
|
m_curFilterArrayIdx = i;
|
|
}
|
|
if (m_curFilterArrayIdx >= 0 && m_curFilterArrayIdx < m_filter.size())
|
|
emit applyCurentFilter(m_filter.at(m_curFilterArrayIdx));
|
|
}
|
|
|
|
void MainHandlerSqlite::maybeAddNewOverlay(qint64 curTime)
|
|
{
|
|
// more than one overlay can start at curTime
|
|
while (true)
|
|
{
|
|
const int nextIdx = findNextIndex(curTime,
|
|
m_curOverlayArrayIdx,
|
|
m_overlays);
|
|
if (nextIdx < 0)
|
|
return;
|
|
m_curOverlayArrayIdx = nextIdx;
|
|
m_currentVisibleOverlays += m_overlays.at(nextIdx);
|
|
DEBUG_LOG(m_logger) << "Showing new overlay with id " << m_overlays.at(m_curOverlayArrayIdx).overlayId
|
|
<< " at time " << curTime;
|
|
emit applyNewOverlay(m_overlays.at(m_curOverlayArrayIdx).overlayId);
|
|
}
|
|
}
|
|
|
|
void MainHandlerSqlite::removeOutdatedOverlays(qint64 curTime)
|
|
{
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " curTime = "<< curTime
|
|
<< " m_currentVisibleOverlays.size()= "<< m_currentVisibleOverlays.size();
|
|
|
|
const qint64 curTimeRelative = curTime - m_replay.timeSpan.begin + m_replay.relativeTimeSpan.begin; // + m_replay.relativeTimeSpan.begin
|
|
for (int i = m_currentVisibleOverlays.size() - 1; i >= 0; --i)
|
|
{
|
|
const qint64 endTime = m_currentVisibleOverlays.at(i).relativeTimeSpan.end;
|
|
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " curTimeRelative = " << curTimeRelative << " endTime = " << endTime;
|
|
|
|
if (curTimeRelative > endTime)
|
|
{
|
|
emit removeOverlay(m_currentVisibleOverlays.at(i).overlayId);
|
|
m_currentVisibleOverlays.removeAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainHandlerSqlite::replayTimeMaybeJumpedOverlays(qint64 curTime)
|
|
{
|
|
// not really optimal
|
|
QSet<int> oldOverlayIds;
|
|
oldOverlayIds.reserve(m_currentVisibleOverlays.size());
|
|
for (const FilterInformation &overlay : qAsConst(m_currentVisibleOverlays))
|
|
oldOverlayIds += overlay.overlayId;
|
|
|
|
m_curOverlayArrayIdx = -1;
|
|
m_currentVisibleOverlays.clear();
|
|
maybeAddNewOverlay(curTime);
|
|
|
|
for (const FilterInformation &overlay : qAsConst(m_currentVisibleOverlays))
|
|
oldOverlayIds -= overlay.overlayId;
|
|
for (const int overlayId : qAsConst(oldOverlayIds))
|
|
emit removeOverlay(overlayId);
|
|
}
|
|
|
|
// struct ReplayInformation
|
|
// {
|
|
// int id = 0;
|
|
// TimeSpan timeSpan; // playbackTime msec since epoche
|
|
// TimeSpan relativeTimeSpan; // timeInPresentation msec since begin SCRIPT
|
|
|
|
int MainHandlerSqlite::findNextIndex(qint64 curTime, int curIdx, const QVector<FilterInformation> &data) const
|
|
{
|
|
const int nextIdx = curIdx + 1;
|
|
if (nextIdx < 0 || nextIdx >= data.size())
|
|
return -1;
|
|
const qint64 curTimeRelative = (curTime - m_replay.timeSpan.begin) + m_replay.relativeTimeSpan.begin;
|
|
// ^abs ^absBgn cur Topic/Replay + timeInPresentation since begin SCRIPT
|
|
// example: 1645787949000 - 1645787949000 + 375000
|
|
|
|
// startTime: msec, rel. to begin of SCRIPT
|
|
const qint64 startTime = data.at(nextIdx).relativeTimeSpan.begin;
|
|
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " curTimeRelative = " << curTimeRelative << " startTime = " << startTime;
|
|
|
|
if (curTimeRelative < startTime)
|
|
return -1;
|
|
return nextIdx;
|
|
}
|
|
|
|
} // namespace SituationReplay
|