2 ******************************************************************************
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017.
6 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
7 * @see The GNU Public License (GPL) Version 3
9 *****************************************************************************/
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 LogFile::LogFile(QObject
*parent
) : QIODevice(parent
),
32 m_previousTimeStamp(0),
38 m_useProvidedTimeStamp(false),
39 m_providedTimeStamp(0)
41 connect(&m_timer
, &QTimer::timeout
, this, &LogFile::timerFired
);
44 bool LogFile::isSequential() const
46 // returning true fixes "UAVTalk - error : bad type" errors when replaying a log file
51 * Opens the logfile QIODevice and the underlying logfile. In case
52 * we want to save the logfile, we open in WriteOnly. In case we
53 * want to read the logfile, we open in ReadOnly.
55 bool LogFile::open(OpenMode mode
)
57 // start a timer for playback
59 if (m_file
.isOpen()) {
60 // We end up here when doing a replay, because the connection
61 // manager will also try to open the QIODevice, even though we just
62 // opened it after selecting the file, which happens before the
63 // connection manager call...
66 qDebug() << "LogFile - open" << fileName();
68 if (m_file
.open(mode
) == false) {
69 qWarning() << "Unable to open " << m_file
.fileName() << " for logging";
73 // TODO: Write a header at the beginng describing objects so that in future
74 // they can be read back if ID's change
76 // Must call parent function for QIODevice to pass calls to writeData
77 // We always open ReadWrite, because otherwise we will get tons of warnings
78 // during a logfile replay. Read nature is checked upon write ops below.
79 QIODevice::open(QIODevice::ReadWrite
);
86 qDebug() << "LogFile - close" << fileName();
92 qint64
LogFile::writeData(const char *data
, qint64 dataSize
)
94 if (!m_file
.isWritable()) {
98 // If needed, use provided timestamp instead of the GCS timer
99 // This is used when saving logs from on-board logging
100 quint32 timeStamp
= m_useProvidedTimeStamp
? m_providedTimeStamp
: m_myTime
.elapsed();
102 m_file
.write((char *)&timeStamp
, sizeof(timeStamp
));
103 m_file
.write((char *)&dataSize
, sizeof(dataSize
));
105 qint64 written
= m_file
.write(data
, dataSize
);
107 // flush (needed to avoid UAVTalk device full errors)
111 emit
bytesWritten(written
);
117 qint64
LogFile::readData(char *data
, qint64 maxlen
)
119 QMutexLocker
locker(&m_mutex
);
121 qint64 len
= qMin(maxlen
, (qint64
)m_dataBuffer
.size());
124 memcpy(data
, m_dataBuffer
.data(), len
);
125 m_dataBuffer
.remove(0, len
);
131 qint64
LogFile::bytesAvailable() const
133 QMutexLocker
locker(&m_mutex
);
135 qint64 len
= m_dataBuffer
.size();
140 void LogFile::timerFired()
142 if (m_file
.bytesAvailable() > 4) {
144 time
= m_myTime
.elapsed();
146 // TODO: going back in time will be a problem
147 while ((m_lastPlayed
+ ((double)(time
- m_timeOffset
) * m_playbackSpeed
) > m_nextTimeStamp
)) {
148 m_lastPlayed
+= ((double)(time
- m_timeOffset
) * m_playbackSpeed
);
152 if (m_file
.bytesAvailable() < (qint64
)sizeof(dataSize
)) {
153 qDebug() << "LogFile - end of log file reached";
157 m_file
.read((char *)&dataSize
, sizeof(dataSize
));
159 // check size consistency
160 if (dataSize
< 1 || dataSize
> (1024 * 1024)) {
161 qWarning() << "LogFile - corrupted log file! Unlikely packet size:" << dataSize
;
167 if (m_file
.bytesAvailable() < dataSize
) {
168 qDebug() << "LogFile - end of log file reached";
172 QByteArray data
= m_file
.read(dataSize
);
174 // make data available
176 m_dataBuffer
.append(data
);
181 // read next timestamp
182 if (m_file
.bytesAvailable() < (qint64
)sizeof(m_nextTimeStamp
)) {
183 qDebug() << "LogFile - end of log file reached";
187 m_previousTimeStamp
= m_nextTimeStamp
;
188 m_file
.read((char *)&m_nextTimeStamp
, sizeof(m_nextTimeStamp
));
190 // some validity checks
191 if ((m_nextTimeStamp
< m_previousTimeStamp
) // logfile goes back in time
192 || ((m_nextTimeStamp
- m_previousTimeStamp
) > 60 * 60 * 1000)) { // gap of more than 60 minutes
193 qWarning() << "LogFile - corrupted log file! Unlikely timestamp:" << m_nextTimeStamp
<< "after" << m_previousTimeStamp
;
199 time
= m_myTime
.elapsed();
202 qDebug() << "LogFile - end of log file reached";
207 bool LogFile::isPlaying() const
209 return m_file
.isOpen() && m_timer
.isActive();
212 bool LogFile::startReplay()
214 if (!m_file
.isOpen() || m_timer
.isActive()) {
217 qDebug() << "LogFile - startReplay";
222 m_previousTimeStamp
= 0;
224 m_dataBuffer
.clear();
226 // read next timestamp
227 if (m_file
.bytesAvailable() < (qint64
)sizeof(m_nextTimeStamp
)) {
228 qWarning() << "LogFile - invalid log file!";
231 m_file
.read((char *)&m_nextTimeStamp
, sizeof(m_nextTimeStamp
));
233 m_timer
.setInterval(10);
237 emit
replayStarted();
241 bool LogFile::stopReplay()
243 if (!m_file
.isOpen() || !(m_timer
.isActive() || paused
)) {
246 qDebug() << "LogFile - stopReplay";
250 emit
replayFinished();
254 bool LogFile::pauseReplay()
256 if (!m_timer
.isActive()) {
259 qDebug() << "LogFile - pauseReplay";
263 // hack to notify UI that replay paused
264 emit
replayStarted();
268 bool LogFile::resumeReplay()
270 if (m_timer
.isActive()) {
273 qDebug() << "LogFile - resumeReplay";
274 m_timeOffset
= m_myTime
.elapsed();
278 // hack to notify UI that replay resumed
279 emit
replayStarted();