LogReplayLink.h 7.34 KB
Newer Older
1 2
/****************************************************************************
 *
3
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4 5 6 7 8
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/
Don Gagne's avatar
Don Gagne committed
9

10
#pragma once
Don Gagne's avatar
Don Gagne committed
11 12 13 14 15 16

#include "MAVLinkProtocol.h"

#include <QTimer>
#include <QFile>

17 18
class LinkManager;

Don Gagne's avatar
Don Gagne committed
19 20
class LogReplayLinkConfiguration : public LinkConfiguration
{
Don Gagne's avatar
Don Gagne committed
21 22
    Q_OBJECT

Don Gagne's avatar
Don Gagne committed
23
public:
24 25
    Q_PROPERTY(QString  fileName    READ logFilename    WRITE setLogFilename    NOTIFY fileNameChanged)

Don Gagne's avatar
Don Gagne committed
26 27
    LogReplayLinkConfiguration(const QString& name);
    LogReplayLinkConfiguration(LogReplayLinkConfiguration* copy);
28

Don Gagne's avatar
Don Gagne committed
29
    QString logFilename(void) { return _logFilename; }
30
    void setLogFilename(const QString logFilename) { _logFilename = logFilename; emit fileNameChanged(); }
31

Don Gagne's avatar
Don Gagne committed
32 33 34
    QString logFilenameShort(void);

    // Virtuals from LinkConfiguration
35 36 37 38 39 40 41
    LinkType    type                    (void) override                                         { return LinkConfiguration::TypeLogReplay; }
    void        copyFrom                (LinkConfiguration* source) override;
    void        loadSettings            (QSettings& settings, const QString& root) override;
    void        saveSettings            (QSettings& settings, const QString& root) override;
    QString     settingsURL             (void) override                                         { return "LogReplaySettings.qml"; }
    QString     settingsTitle           (void) override                                          { return tr("Log Replay Link Settings"); }

42 43
signals:
    void fileNameChanged();
Don Gagne's avatar
Don Gagne committed
44 45 46 47 48 49

private:
    static const char*  _logFilenameKey;
    QString             _logFilename;
};

50
/// Pseudo link that reads a telemetry log and feeds it into the application.
Don Gagne's avatar
Don Gagne committed
51 52 53 54 55
class LogReplayLink : public LinkInterface
{
    Q_OBJECT

    friend class LinkManager;
56

Don Gagne's avatar
Don Gagne committed
57
public:
58 59
    ~LogReplayLink();

Don Gagne's avatar
Don Gagne committed
60 61
    /// @return true: log is currently playing, false: log playback is paused
    bool isPlaying(void) { return _readTickTimer.isActive(); }
62

63 64 65
    void play           (void) { emit _playOnThread(); }
    void pause          (void) { emit _pauseOnThread(); }
    void movePlayhead   (qreal percentComplete);
66

67 68 69 70
    // overrides from LinkInterface
    bool isConnected(void) const override { return _connected; }
    bool isLogReplay(void) override { return true; }
    void disconnect (void) override;
71

72
public slots:
Don Gagne's avatar
Don Gagne committed
73
    /// Sets the acceleration factor: -100: 0.01X, 0: 1.0X, 100: 100.0X
74
    void setPlaybackSpeed(qreal playbackSpeed) { emit _setPlaybackSpeedOnThread(playbackSpeed); }
75

Don Gagne's avatar
Don Gagne committed
76
signals:
77 78 79 80 81 82
    void logFileStats                   (int logDurationSecs);
    void playbackStarted                (void);
    void playbackPaused                 (void);
    void playbackAtEnd                  (void);
    void playbackPercentCompleteChanged (qreal percentComplete);
    void currentLogTimeSecs             (int secs);
83

Don Gagne's avatar
Don Gagne committed
84
    // Internal signals
85 86 87
    void _playOnThread              (void);
    void _pauseOnThread             (void);
    void _setPlaybackSpeedOnThread  (qreal playbackSpeed);
Don Gagne's avatar
Don Gagne committed
88 89

private slots:
90 91 92 93 94 95 96
    // LinkInterface overrides
    void _writeBytes(const QByteArray bytes) override;

    void _readNextLogEntry  (void);
    void _play              (void);
    void _pause             (void);
    void _setPlaybackSpeed  (qreal playbackSpeed);
Don Gagne's avatar
Don Gagne committed
97 98 99

private:
    // Links are only created/destroyed by LinkManager so constructor/destructor is not public
100
    LogReplayLink(SharedLinkConfigurationPtr& config);
101

102 103
    // LinkInterface overrides
    bool _connect(void) override;
104

105 106 107 108 109 110 111 112 113
    void    _replayError                (const QString& errorMsg);
    quint64 _parseTimestamp             (const QByteArray& bytes);
    quint64 _seekToNextMavlinkMessage   (mavlink_message_t* nextMsg);
    quint64 _findLastTimestamp          (void);
    quint64 _readNextMavlinkMessage     (QByteArray& bytes);
    bool    _loadLogFile                (void);
    void    _finishPlayback             (void);
    void    _resetPlaybackToBeginning   (void);
    void    _signalCurrentLogTimeSecs   (void);
114

115 116
    // QThread overrides
    void run(void) override;
117

118
    LogReplayLinkConfiguration* _logReplayConfig;
Don Gagne's avatar
Don Gagne committed
119 120

    bool    _connected;
121
    uint8_t _mavlinkChannel;
Don Gagne's avatar
Don Gagne committed
122
    QTimer  _readTickTimer;      ///< Timer which signals a read of next log record
123

124
    QString _errorTitle; ///< Title for communicatorError signals
125

Don Gagne's avatar
Don Gagne committed
126 127 128 129
    quint64 _logCurrentTimeUSecs;   ///< The timestamp of the next message in the log file.
    quint64 _logStartTimeUSecs;     ///< The first timestamp in the current log file.
    quint64 _logEndTimeUSecs;       ///< The last timestamp in the current log file.
    quint64 _logDurationUSecs;
130

131
    qreal   _playbackSpeed;
Don Gagne's avatar
Don Gagne committed
132
    quint64 _playbackStartTimeMSecs;    ///< The time when the logfile was first played back. This is used to pace out replaying the messages to fix long-term drift/skew. 0 indicates that the player hasn't initiated playback of this log file.
133
    quint64 _playbackStartLogTimeUSecs;
134

Don Gagne's avatar
Don Gagne committed
135 136 137
    MAVLinkProtocol*    _mavlink;
    QFile               _logFile;
    quint64             _logFileSize;
138

Don Gagne's avatar
Don Gagne committed
139 140 141
    static const int cbTimestamp = sizeof(quint64);
};

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
class LogReplayLinkController : public QObject
{
    Q_OBJECT

public:
    Q_PROPERTY(LogReplayLink*   link            READ link               WRITE setLink               NOTIFY linkChanged)
    Q_PROPERTY(bool             isPlaying       READ isPlaying          WRITE setIsPlaying          NOTIFY isPlayingChanged)
    Q_PROPERTY(qreal            percentComplete READ percentComplete    WRITE setPercentComplete    NOTIFY percentCompleteChanged)
    Q_PROPERTY(QString          totalTime       MEMBER _totalTime                                   NOTIFY totalTimeChanged)
    Q_PROPERTY(QString          playheadTime    MEMBER _playheadTime                                NOTIFY playheadTimeChanged)
    Q_PROPERTY(qreal            playbackSpeed   MEMBER _playbackSpeed                               NOTIFY playbackSpeedChanged)

    LogReplayLinkController(void);

    LogReplayLink*  link            (void) { return _link; }
    bool            isPlaying       (void) { return _isPlaying; }
    qreal           percentComplete (void) { return _percentComplete; }

    void setLink            (LogReplayLink* link);
    void setIsPlaying       (bool isPlaying);
    void setPercentComplete (qreal percentComplete);

signals:
    void linkChanged            (LogReplayLink* link);
    void isPlayingChanged       (bool isPlaying);
    void percentCompleteChanged (qreal percentComplete);
    void playheadTimeChanged    (QString playheadTime);
    void totalTimeChanged       (QString totalTime);
    void playbackSpeedChanged   (qreal playbackSpeed);

private slots:
    void _logFileStats                   (int logDurationSecs);
    void _playbackStarted                (void);
    void _playbackPaused                 (void);
    void _playbackAtEnd                  (void);
    void _playbackPercentCompleteChanged (qreal percentComplete);
    void _currentLogTimeSecs             (int secs);
    void _linkDisconnected               (void);

private:
    QString _secondsToHMS(int seconds);

    LogReplayLink*  _link;
    bool            _isPlaying;
    qreal           _percentComplete;
    int             _playheadSecs;
    QString         _playheadTime;
    QString         _totalTime;
    qreal           _playbackSpeed;
};