/** * situation replay objects * $Id$ * $HeadURL$ * $Author$ * Created by Hanna E. Rathje, SLSE, 2016-01-13 */ #include "object_situation_replay.h" #include "dds_handler.h" #include "db_reader.h" #include "logger.h" #include #include #include namespace SituationReplay { ObjectSituationReplay::ObjectSituationReplay(const std::shared_ptr &ddsHandler, const std::shared_ptr &db, const std::shared_ptr &dbReader, int exerciseId) : m_ddsInterface(ddsHandler) , m_db(db) , m_dbReader(dbReader) , m_exerciseId(exerciseId) , m_replayTimer(new QTimer(this)) , m_currentReplayTime(0) , m_logger(getLogger("ObjectSituationReplay")) { connect(m_replayTimer, &QTimer::timeout, this, &ObjectSituationReplay::onReplayTimerTimeout); } ObjectSituationReplay::~ObjectSituationReplay() { m_ddsInterface->dts()->removeSitReplayByKey(m_data.id); delete m_replayTimer; } bool ObjectSituationReplay::isStopped() const { return (m_data.srpStateId == SituationReplay::SrpState_def::Stopped); } bool ObjectSituationReplay::isRunning() const { return (m_data.srpStateId == SituationReplay::SrpState_def::Started || m_data.srpStateId == SituationReplay::SrpState_def::Resumed); } Core::FunctionReplyObject ObjectSituationReplay::createObject(const Database::SrpSituationReplay &data) { static int s_idCounter = 1; m_data = data; setPace(m_data.pace); m_data.id = 0; m_data.srpStateId = SituationReplay::SrpState_def::Unknown; m_currentReplayTime = m_data.beginTime; if (m_db) { if (!m_db->insert(m_data)) return Core::FunctionReplyObject(0, tr_lts("dds", "Error inserting SrpSitatuationReplay-data into database")); } else { m_data.id = s_idCounter++; } // Id is serial -> will be generated by db => inside m_data after db-insert DEBUG_LOG(m_logger) << "New srpId: " << m_data.id; switchToState(SituationReplay::SrpState_def::Stopped); return Core::FunctionReplyObject(m_data.id); } Core::FunctionReplyObject ObjectSituationReplay::removeObject() { switchToState(SrpState::Stopped); if (m_db && !m_db->removeSrpSituationReplay(m_data.id)) return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "SituationReplay %1 can not be removed")).arg(m_data.id); m_ddsInterface->dts()->removeSitReplayByKey(m_data.id); return Core::FunctionReplyObject(m_data.id); } bool ObjectSituationReplay::setPace(double newPace) { if (newPace < 0) { WARNING_LOG(m_logger) << "Trying to set a pace < 0"; return false; } m_data.pace = newPace; return true; } bool ObjectSituationReplay::setSrpData(const Database::SrpSituationReplay &newData) { const int id = m_data.id; m_data = newData; setPace(m_data.pace); m_data.srpStateId = SituationReplay::SrpState::Unknown; m_data.id = id; m_currentReplayTime = m_data.beginTime; switchToState(newData.srpStateId); if (m_db) { if (!m_db->update(m_data)) { ERROR_LOG(m_logger) << "Error updating SitReplay data in database (exerciseState)"; return false; } Database::SrpReplayJournal stateChange; stateChange.situationReplayId = m_data.id; stateChange.beginTime = m_data.beginTime; stateChange.endTime = m_data.endTime; stateChange.pace = m_data.pace; stateChange.replayPartitionId = m_data.replayPartitionId; stateChange.srpStateId = m_data.srpStateId; stateChange.changedAt = QDateTime::currentDateTime(); // record SrpStateChange if (!m_db->insert(stateChange)) WARNING_LOG(m_logger) << "Recording change of srpState failed"; } // Publish Data via DTS onPublishReplayInformation(); return true; } bool ObjectSituationReplay::start() { INFO_LOG(m_logger) << "start: " << m_currentReplayTime; m_currentReplayTime = m_data.beginTime; emit started(m_currentReplayTime); // Read all data for a certain interval if (!m_dbReader->setReplayStartTime(m_currentReplayTime)) return false; if (!checkPublishData()) return false; m_replayTimer->start(1000); return true; } bool ObjectSituationReplay::pause() { INFO_LOG(m_logger) << "paused: " << m_currentReplayTime; m_replayTimer->stop(); emit paused(); return true; } bool ObjectSituationReplay::resume() { INFO_LOG(m_logger) << "resumed" << m_currentReplayTime; m_replayTimer->start(1000); emit resumed(m_currentReplayTime); return true; } bool ObjectSituationReplay::stop() { INFO_LOG(m_logger) << "stopped: " << m_currentReplayTime; m_replayTimer->stop(); emit stopped(); return true; } void ObjectSituationReplay::onPublishReplayInformation() { const SituationReplay::SitReplay ddsData(m_data.id, m_data.srpStateId, m_exerciseId, m_data.replayPartitionId, m_data.beginTime, m_data.endTime, m_data.pace); INFO_LOG(m_logger) << "Publish SitReplay data: " << ddsData; if (!m_ddsInterface->dts()->publish(ddsData)) ERROR_LOG(m_logger) << "Error publishing SitReplay data to dds"; } void ObjectSituationReplay::onPublishReplayTime() { // publish partitionTime m_ddsInterface->publishPartitionTime(Core::PartitionTime(m_currentReplayTime)); emit replayTimeChanged(m_currentReplayTime); } void ObjectSituationReplay::onReplayTimerTimeout() { m_currentReplayTime += qRound64(m_data.pace * 1000); DEBUG_LOG(m_logger) << "onReplayTimerTimeout, endTime: " << m_data.endTime << " - " << m_currentReplayTime; checkPublishData(); } bool ObjectSituationReplay::checkPublishData() { INFO_LOG(m_logger) << " checkPublishData..."; onPublishReplayTime(); if (m_data.endTime != 0 && m_data.endTime < m_currentReplayTime) { if (switchToState(SrpState_def::Stopped).error()) return false; } else { QElapsedTimer timer; if (m_logger.isInfoEnabled()) timer.start(); // publish data for current time m_dbReader->publish(m_currentReplayTime); if (m_logger.isInfoEnabled()) INFO_LOG(m_logger) << "m_dbReader->publish took " << timer.elapsed() << " [ms]"; if (qFuzzyIsNull(m_data.pace)) // pace == 0 -> snapshot { QElapsedTimer timerReadData; if (m_logger.isInfoEnabled()) timerReadData.start(); m_dbReader->waitForAsyncOperations(m_data.endTime); if (switchToState(SrpState_def::Stopped).error()) return false; if (m_logger.isInfoEnabled()) INFO_LOG(m_logger) << "m_dbReader->waitForAsyncOperations took " << timerReadData.elapsed() << " [ms]"; } else { QElapsedTimer timerReadData; if (m_logger.isInfoEnabled()) timerReadData.start(); // read next chunk if (!m_dbReader->readData(m_currentReplayTime, m_currentReplayTime + qRound64(m_data.pace * 1000))) return false; if (m_logger.isInfoEnabled()) INFO_LOG(m_logger) << "m_dbReader->readData took " << timerReadData.elapsed() << " [ms]"; } } INFO_LOG(m_logger) << " checkPublishData... DONE"; return true; } Core::FunctionReplyObject ObjectSituationReplay::switchToState(SrpState newSrpState) { Core::FunctionReplyObject ret(m_data.id); INFO_LOG(m_logger) << "-------------- switchTo: " << newSrpState << ", current: " << m_data.srpStateId; if (m_data.srpStateId != newSrpState) { switch (newSrpState.underlying()) { case SrpState_def::Paused: if (!pause()) return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Could not pause replay")); break; case SrpState_def::Resumed: if (!resume()) return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Could not resume replay")); break; case SrpState_def::Started: if (!start()) return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Could not start replay")); break; case SrpState_def::Stopped: if (!stop()) return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Could not stop replay")); break; default: break; } m_data.srpStateId = newSrpState; onPublishReplayInformation(); } return ret; } } // Namespace SituationReplay