/** * 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 #include #include #include #include #include #include #include #include #include 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 &filter, const QVector &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(); 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(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(output); if (!m_ddsInterface->init()) return false; return true; } std::shared_ptr 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 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 &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