diff --git a/ground/src/plugins/logging/logfile.cpp b/ground/src/plugins/logging/logfile.cpp index 9b33e732c..6072de049 100644 --- a/ground/src/plugins/logging/logfile.cpp +++ b/ground/src/plugins/logging/logfile.cpp @@ -1,9 +1,11 @@ #include "logfile.h" #include +#include LogFile::LogFile(QObject *parent) : QIODevice(parent) { + connect(&timer, SIGNAL(timeout()), this, SLOT(timerFired())); } bool LogFile::open(OpenMode mode) { @@ -11,8 +13,6 @@ bool LogFile::open(OpenMode mode) { // start a timer for playback myTime.restart(); - // TODO: support openning for read or write to determine playback or not - Q_ASSERT(mode == QIODevice::WriteOnly); if(file.open(mode) == FALSE) { qDebug() << "Unable to open " << file.fileName() << " for logging"; @@ -44,6 +44,47 @@ qint64 LogFile::writeData(const char * data, qint64 dataSize) { if(written != -1) emit bytesWritten(written); - //qDebug() << "Wrote " << dataSize << " bytes at " << timeStamp << " ms"; return dataSize; } + +qint64 LogFile::readData(char * data, qint64 maxSize) { + qint64 toRead = qMin(maxSize,(qint64)dataBuffer.size()); + memcpy(data,dataBuffer.data(),toRead); + dataBuffer.remove(0,toRead); + return toRead; +} + +qint64 LogFile::bytesAvailable() const +{ + return dataBuffer.size(); +} + +void LogFile::timerFired() +{ + qint64 dataSize; + + // TODO: support time rescaling and seeking + while (myTime.elapsed() > lastTimeStamp) { + file.read((char *) &dataSize, sizeof(dataSize)); + dataBuffer.append(file.read(dataSize)); + emit readyRead(); + + file.read((char *) &lastTimeStamp,sizeof(lastTimeStamp)); + } +} + +bool LogFile::startReplay() { + dataBuffer.clear(); + myTime.restart(); + + file.read((char *) &lastTimeStamp,sizeof(lastTimeStamp)); + + timer.setInterval(10); + timer.start(); + return true; +} + +bool LogFile::stopReplay() { + timer.stop(); + return true; +} diff --git a/ground/src/plugins/logging/logfile.h b/ground/src/plugins/logging/logfile.h index 708fa6c46..4b0976eb5 100644 --- a/ground/src/plugins/logging/logfile.h +++ b/ground/src/plugins/logging/logfile.h @@ -3,7 +3,9 @@ #include #include +#include #include +#include #include class LogFile : public QIODevice @@ -11,20 +13,29 @@ class LogFile : public QIODevice Q_OBJECT public: explicit LogFile(QObject *parent = 0); - qint64 bytesAvailable() { return 0; }; + qint64 bytesAvailable() const; qint64 bytesToWrite() { return file.bytesToWrite(); }; bool open(OpenMode mode); void setFileName(QString name) { file.setFileName(name); }; void close(); qint64 writeData(const char * data, qint64 dataSize); - qint64 readData(char * /*data*/, qint64 /*maxlen*/) {return 0; } + qint64 readData(char * data, qint64 maxlen); + + bool startReplay(); + bool stopReplay(); + +public slots: + void timerFired(); signals: -public slots: -private: + void readReady(); + protected: + QByteArray dataBuffer; + QTimer timer; QTime myTime; QFile file; + qint32 lastTimeStamp; }; #endif // LOGFILE_H diff --git a/ground/src/plugins/logging/loggingplugin.cpp b/ground/src/plugins/logging/loggingplugin.cpp index ff16cdaa7..05527f0b5 100644 --- a/ground/src/plugins/logging/loggingplugin.cpp +++ b/ground/src/plugins/logging/loggingplugin.cpp @@ -118,7 +118,6 @@ void LoggingThread::stopLogging() quit(); } - LoggingPlugin::LoggingPlugin() : state(IDLE) { // Do nothing @@ -141,6 +140,7 @@ bool LoggingPlugin::initialize(const QStringList& args, QString *errMsg) Core::ActionManager* am = Core::ICore::instance()->actionManager(); Core::ActionContainer* ac = am->actionContainer(Core::Constants::M_FILE); + // Command to start logging Core::Command* cmd = am->registerAction(new QAction(this), "LoggingPlugin.Logging", QList() << @@ -154,6 +154,19 @@ bool LoggingPlugin::initialize(const QStringList& args, QString *errMsg) connect(cmd->action(), SIGNAL(triggered(bool)), this, SLOT(toggleLogging())); + // Command to replay logging + Core::Command* cmd2 = am->registerAction(new QAction(this), + "LoggingPlugin.Playback", + QList() << + Core::Constants::C_GLOBAL_ID); + cmd2->setDefaultKeySequence(QKeySequence("Ctrl+R")); + cmd2->action()->setText("Replay..."); + + ac->appendGroup("Replay"); + ac->addAction(cmd2, "Replay"); + + connect(cmd2->action(), SIGNAL(triggered(bool)), this, SLOT(toggleReplay())); + return true; } @@ -171,19 +184,40 @@ void LoggingPlugin::toggleLogging() connect(fd, SIGNAL(fileSelected(QString)), this, SLOT(startLogging(QString))); fd->exec(); } - else + else if(state == LOGGING) { stopLogging(); } } +/** + * The action that is triggered by the menu item which opens the + * file and begins replay if successful + */ +void LoggingPlugin::toggleReplay() +{ + if(state == IDLE) + { + QFileDialog * fd = new QFileDialog(); + fd->setAcceptMode(QFileDialog::AcceptOpen); + fd->setNameFilter("OpenPilot Log (*.opl)"); + connect(fd, SIGNAL(fileSelected(QString)), this, SLOT(startReplay(QString))); + fd->exec(); + } + else if(state == REPLAY) + { + stopReplay(); + } +} + + /** * Starts the logging thread to a certain file */ void LoggingPlugin::startLogging(QString file) { qDebug() << "Logging to " << file; - LoggingThread *loggingThread = new LoggingThread(); + loggingThread = new LoggingThread(); if(loggingThread->openFile(file,this)) { connect(loggingThread,SIGNAL(finished()),this,SLOT(loggingStopped())); @@ -196,6 +230,30 @@ void LoggingPlugin::startLogging(QString file) } } +/** + * Starts the logging thread replaying a certain file + */ +void LoggingPlugin::startReplay(QString file) +{ + + logFile = new LogFile; + logFile->setFileName(file); + if(logFile->open(QIODevice::ReadOnly)) { + qDebug() << "Replaying " << file; + state = REPLAY; + + ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); + UAVObjectManager *objManager = pm->getObject(); + + uavTalk = new UAVTalk(logFile, objManager); + logFile->startReplay(); + } else { + QErrorMessage err; + err.showMessage("Unable to open file for replay"); + err.exec(); + } +} + /** * Send the stop logging signal to the LoggingThread */ @@ -204,14 +262,33 @@ void LoggingPlugin::stopLogging() emit stopLoggingSignal(); } + +/** + * Send the stop replay signal to the ReplayThread + */ +void LoggingPlugin::stopReplay() +{ + logFile->stopReplay(); + logFile->close(); + free(uavTalk); + free(logFile); + uavTalk = 0; + logFile = 0; + state = IDLE; +} + /** * Receive the logging stopped signal from the LoggingThread * and change status to not logging */ void LoggingPlugin::loggingStopped() { - state = IDLE; + if(state == LOGGING) + state = IDLE; + free(loggingThread); + loggingThread = NULL; } + void LoggingPlugin::extensionsInitialized() { // Do nothing diff --git a/ground/src/plugins/logging/loggingplugin.h b/ground/src/plugins/logging/loggingplugin.h index 44493f789..55e8ebc4f 100644 --- a/ground/src/plugins/logging/loggingplugin.h +++ b/ground/src/plugins/logging/loggingplugin.h @@ -70,15 +70,23 @@ public: signals: void stopLoggingSignal(void); + void stopReplaySignal(void); protected: - enum {IDLE, LOGGING} state; + enum {IDLE, LOGGING, REPLAY} state; LoggingThread * loggingThread; + // These are used for replay, logging in its own thread + UAVTalk * uavTalk; + LogFile * logFile; + private slots: void toggleLogging(); + void toggleReplay(); void startLogging(QString file); + void startReplay(QString file); void stopLogging(); + void stopReplay(); void loggingStopped(); }; #endif /* LoggingPLUGIN_H_ */