From df0ed37d29e383e141de966817426eac5c1d9232 Mon Sep 17 00:00:00 2001 From: peabody124 Date: Tue, 21 Sep 2010 19:28:01 +0000 Subject: [PATCH] Ground/Logging: Supports replay. Still needs a sexy widget to allow seeking (although seeking will be difficult since technically it depends on the entire state but we can probably ignore that). Also I think the interface for replay shoudln't be through a menu item but should be like any other connection. The way I implemented it (custom QIODevice that buffers the logfile and reads the timestamps) makes this pretty easy to do. I'm just not familiar enough with the connection manager to do this. This means currently if you're connected to device and start a replay you get updates from two places. git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@1705 ebee16cc-31ac-478f-84a7-5cbb03baadba --- ground/src/plugins/logging/logfile.cpp | 47 ++++++++++- ground/src/plugins/logging/logfile.h | 19 ++++- ground/src/plugins/logging/loggingplugin.cpp | 85 +++++++++++++++++++- ground/src/plugins/logging/loggingplugin.h | 10 ++- 4 files changed, 149 insertions(+), 12 deletions(-) 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_ */