mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2025-01-30 15:52:12 +01:00
LP-597 Progress bar for GCS log replay - add progress bar and a stop button
- Added stop button: - start: starts from 0 position after stop signal, resumes from current position after pause signal. - pause: freezes at current position, also freezes scopes. - stop: stops replay and resets start position to 0 (does not close the logfile) - Creates an index of the timestamp positions in the logfile for quick seeking to the target position in the log. - Start, End and current position timestamps are visible in the GUI below the position slider. - Determine replay position by moving the position slider - Update position label while changing position bar - Speed widget: lowest multiplier is now 0.1 instead of 0. Only set one decimal of precision. More decimals seem useless at this time.
This commit is contained in:
parent
a4c0bcfb1a
commit
c53e99ee41
@ -26,6 +26,8 @@
|
||||
|
||||
#include <QDebug>
|
||||
#include <QtGlobal>
|
||||
#include <QDataStream>
|
||||
#include <QThread> // DEBUG: to display the thread ID
|
||||
|
||||
LogFile::LogFile(QObject *parent) : QIODevice(parent),
|
||||
m_timer(this),
|
||||
@ -34,9 +36,12 @@ LogFile::LogFile(QObject *parent) : QIODevice(parent),
|
||||
m_lastPlayed(0),
|
||||
m_timeOffset(0),
|
||||
m_playbackSpeed(1.0),
|
||||
paused(false),
|
||||
m_replayStatus(STOPPED),
|
||||
m_useProvidedTimeStamp(false),
|
||||
m_providedTimeStamp(0)
|
||||
m_providedTimeStamp(0),
|
||||
m_beginTimeStamp(0),
|
||||
m_endTimeStamp(0),
|
||||
m_timer_tick(0)
|
||||
{
|
||||
connect(&m_timer, &QTimer::timeout, this, &LogFile::timerFired);
|
||||
}
|
||||
@ -137,14 +142,58 @@ qint64 LogFile::bytesAvailable() const
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
timerFired()
|
||||
|
||||
This function is called at a 10 ms interval to fill the replay buffers.
|
||||
|
||||
*/
|
||||
|
||||
void LogFile::timerFired()
|
||||
{
|
||||
if (m_replayStatus != PLAYING) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_timer_tick++;
|
||||
if ( m_timer_tick % 100 == 0 ) {
|
||||
qDebug() << "----------------------------------------------------------";
|
||||
qDebug() << "LogFile::timerFired() -> Tick = " << m_timer_tick;
|
||||
}
|
||||
|
||||
if (m_file.bytesAvailable() > 4) {
|
||||
int time;
|
||||
time = m_myTime.elapsed();
|
||||
|
||||
// TODO: going back in time will be a problem
|
||||
while ((m_lastPlayed + ((double)(time - m_timeOffset) * m_playbackSpeed) > m_nextTimeStamp)) {
|
||||
/*
|
||||
This code generates an advancing window. All samples that fit in the window
|
||||
are replayed. The window is about the size of the timer interval: 10 ms.
|
||||
|
||||
Description of used variables:
|
||||
|
||||
time : time passed since start of playback (in ms) - current
|
||||
m_timeOffset : time passed since start of playback (in ms) - when timerFired() was previously run
|
||||
m_lastPlayed : next log timestamp to advance to (in ms)
|
||||
m_nextTimeStamp : timestamp of most recently read log entry (in ms)
|
||||
m_playbackSpeed : 1 .. 10 replay speedup factor
|
||||
|
||||
*/
|
||||
|
||||
while ( m_nextTimeStamp < (m_lastPlayed + (double)(time - m_timeOffset) * m_playbackSpeed) ) {
|
||||
// if ( m_timer_tick % 100 == 0 ) {
|
||||
// if ( true ) {
|
||||
// qDebug() << "LogFile::timerFired() -> m_lastPlayed = " << m_lastPlayed;
|
||||
// qDebug() << "LogFile::timerFired() -> m_nextTimeStamp = " << m_nextTimeStamp;
|
||||
// qDebug() << "LogFile::timerFired() -> time = " << time;
|
||||
// qDebug() << "LogFile::timerFired() -> m_timeOffset = " << m_timeOffset;
|
||||
// qDebug() << "---";
|
||||
// qDebug() << "LogFile::timerFired() -> m_nextTimeStamp = " << m_nextTimeStamp;
|
||||
// qDebug() << "LogFile::timerFired() -> (m_lastPlayed + (double)(time - m_timeOffset) * m_playbackSpeed) = " << (m_lastPlayed + (double)(time - m_timeOffset) * m_playbackSpeed);
|
||||
// qDebug() << "---";
|
||||
// }
|
||||
|
||||
// advance the replay window for the next time period
|
||||
|
||||
m_lastPlayed += ((double)(time - m_timeOffset) * m_playbackSpeed);
|
||||
|
||||
// read data size
|
||||
@ -178,6 +227,10 @@ void LogFile::timerFired()
|
||||
|
||||
emit readyRead();
|
||||
|
||||
// rate-limit slider bar position updates to 10 updates per second
|
||||
if (m_timer_tick % 10 == 0) {
|
||||
emit replayPosition(m_nextTimeStamp);
|
||||
}
|
||||
// read next timestamp
|
||||
if (m_file.bytesAvailable() < (qint64)sizeof(m_nextTimeStamp)) {
|
||||
qDebug() << "LogFile - end of log file reached";
|
||||
@ -196,7 +249,7 @@ void LogFile::timerFired()
|
||||
}
|
||||
|
||||
m_timeOffset = time;
|
||||
time = m_myTime.elapsed();
|
||||
time = m_myTime.elapsed(); // number of milliseconds since start of playback
|
||||
}
|
||||
} else {
|
||||
qDebug() << "LogFile - end of log file reached";
|
||||
@ -209,8 +262,28 @@ bool LogFile::isPlaying() const
|
||||
return m_file.isOpen() && m_timer.isActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* FUNCTION: startReplay()
|
||||
*
|
||||
* Starts replaying a newly opened logfile.
|
||||
* Starts a timer: m_timer
|
||||
*
|
||||
* This function and the stopReplay() function should only ever be called from the same thread.
|
||||
* This is required for correctly controlling the timer.
|
||||
*
|
||||
*/
|
||||
bool LogFile::startReplay()
|
||||
{
|
||||
qDebug() << "startReplay(): start of function, current Thread ID is: " << QThread::currentThreadId();
|
||||
|
||||
// Walk through logfile and create timestamp index
|
||||
// Don't start replay if there was a problem indexing the logfile.
|
||||
if (!buildIndex()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_timer_tick = 0;
|
||||
|
||||
if (!m_file.isOpen() || m_timer.isActive()) {
|
||||
return false;
|
||||
}
|
||||
@ -221,7 +294,9 @@ bool LogFile::startReplay()
|
||||
m_lastPlayed = 0;
|
||||
m_previousTimeStamp = 0;
|
||||
m_nextTimeStamp = 0;
|
||||
m_mutex.lock();
|
||||
m_dataBuffer.clear();
|
||||
m_mutex.unlock();
|
||||
|
||||
// read next timestamp
|
||||
if (m_file.bytesAvailable() < (qint64)sizeof(m_nextTimeStamp)) {
|
||||
@ -232,50 +307,312 @@ bool LogFile::startReplay()
|
||||
|
||||
m_timer.setInterval(10);
|
||||
m_timer.start();
|
||||
paused = false;
|
||||
m_replayStatus = PLAYING;
|
||||
|
||||
emit replayStarted();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUNCTION: stopReplay()
|
||||
*
|
||||
* Stops replaying the logfile.
|
||||
* Stops the timer: m_timer
|
||||
*
|
||||
* This function and the startReplay() function should only ever be called from the same thread.
|
||||
* This is a requirement to be able to control the timer.
|
||||
*
|
||||
*/
|
||||
bool LogFile::stopReplay()
|
||||
{
|
||||
if (!m_file.isOpen() || !(m_timer.isActive() || paused)) {
|
||||
qDebug() << "stopReplay(): start of function, current Thread ID is: " << QThread::currentThreadId();
|
||||
|
||||
if (!m_file.isOpen() || !m_timer.isActive()) {
|
||||
return false;
|
||||
}
|
||||
qDebug() << "LogFile - stopReplay";
|
||||
m_timer.stop();
|
||||
paused = false;
|
||||
m_replayStatus = STOPPED;
|
||||
|
||||
emit replayFinished();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* SLOT: restartReplay()
|
||||
*
|
||||
* This function starts replay from the begining of the currently opened logfile.
|
||||
*
|
||||
*/
|
||||
void LogFile::restartReplay()
|
||||
{
|
||||
qDebug() << "restartReplay(): start of function, current Thread ID is: " << QThread::currentThreadId();
|
||||
|
||||
resumeReplayFrom(0);
|
||||
|
||||
qDebug() << "restartReplay(): end of function, current Thread ID is: " << QThread::currentThreadId();
|
||||
}
|
||||
|
||||
/**
|
||||
* SLOT: haltReplay()
|
||||
*
|
||||
* Stops replay without storing the current playback position
|
||||
*
|
||||
*/
|
||||
void LogFile::haltReplay()
|
||||
{
|
||||
qDebug() << "haltReplay(): start of function, current Thread ID is: " << QThread::currentThreadId();
|
||||
|
||||
qDebug() << "haltReplay() time = m_myTime.elapsed() = " << m_myTime.elapsed();
|
||||
qDebug() << "haltReplay() m_timeOffset = " << m_timeOffset;
|
||||
qDebug() << "haltReplay() m_nextTimeStamp = " << m_nextTimeStamp;
|
||||
qDebug() << "haltReplay() m_lastPlayed = " << m_lastPlayed;
|
||||
|
||||
m_replayStatus = STOPPED;
|
||||
|
||||
qDebug() << "haltReplay(): end of function, current Thread ID is: " << QThread::currentThreadId();
|
||||
}
|
||||
/**
|
||||
* SLOT: pauseReplay()
|
||||
*
|
||||
* Pauses replay while storing the current playback position
|
||||
*
|
||||
*/
|
||||
bool LogFile::pauseReplay()
|
||||
{
|
||||
qDebug() << "pauseReplay(): start of function, current Thread ID is: " << QThread::currentThreadId();
|
||||
|
||||
qDebug() << "pauseReplay() time = m_myTime.elapsed() = " << m_myTime.elapsed();
|
||||
qDebug() << "pauseReplay() m_timeOffset = " << m_timeOffset;
|
||||
qDebug() << "pauseReplay() m_nextTimeStamp = " << m_nextTimeStamp;
|
||||
qDebug() << "pauseReplay() m_lastPlayed = " << m_lastPlayed;
|
||||
|
||||
if (!m_timer.isActive()) {
|
||||
return false;
|
||||
}
|
||||
qDebug() << "LogFile - pauseReplay";
|
||||
m_timer.stop();
|
||||
paused = true;
|
||||
m_replayStatus = PAUSED;
|
||||
|
||||
// hack to notify UI that replay paused
|
||||
emit replayStarted();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* SLOT: resumeReplay()
|
||||
*
|
||||
* Resumes replay from the stored playback position
|
||||
*
|
||||
*/
|
||||
bool LogFile::resumeReplay()
|
||||
{
|
||||
qDebug() << "resumeReplay(): start of function, current Thread ID is: " << QThread::currentThreadId();
|
||||
|
||||
m_mutex.lock();
|
||||
m_dataBuffer.clear();
|
||||
m_mutex.unlock();
|
||||
|
||||
m_file.seek(0);
|
||||
|
||||
for (int i = 0; i < m_timeStamps.size(); ++i) {
|
||||
if (m_timeStamps.at(i) >= m_lastPlayed) {
|
||||
m_file.seek(m_timeStampPositions.at(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_file.read((char *)&m_nextTimeStamp, sizeof(m_nextTimeStamp));
|
||||
|
||||
m_myTime.restart();
|
||||
m_myTime = m_myTime.addMSecs(-m_timeOffset); // Set startpoint this far back in time.
|
||||
|
||||
qDebug() << "resumeReplay() time = m_myTime.elapsed() = " << m_myTime.elapsed();
|
||||
qDebug() << "resumeReplay() m_timeOffset = " << m_timeOffset;
|
||||
qDebug() << "resumeReplay() m_nextTimeStamp = " << m_nextTimeStamp;
|
||||
qDebug() << "resumeReplay() m_lastPlayed = " << m_lastPlayed;
|
||||
|
||||
qDebug() << "resumeReplay(): end of function, current Thread ID is: " << QThread::currentThreadId();
|
||||
if (m_timer.isActive()) {
|
||||
return false;
|
||||
}
|
||||
qDebug() << "LogFile - resumeReplay";
|
||||
m_timeOffset = m_myTime.elapsed();
|
||||
m_timer.start();
|
||||
paused = false;
|
||||
m_replayStatus = PLAYING;
|
||||
|
||||
// hack to notify UI that replay resumed
|
||||
// Notify UI that replay has been resumed
|
||||
emit replayStarted();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* SLOT: resumeReplayFrom()
|
||||
*
|
||||
* Resumes replay from the given position
|
||||
*
|
||||
*/
|
||||
void LogFile::resumeReplayFrom(quint32 desiredPosition)
|
||||
{
|
||||
qDebug() << "resumeReplayFrom(): start of function, current Thread ID is: " << QThread::currentThreadId();
|
||||
|
||||
m_mutex.lock();
|
||||
m_dataBuffer.clear();
|
||||
m_mutex.unlock();
|
||||
|
||||
m_file.seek(0);
|
||||
|
||||
qint32 i;
|
||||
for (i = 0; i < m_timeStamps.size(); ++i) {
|
||||
if (m_timeStamps.at(i) >= desiredPosition) {
|
||||
m_file.seek(m_timeStampPositions.at(i));
|
||||
m_lastPlayed = m_timeStamps.at(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_file.read((char *)&m_nextTimeStamp, sizeof(m_nextTimeStamp));
|
||||
|
||||
if (m_nextTimeStamp != m_timeStamps.at(i)) {
|
||||
qDebug() << "resumeReplayFrom() m_nextTimeStamp != m_timeStamps.at(i) -> " << m_nextTimeStamp << " != " << m_timeStamps.at(i);
|
||||
}
|
||||
|
||||
// m_timeOffset = (m_lastPlayed - m_nextTimeStamp) / m_playbackSpeed;
|
||||
m_timeOffset = 0;
|
||||
|
||||
m_myTime.restart();
|
||||
// m_myTime = m_myTime.addMSecs(-m_timeOffset); // Set startpoint this far back in time.
|
||||
// TODO: The above line is a possible memory leak. I'm not sure how to handle this correctly.
|
||||
|
||||
qDebug() << "resumeReplayFrom() time = m_myTime.elapsed() = " << m_myTime.elapsed();
|
||||
qDebug() << "resumeReplayFrom() m_timeOffset = " << m_timeOffset;
|
||||
qDebug() << "resumeReplayFrom() m_nextTimeStamp = " << m_nextTimeStamp;
|
||||
qDebug() << "resumeReplayFrom() m_lastPlayed = " << m_lastPlayed;
|
||||
|
||||
m_replayStatus = PLAYING;
|
||||
emit replayStarted();
|
||||
|
||||
qDebug() << "resumeReplayFrom(): end of function, current Thread ID is: " << QThread::currentThreadId();
|
||||
}
|
||||
|
||||
/**
|
||||
* FUNCTION: getReplayStatus()
|
||||
*
|
||||
* Returns the current replay status.
|
||||
*
|
||||
*/
|
||||
ReplayState LogFile::getReplayStatus()
|
||||
{
|
||||
return m_replayStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUNCTION: buildIndex()
|
||||
*
|
||||
* Walk through the opened logfile and sets the start and end position timestamps
|
||||
* Also builds an index for quickly skipping to a specific position in the logfile.
|
||||
*
|
||||
* returns true when indexing has completed successfully
|
||||
* returns false when a problem was encountered
|
||||
*
|
||||
*/
|
||||
bool LogFile::buildIndex()
|
||||
{
|
||||
quint32 timeStamp;
|
||||
qint64 totalSize;
|
||||
qint64 readPointer = 0;
|
||||
quint64 index = 0;
|
||||
|
||||
QByteArray arr = m_file.readAll();
|
||||
|
||||
totalSize = arr.size();
|
||||
QDataStream dataStream(&arr, QIODevice::ReadOnly);
|
||||
|
||||
// set the start timestamp
|
||||
if (totalSize - readPointer >= 4) {
|
||||
dataStream.readRawData((char *)&timeStamp, 4);
|
||||
m_timeStamps.append(timeStamp);
|
||||
m_timeStampPositions.append(readPointer);
|
||||
qDebug() << "LogFile::buildIndex() element index = " << index << " \t-> timestamp = " << timeStamp << " \t-> bytes in file = " << readPointer;
|
||||
readPointer += 4;
|
||||
index++;
|
||||
m_beginTimeStamp = timeStamp;
|
||||
m_endTimeStamp = timeStamp;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
qint64 dataSize;
|
||||
|
||||
// Check if there are enough bytes remaining for a correct "dataSize" field
|
||||
if (totalSize - readPointer < (qint64)sizeof(dataSize)) {
|
||||
qDebug() << "Error: Logfile corrupted! Unexpected end of file";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the dataSize field
|
||||
dataStream.readRawData((char *)&dataSize, sizeof(dataSize));
|
||||
readPointer += sizeof(dataSize);
|
||||
|
||||
if (dataSize < 1 || dataSize > (1024 * 1024)) {
|
||||
qDebug() << "Error: Logfile corrupted! Unlikely packet size: " << dataSize << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if there are enough bytes remaining
|
||||
if (totalSize - readPointer < dataSize) {
|
||||
qDebug() << "Error: Logfile corrupted! Unexpected end of file";
|
||||
return false;
|
||||
}
|
||||
|
||||
// skip reading the data (we don't need it at this point)
|
||||
readPointer += dataStream.skipRawData(dataSize);
|
||||
|
||||
// read the next timestamp
|
||||
if (totalSize - readPointer >= 4) {
|
||||
dataStream.readRawData((char *)&timeStamp, 4);
|
||||
qDebug() << "LogFile::buildIndex() element index = " << index << " \t-> timestamp = " << timeStamp << " \t-> bytes in file = " << readPointer;
|
||||
|
||||
// some validity checks
|
||||
if (timeStamp < m_endTimeStamp // logfile goes back in time
|
||||
|| (timeStamp - m_endTimeStamp) > (60 * 60 * 1000)) { // gap of more than 60 minutes)
|
||||
qDebug() << "Error: Logfile corrupted! Unlikely timestamp " << timeStamp << " after " << m_endTimeStamp;
|
||||
// return false;
|
||||
}
|
||||
|
||||
m_timeStamps.append(timeStamp);
|
||||
m_timeStampPositions.append(readPointer);
|
||||
readPointer += 4;
|
||||
index++;
|
||||
m_endTimeStamp = timeStamp;
|
||||
} else {
|
||||
// Break without error (we expect to end at this location when we are at the end of the logfile)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "buildIndex() -> first timestamp in log = " << m_beginTimeStamp;
|
||||
qDebug() << "buildIndex() -> last timestamp in log = " << m_endTimeStamp;
|
||||
|
||||
emit updateBeginAndEndtimes(m_beginTimeStamp, m_endTimeStamp);
|
||||
|
||||
// reset the read pointer to the start of the file
|
||||
m_file.seek(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* FUNCTION: setReplaySpeed()
|
||||
*
|
||||
* Update the replay speed.
|
||||
*
|
||||
* FIXME: currently, changing the replay speed, while skipping through the logfile
|
||||
* with the position bar causes position alignment to be lost.
|
||||
*
|
||||
*/
|
||||
void LogFile::setReplaySpeed(double val)
|
||||
{
|
||||
m_playbackSpeed = val;
|
||||
qDebug() << "Playback speed is now " << QString("%1").arg(m_playbackSpeed, 4, 'f', 2, QChar('0'));
|
||||
}
|
||||
|
||||
|
||||
|
@ -34,6 +34,9 @@
|
||||
#include <QDebug>
|
||||
#include <QBuffer>
|
||||
#include <QFile>
|
||||
#include <QVector>
|
||||
|
||||
typedef enum { PLAYING, PAUSED, STOPPED } ReplayState;
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT LogFile : public QIODevice {
|
||||
Q_OBJECT
|
||||
@ -75,32 +78,34 @@ public:
|
||||
m_providedTimeStamp = providedTimestamp;
|
||||
}
|
||||
|
||||
ReplayState getReplayStatus();
|
||||
|
||||
public slots:
|
||||
void setReplaySpeed(double val)
|
||||
{
|
||||
m_playbackSpeed = val;
|
||||
qDebug() << "Playback speed is now" << m_playbackSpeed;
|
||||
};
|
||||
void setReplaySpeed(double val);
|
||||
bool startReplay();
|
||||
bool stopReplay();
|
||||
bool pauseReplay();
|
||||
bool resumeReplay();
|
||||
void resumeReplayFrom(quint32);
|
||||
void restartReplay();
|
||||
void haltReplay();
|
||||
|
||||
protected slots:
|
||||
void timerFired();
|
||||
|
||||
signals:
|
||||
void readReady();
|
||||
void replayStarted();
|
||||
void replayFinished();
|
||||
void replayPosition(quint32);
|
||||
void updateBeginAndEndtimes(quint32, quint32);
|
||||
|
||||
protected:
|
||||
QByteArray m_dataBuffer;
|
||||
QTimer m_timer;
|
||||
QTime m_myTime;
|
||||
QFile m_file;
|
||||
qint32 m_previousTimeStamp;
|
||||
qint32 m_nextTimeStamp;
|
||||
quint32 m_previousTimeStamp;
|
||||
quint32 m_nextTimeStamp;
|
||||
double m_lastPlayed;
|
||||
// QMutex wants to be mutable
|
||||
// http://stackoverflow.com/questions/25521570/can-mutex-locking-function-be-marked-as-const
|
||||
@ -108,11 +113,18 @@ protected:
|
||||
|
||||
int m_timeOffset;
|
||||
double m_playbackSpeed;
|
||||
bool paused;
|
||||
ReplayState m_replayStatus;
|
||||
|
||||
private:
|
||||
bool m_useProvidedTimeStamp;
|
||||
qint32 m_providedTimeStamp;
|
||||
quint32 m_beginTimeStamp;
|
||||
quint32 m_endTimeStamp;
|
||||
quint32 m_timer_tick;
|
||||
QVector<quint32> m_timeStamps;
|
||||
QVector<qint64> m_timeStampPositions;
|
||||
|
||||
bool buildIndex();
|
||||
};
|
||||
|
||||
#endif // LOGFILE_H
|
||||
|
@ -7,19 +7,19 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>439</width>
|
||||
<height>122</height>
|
||||
<height>150</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>100</horstretch>
|
||||
<verstretch>80</verstretch>
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>80</height>
|
||||
<height>150</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -27,9 +27,9 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="2,2,2,0,0,0">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetNoConstraint</enum>
|
||||
</property>
|
||||
@ -71,7 +71,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">Pause</string>
|
||||
<string>Pause</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../notify/res.qrc">
|
||||
@ -79,6 +79,29 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="stopButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stop</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../notify/res.qrc">
|
||||
<normaloff>:/notify/images/delete.png</normaloff>:/notify/images/delete.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
@ -125,11 +148,17 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="playbackSpeed">
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.10000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>10.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.100000000000000</double>
|
||||
<double>0.10000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1.000000000000000</double>
|
||||
@ -151,6 +180,106 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="playBackPosition">
|
||||
<property name="tracking">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="invertedAppearance">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="invertedControls">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBothSides</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>5</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="startTimeLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="positionTimestampLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="endTimeLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -40,6 +40,15 @@ LoggingGadgetWidget::LoggingGadgetWidget(QWidget *parent) : QWidget(parent), log
|
||||
|
||||
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
|
||||
scpPlugin = pm->getObject<ScopeGadgetFactory>();
|
||||
|
||||
disableButtons();
|
||||
|
||||
sliderActionDelay.setSingleShot(true);
|
||||
sliderActionDelay.setInterval(200); // Delay for 200 ms
|
||||
|
||||
connect(&sliderActionDelay, SIGNAL(timeout()), this, SLOT(sendResumeReplayFrom()));
|
||||
|
||||
m_storedPosition = 0;
|
||||
}
|
||||
|
||||
LoggingGadgetWidget::~LoggingGadgetWidget()
|
||||
@ -51,6 +60,31 @@ void LoggingGadgetWidget::setPlugin(LoggingPlugin *p)
|
||||
{
|
||||
loggingPlugin = p;
|
||||
|
||||
connect(p->getLogfile(), SIGNAL(updateBeginAndEndtimes(quint32, quint32)), this, SLOT(updateBeginAndEndtimes(quint32, quint32)));
|
||||
connect(p->getLogfile(), SIGNAL(replayPosition(quint32)), this, SLOT(replayPosition(quint32)));
|
||||
connect(m_logging->playBackPosition, SIGNAL(valueChanged(int)), this, SLOT(sliderMoved(int)));
|
||||
connect(this, SIGNAL(resumeReplayFrom(quint32)), p->getLogfile(), SLOT(resumeReplayFrom(quint32)));
|
||||
|
||||
connect(this, SIGNAL(startReplay()), p->getLogfile(), SLOT(restartReplay()));
|
||||
connect(this, SIGNAL(stopReplay()), p->getLogfile(), SLOT(haltReplay()));
|
||||
connect(this, SIGNAL(pauseReplay()), p->getLogfile(), SLOT(pauseReplay()));
|
||||
connect(this, SIGNAL(resumeReplay()), p->getLogfile(), SLOT(resumeReplay()));
|
||||
|
||||
connect(this, SIGNAL(startReplay()), scpPlugin, SLOT(startPlotting()));
|
||||
connect(this, SIGNAL(stopReplay()), scpPlugin, SLOT(stopPlotting()));
|
||||
connect(this, SIGNAL(pauseReplay()), scpPlugin, SLOT(stopPlotting()));
|
||||
connect(this, SIGNAL(resumeReplay()), scpPlugin, SLOT(startPlotting()));
|
||||
|
||||
connect(m_logging->playButton, SIGNAL(clicked()), this, SLOT(playButton()));
|
||||
connect(m_logging->pauseButton, SIGNAL(clicked()), this, SLOT(pauseButton()));
|
||||
connect(m_logging->stopButton, SIGNAL(clicked()), this, SLOT(stopButton()));
|
||||
|
||||
connect(p->getLogfile(), SIGNAL(replayStarted()), this, SLOT(enableButtons()));
|
||||
connect(p->getLogfile(), SIGNAL(replayFinished()), this, SLOT(disableButtons()));
|
||||
connect(p->getLogfile(), SIGNAL(replayFinished()), scpPlugin, SLOT(stopPlotting()));
|
||||
|
||||
connect(m_logging->playbackSpeed, SIGNAL(valueChanged(double)), p->getLogfile(), SLOT(setReplaySpeed(double)));
|
||||
|
||||
connect(m_logging->playButton, &QPushButton::clicked, scpPlugin, &ScopeGadgetFactory::startPlotting);
|
||||
connect(m_logging->pauseButton, &QPushButton::clicked, scpPlugin, &ScopeGadgetFactory::stopPlotting);
|
||||
|
||||
@ -64,6 +98,46 @@ void LoggingGadgetWidget::setPlugin(LoggingPlugin *p)
|
||||
stateChanged(loggingPlugin->getState());
|
||||
}
|
||||
|
||||
void LoggingGadgetWidget::playButton()
|
||||
{
|
||||
ReplayState replayState = (loggingPlugin->getLogfile())->getReplayStatus();
|
||||
|
||||
if (replayState == STOPPED) {
|
||||
emit startReplay();
|
||||
} else if (replayState == PAUSED) {
|
||||
emit resumeReplay();
|
||||
}
|
||||
m_logging->playButton->setEnabled(false);
|
||||
m_logging->pauseButton->setEnabled(true);
|
||||
m_logging->stopButton->setEnabled(true);
|
||||
}
|
||||
|
||||
void LoggingGadgetWidget::pauseButton()
|
||||
{
|
||||
ReplayState replayState = (loggingPlugin->getLogfile())->getReplayStatus();
|
||||
|
||||
if (replayState == PLAYING) {
|
||||
emit pauseReplay();
|
||||
}
|
||||
m_logging->playButton->setEnabled(true);
|
||||
m_logging->pauseButton->setEnabled(false);
|
||||
m_logging->stopButton->setEnabled(true);
|
||||
}
|
||||
|
||||
void LoggingGadgetWidget::stopButton()
|
||||
{
|
||||
ReplayState replayState = (loggingPlugin->getLogfile())->getReplayStatus();
|
||||
|
||||
if (replayState != STOPPED) {
|
||||
emit stopReplay();
|
||||
}
|
||||
m_logging->playButton->setEnabled(true);
|
||||
m_logging->pauseButton->setEnabled(false);
|
||||
m_logging->stopButton->setEnabled(false);
|
||||
|
||||
replayPosition(0);
|
||||
}
|
||||
|
||||
void LoggingGadgetWidget::stateChanged(LoggingPlugin::State state)
|
||||
{
|
||||
QString status;
|
||||
@ -86,8 +160,122 @@ void LoggingGadgetWidget::stateChanged(LoggingPlugin::State state)
|
||||
bool playing = loggingPlugin->getLogfile()->isPlaying();
|
||||
m_logging->playButton->setEnabled(enabled && !playing);
|
||||
m_logging->pauseButton->setEnabled(enabled && playing);
|
||||
m_logging->stopButton->setEnabled(enabled && playing);
|
||||
}
|
||||
|
||||
void LoggingGadgetWidget::updateBeginAndEndtimes(quint32 startTimeStamp, quint32 endTimeStamp)
|
||||
{
|
||||
int startSec, startMin, endSec, endMin;
|
||||
|
||||
startSec = (startTimeStamp / 1000) % 60;
|
||||
startMin = startTimeStamp / (60 * 1000);
|
||||
|
||||
endSec = (endTimeStamp / 1000) % 60;
|
||||
endMin = endTimeStamp / (60 * 1000);
|
||||
|
||||
// update start and end labels
|
||||
m_logging->startTimeLabel->setText(QString("%1:%2").arg(startMin, 2, 10, QChar('0')).arg(startSec, 2, 10, QChar('0')));
|
||||
m_logging->endTimeLabel->setText(QString("%1:%2").arg(endMin, 2, 10, QChar('0')).arg(endSec, 2, 10, QChar('0')));
|
||||
|
||||
// Update position bar
|
||||
m_logging->playBackPosition->setMinimum(startTimeStamp);
|
||||
m_logging->playBackPosition->setMaximum(endTimeStamp);
|
||||
|
||||
m_logging->playBackPosition->setSingleStep((endTimeStamp - startTimeStamp) / 100);
|
||||
m_logging->playBackPosition->setPageStep((endTimeStamp - startTimeStamp) / 10);
|
||||
m_logging->playBackPosition->setTickInterval((endTimeStamp - startTimeStamp) / 10);
|
||||
m_logging->playBackPosition->setTickPosition(QSlider::TicksBothSides);
|
||||
}
|
||||
|
||||
void LoggingGadgetWidget::replayPosition(quint32 positionTimeStamp)
|
||||
{
|
||||
// Update position bar, but only if the user is not updating the slider position
|
||||
if (!m_logging->playBackPosition->isSliderDown() && !sliderActionDelay.isActive()) {
|
||||
// Block signals during slider position update:
|
||||
m_logging->playBackPosition->blockSignals(true);
|
||||
m_logging->playBackPosition->setValue(positionTimeStamp);
|
||||
m_logging->playBackPosition->blockSignals(false);
|
||||
|
||||
// update current position label
|
||||
updatePositionLabel(positionTimeStamp);
|
||||
}
|
||||
}
|
||||
|
||||
void LoggingGadgetWidget::enableButtons()
|
||||
{
|
||||
ReplayState replayState = (loggingPlugin->getLogfile())->getReplayStatus();
|
||||
|
||||
switch (replayState)
|
||||
{
|
||||
case STOPPED:
|
||||
m_logging->playButton->setEnabled(true);
|
||||
m_logging->pauseButton->setEnabled(false);
|
||||
m_logging->stopButton->setEnabled(false);
|
||||
break;
|
||||
|
||||
case PLAYING:
|
||||
m_logging->playButton->setEnabled(false);
|
||||
m_logging->pauseButton->setEnabled(true);
|
||||
m_logging->stopButton->setEnabled(true);
|
||||
break;
|
||||
|
||||
case PAUSED:
|
||||
m_logging->playButton->setEnabled(true);
|
||||
m_logging->pauseButton->setEnabled(false);
|
||||
m_logging->stopButton->setEnabled(true);
|
||||
break;
|
||||
}
|
||||
m_logging->playBackPosition->setEnabled(true);
|
||||
}
|
||||
|
||||
void LoggingGadgetWidget::disableButtons()
|
||||
{
|
||||
// m_logging->startTimeLabel->setText(QString(""));
|
||||
// m_logging->endTimeLabel->setText(QString(""));
|
||||
|
||||
m_logging->playButton->setEnabled(false);
|
||||
m_logging->pauseButton->setEnabled(false);
|
||||
m_logging->stopButton->setEnabled(false);
|
||||
|
||||
m_logging->playBackPosition->setEnabled(false);
|
||||
}
|
||||
|
||||
void LoggingGadgetWidget::sliderMoved(int position)
|
||||
{
|
||||
qDebug() << "sliderMoved(): start of function, stored position was: " << m_storedPosition;
|
||||
|
||||
m_storedPosition = position;
|
||||
// pause
|
||||
emit pauseButton();
|
||||
|
||||
updatePositionLabel(position);
|
||||
|
||||
// Start or restarts a time-out after which replay is resumed from the new position.
|
||||
sliderActionDelay.start();
|
||||
|
||||
qDebug() << "sliderMoved(): end of function, stored position is now: " << m_storedPosition;
|
||||
}
|
||||
|
||||
void LoggingGadgetWidget::updatePositionLabel(quint32 positionTimeStamp)
|
||||
{
|
||||
// update position timestamp label
|
||||
int sec = (positionTimeStamp / 1000) % 60;
|
||||
int min = positionTimeStamp / (60 * 1000);
|
||||
m_logging->positionTimestampLabel->setText(QString("%1:%2").arg(min, 2, 10, QChar('0')).arg(sec, 2, 10, QChar('0')));
|
||||
}
|
||||
|
||||
void LoggingGadgetWidget::sendResumeReplayFrom()
|
||||
{
|
||||
qDebug() << "sendResumeReplayFrom(): start of function, stored position is: " << m_storedPosition;
|
||||
|
||||
emit resumeReplayFrom(m_storedPosition);
|
||||
|
||||
emit resumeReplay();
|
||||
|
||||
qDebug() << "sendResumeReplayFrom(): end of function, stored position is: " << m_storedPosition;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <QWidget>
|
||||
|
||||
class Ui_Logging;
|
||||
class QTimer;
|
||||
|
||||
class LoggingGadgetWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
@ -48,15 +49,32 @@ public:
|
||||
|
||||
protected slots:
|
||||
void stateChanged(LoggingPlugin::State state);
|
||||
void updateBeginAndEndtimes(quint32 startTimeStamp, quint32 endTimeStamp);
|
||||
void replayPosition(quint32 positionTimeStamp);
|
||||
void playButton();
|
||||
void pauseButton();
|
||||
void stopButton();
|
||||
void enableButtons();
|
||||
void disableButtons();
|
||||
void sliderMoved(int);
|
||||
void sendResumeReplayFrom();
|
||||
|
||||
signals:
|
||||
void pause();
|
||||
void play();
|
||||
void startReplay();
|
||||
void stopReplay();
|
||||
void pauseReplay();
|
||||
void resumeReplay();
|
||||
void resumeReplayFrom(quint32 positionTimeStamp);
|
||||
|
||||
private:
|
||||
Ui_Logging *m_logging;
|
||||
LoggingPlugin *loggingPlugin;
|
||||
ScopeGadgetFactory *scpPlugin;
|
||||
QTimer sliderActionDelay;
|
||||
quint32 m_storedPosition;
|
||||
|
||||
void updatePositionLabel(quint32 positionTimeStamp);
|
||||
|
||||
};
|
||||
|
||||
#endif /* LoggingGADGETWIDGET_H_ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user