Commit 6d7fb36a authored by Don Gagne's avatar Don Gagne

New mavlink logging support

- Logging now always on to temp file
- User prompted to save temp file after disconnect
- Orphaned temp logs checked for on boot
- Logs saved to new save file location
- Log file starts/stops with first and last connnection
parent 84fb1ad7
This diff is collapsed.
......@@ -37,6 +37,8 @@ This file is part of the QGROUNDCONTROL project
#include <QFile>
#include <QMap>
#include <QByteArray>
#include <QTemporaryFile>
#include "ProtocolInterface.h"
#include "LinkInterface.h"
#include "QGCMAVLink.h"
......@@ -69,10 +71,7 @@ public:
bool heartbeatsEnabled() const {
return m_heartbeatsEnabled;
}
/** @brief Get logging state */
bool loggingEnabled() const {
return m_loggingEnabled;
}
/** @brief Get protocol version check state */
bool versionCheckEnabled() const {
return m_enable_version_check;
......@@ -93,8 +92,6 @@ public:
QString getAuthKey() {
return m_authKey;
}
/** @brief Get the name of the packet log file */
QString getLogfileName();
/** @brief Get state of parameter retransmission */
bool paramGuardEnabled() {
return m_paramGuardEnabled;
......@@ -140,7 +137,7 @@ public:
* Reset the counters for all metadata for this link.
*/
virtual void resetMetadataForLink(const LinkInterface *link);
void run();
public slots:
......@@ -161,9 +158,6 @@ public slots:
/** @brief Enable / disable the heartbeat emission */
void enableHeartbeats(bool enabled);
/** @brief Enable/disable binary packet logging */
void enableLogging(bool enabled);
/** @brief Enabled/disable packet multiplexing */
void enableMultiplexing(bool enabled);
......@@ -182,9 +176,6 @@ public slots:
/** @brief Set parameter read timeout */
void setActionRetransmissionTimeout(int ms);
/** @brief Set log file name */
void setLogfileName(const QString& filename);
/** @brief Enable / disable version check */
void enableVersionCheck(bool enabled);
......@@ -203,16 +194,22 @@ public slots:
void loadSettings();
/** @brief Store protocol settings */
void storeSettings();
/// @brief Suspend/Restart logging during replay. This must be emitted as a signal
/// and not called directly in order to synchronize with the bytesReady signal
/// which may be ahead of it in the signal queue.
void suspendLogForReplay(bool suspend);
protected:
// Override from QObject
virtual void connectNotify(const QMetaMethod& signal);
protected:
QTimer *heartbeatTimer; ///< Timer to emit heartbeats
int heartbeatRate; ///< Heartbeat rate, controls the timer interval
bool m_heartbeatsEnabled; ///< Enabled/disable heartbeat emission
bool m_multiplexingEnabled; ///< Enable/disable packet multiplexing
bool m_authEnabled; ///< Enable authentication token broadcast
QString m_authKey; ///< Authentication key
bool m_loggingEnabled; ///< Enable/disable packet logging
QFile* m_logfile; ///< Logfile
bool m_enable_version_check; ///< Enable checking of version match of MAV and QGC
int m_paramRetransmissionTimeout; ///< Timeout for parameter retransmission
int m_paramRewriteTimeout; ///< Timeout for sending re-write request
......@@ -235,8 +232,6 @@ signals:
void messageReceived(LinkInterface* link, mavlink_message_t message);
/** @brief Emitted if heartbeat emission mode is changed */
void heartbeatChanged(bool heartbeats);
/** @brief Emitted if logging is started / stopped */
void loggingChanged(bool enabled);
/** @brief Emitted if multiplexing is started / stopped */
void multiplexingChanged(bool enabled);
/** @brief Emitted if authentication support is enabled / disabled */
......@@ -272,6 +267,27 @@ signals:
*/
void radioStatusChanged(LinkInterface* link, unsigned rxerrors, unsigned fixed, unsigned rssi, unsigned remrssi,
unsigned txbuf, unsigned noise, unsigned remnoise);
/// @brief Emitted when a temporary log file is ready for saving
void saveTempFlightDataLog(QString tempLogfile);
private:
bool _closeLogFile(void);
void _startLogging(void);
void _stopLogging(void);
void _checkLostLogFiles(void);
QList<LinkInterface*> _connectedLinks; ///< List of all links connected to protocol
bool _logSuspendError; ///< true: Logging suspended due to error
bool _logSuspendReplay; ///< true: Logging suspended due to replay
QTemporaryFile _tempLogFile; ///< File to log to
static const char* _tempLogFileTemplate; ///< Template for temporary log file
static const char* _logFileExtension; ///< Extension for log files
bool _protocolStatusMessageConnected; ///< true: protocolStatusMessage signal has been connected
bool _saveTempFlightDataLogConnected; ///< true: saveTempFlightDataLog signal has been connected
};
#endif // MAVLINKPROTOCOL_H_
......@@ -55,7 +55,6 @@ MAVLinkSettingsWidget::MAVLinkSettingsWidget(MAVLinkProtocol* protocol, QWidget
// Initialize state
m_ui->heartbeatCheckBox->setChecked(protocol->heartbeatsEnabled());
m_ui->loggingCheckBox->setChecked(protocol->loggingEnabled());
m_ui->versionCheckBox->setChecked(protocol->versionCheckEnabled());
m_ui->multiplexingCheckBox->setChecked(protocol->multiplexingEnabled());
m_ui->systemIdSpinBox->setValue(protocol->getSystemId());
......@@ -71,14 +70,9 @@ MAVLinkSettingsWidget::MAVLinkSettingsWidget(MAVLinkProtocol* protocol, QWidget
// Heartbeat
connect(protocol, SIGNAL(heartbeatChanged(bool)), m_ui->heartbeatCheckBox, SLOT(setChecked(bool)));
connect(m_ui->heartbeatCheckBox, SIGNAL(toggled(bool)), protocol, SLOT(enableHeartbeats(bool)));
// Logging
connect(protocol, SIGNAL(loggingChanged(bool)), m_ui->loggingCheckBox, SLOT(setChecked(bool)));
connect(m_ui->loggingCheckBox, SIGNAL(toggled(bool)), protocol, SLOT(enableLogging(bool)));
// Version check
connect(protocol, SIGNAL(versionCheckChanged(bool)), m_ui->versionCheckBox, SLOT(setChecked(bool)));
connect(m_ui->versionCheckBox, SIGNAL(toggled(bool)), protocol, SLOT(enableVersionCheck(bool)));
// Logfile
connect(m_ui->logFileButton, SIGNAL(clicked()), this, SLOT(chooseLogfileName()));
// System ID
connect(protocol, SIGNAL(systemIdChanged(int)), m_ui->systemIdSpinBox, SLOT(setValue(int)));
connect(m_ui->systemIdSpinBox, SIGNAL(valueChanged(int)), protocol, SLOT(setSystemId(int)));
......@@ -110,16 +104,10 @@ MAVLinkSettingsWidget::MAVLinkSettingsWidget(MAVLinkProtocol* protocol, QWidget
// Update values
m_ui->versionLabel->setText(tr("MAVLINK_VERSION: %1").arg(protocol->getVersion()));
updateLogfileName(protocol->getLogfileName());
// Connect visibility updates
connect(protocol, SIGNAL(versionCheckChanged(bool)), m_ui->versionLabel, SLOT(setVisible(bool)));
m_ui->versionLabel->setVisible(protocol->versionCheckEnabled());
// Logging visibility
connect(protocol, SIGNAL(loggingChanged(bool)), m_ui->logFileLabel, SLOT(setVisible(bool)));
m_ui->logFileLabel->setVisible(protocol->loggingEnabled());
connect(protocol, SIGNAL(loggingChanged(bool)), m_ui->logFileButton, SLOT(setVisible(bool)));
m_ui->logFileButton->setVisible(protocol->loggingEnabled());
// // Multiplexing visibility
// connect(protocol, SIGNAL(multiplexingChanged(bool)), m_ui->multiplexingFilterCheckBox, SLOT(setVisible(bool)));
// m_ui->multiplexingFilterCheckBox->setVisible(protocol->multiplexingEnabled());
......@@ -146,39 +134,6 @@ MAVLinkSettingsWidget::MAVLinkSettingsWidget(MAVLinkProtocol* protocol, QWidget
m_ui->multiplexingFilterLineEdit->setVisible(false);
}
void MAVLinkSettingsWidget::updateLogfileName(const QString& fileName)
{
QFileInfo file(fileName);
m_ui->logFileLabel->setText(file.fileName());
}
void MAVLinkSettingsWidget::chooseLogfileName()
{
QString fileName = QFileDialog::getSaveFileName(this, tr("Specify MAVLink log file name"), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation), tr("MAVLink Logfile (*.mavlink);;"));
if (!fileName.endsWith(".mavlink"))
{
fileName.append(".mavlink");
}
QFileInfo file(fileName);
if (file.exists() && !file.isWritable())
{
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Critical);
msgBox.setText(tr("The selected logfile is not writable"));
msgBox.setInformativeText(tr("Please make sure that the file %1 is writable or select a different file").arg(fileName));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
}
else
{
updateLogfileName(fileName);
protocol->setLogfileName(fileName);
}
}
void MAVLinkSettingsWidget::enableDroneOS(bool enable)
{
// Enable multiplexing
......
......@@ -18,10 +18,6 @@ public:
~MAVLinkSettingsWidget();
public slots:
/** @brief Update the log file name display */
void updateLogfileName(const QString& fileName);
/** @brief Start the file select dialog for the log file */
void chooseLogfileName();
/** @brief Enable DroneOS forwarding */
void enableDroneOS(bool enable);
......
This diff is collapsed.
This diff is collapsed.
......@@ -100,31 +100,18 @@ public:
CUSTOM_MODE_WIFI
};
/**
* A static function for obtaining the sole instance of the MainWindow. The screen
* argument is only important on the FIRST call to this function. The provided splash
* screen is updated with some status messages that are emitted during init(). This
* function cannot be used within the MainWindow constructor!
*/
static MainWindow* instance(QSplashScreen* screen = 0);
static MainWindow* instance_mode(QSplashScreen* screen = 0, enum MainWindow::CUSTOM_MODE mode = MainWindow::CUSTOM_MODE_NONE);
/// @brief Returns the MainWindow singleton. Will not create the MainWindow if it has not already
/// been created.
static MainWindow* instance(void);
/// @brief Creates the MainWindow singleton. Should only be called once by QGCApplication.
static MainWindow* _create(QSplashScreen* splashScreen, enum MainWindow::CUSTOM_MODE mode);
/// @brief Called to indicate that splash screen is no longer being displayed.
void splashScreenFinished(void) { _splashScreen = NULL; }
/**
* Initializes the MainWindow. Some variables are initialized and the widget is hidden.
* Initialization of the MainWindow class really occurs in init(), which loads the UI
* and does everything important. The constructor is split in two like this so that
* the instance() is available for all classes.
*/
MainWindow(QWidget *parent = NULL);
~MainWindow();
/**
* This function actually performs the non-trivial initialization of the MainWindow
* class. This is separate from the constructor because instance() won't work within
* code executed in the MainWindow constructor.
*/
void init();
enum QGC_MAINWINDOW_STYLE
{
QGC_MAINWINDOW_STYLE_DARK,
......@@ -272,7 +259,7 @@ public slots:
void configureWindowName();
void commsWidgetDestroyed(QObject *obj);
protected slots:
void showDockWidget(const QString &name, bool show);
/**
......@@ -475,8 +462,18 @@ protected:
QGCFlightGearLink* fgLink;
QTimer windowNameUpdateTimer;
CUSTOM_MODE customMode;
private slots:
/// @brief Save the specified Flight Data Log
void _saveTempFlightDataLog(QString tempLogfile);
private:
/// Constructor is private since all creation should be through MainWindow::instance.
MainWindow(QSplashScreen* splashScreen, enum MainWindow::CUSTOM_MODE mode);
void _hideSplashScreen(void);
void _openUrl(const QString& url, const QString& errorMessage);
QList<QObject*> commsWidgetList;
QMap<QString,QString> customWidgetNameToFilenameMap;
MenuActionHelper *menuActionHelper;
......@@ -489,6 +486,8 @@ private:
QString getWindowStateKey();
QString getWindowGeometryKey();
QSplashScreen* _splashScreen; ///< Splash screen, NULL is splash screen not currently being shown
friend class MenuActionHelper; //For VIEW_SECTIONS
};
......
This diff is collapsed.
......@@ -37,29 +37,15 @@ public:
return logFile.isOpen();
}
/**
* @brief Set the last log file name
* @param filename
*/
void setLastLogFile(const QString& filename) {
lastLogDirectory = filename;
}
public slots:
/** @brief Toggle between play and pause */
void playPauseToggle();
/** @brief Play / pause the log */
void playPause(bool play);
/** @brief Replay the logfile */
void play();
/** @brief Pause the log player. */
void pause();
/** @brief Reset the internal log player state, including the UI */
void reset();
/** @brief Select logfile */
bool selectLogFile(const QString startDirectory);
/** @brief Select logfile */
bool selectLogFile();
/** @brief Load log file */
bool loadLogFile(const QString& file);
/** @brief Jump to a position in the logfile */
......@@ -73,6 +59,9 @@ signals:
/** @brief Send ready bytes */
void bytesReady(LinkInterface* link, const QByteArray& bytes);
void logFileEndReached();
/// @brief Connected to the MAVLinkProtocol::suspendLogForReplay
void suspendLogForReplay(bool suspend);
protected:
quint64 playbackStartTime; ///< 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. In units of milliseconds since epoch UTC.
......@@ -92,11 +81,10 @@ protected:
unsigned int currPacketCount;
static const int packetLen = MAVLINK_MAX_PACKET_LEN;
static const int timeLen = sizeof(quint64);
QString lastLogDirectory;
void changeEvent(QEvent *e);
void loadSettings();
void storeSettings();
private slots:
void _selectLogFileForPlayback(void);
private:
Ui::QGCMAVLinkLogPlayer *ui;
......@@ -130,6 +118,9 @@ private:
* @return True if the new file position was successfully jumped to, false otherwise
*/
bool jumpToPlaybackLocation(float percentage);
void _finishPlayback(void);
void _playbackError(void);
};
#endif // QGCMAVLINKLOGPLAYER_H
......@@ -36,13 +36,13 @@
<item>
<widget class="QToolButton" name="playButton">
<property name="toolTip">
<string>Start to replay the logfile</string>
<string>Start to replay Flight Data</string>
</property>
<property name="statusTip">
<string>Start to replay the logfile</string>
<string>Start to replay Flight Data</string>
</property>
<property name="whatsThis">
<string>Start to replay the logfile</string>
<string>Start to replay Flight Data</string>
</property>
<property name="text">
<string>...</string>
......@@ -129,23 +129,23 @@
<item>
<widget class="QLabel" name="logFileNameLabel">
<property name="text">
<string>No logfile selected..</string>
<string>No Flight Data selected..</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="selectFileButton">
<property name="toolTip">
<string>Select the logfile to replay</string>
<string>Select the Flight Data to replay</string>
</property>
<property name="statusTip">
<string>Select the logfile to replay</string>
<string>Select the Flight Data to replay</string>
</property>
<property name="whatsThis">
<string>Select the logfile to replay</string>
<string>Select the Flight Data to replay</string>
</property>
<property name="text">
<string>Replay Logfile</string>
<string>Replay Flight Data</string>
</property>
</widget>
</item>
......
......@@ -27,21 +27,15 @@ This file is part of the QGROUNDCONTROL project
#include "QGCStatusBar.h"
#include "UASManager.h"
#include "MainWindow.h"
#include "QGCCore.h"
QGCStatusBar::QGCStatusBar(QWidget *parent) :
QStatusBar(parent),
toggleLoggingButton(NULL),
player(NULL),
changed(true),
lastLogDirectory(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation))
{
setObjectName("QGC_STATUSBAR");
toggleLoggingButton = new QPushButton(tr("Log to file"), this);
toggleLoggingButton->setCheckable(true);
addPermanentWidget(toggleLoggingButton);
loadSettings();
}
......@@ -58,58 +52,6 @@ void QGCStatusBar::setLogPlayer(QGCMAVLinkLogPlayer* player)
{
this->player = player;
addPermanentWidget(player);
connect(toggleLoggingButton, SIGNAL(clicked(bool)), this, SLOT(logging(bool)));
}
void QGCStatusBar::logging(bool checked)
{
// Stop logging in any case
MainWindow::instance()->getMAVLink()->enableLogging(false);
if (!checked && player)
{
player->setLastLogFile(lastLogDirectory);
}
// If the user is enabling logging
if (checked)
{
// Prompt the user for a filename/location to save to
QString fileName = QFileDialog::getSaveFileName(this, tr("Specify MAVLink log file to save to"), lastLogDirectory, tr("MAVLink Logfile (*.mavlink *.log *.bin);;"));
// Check that they didn't cancel out
if (fileName.isNull())
{
toggleLoggingButton->setChecked(false);
return;
}
// Make sure the file's named properly
if (!fileName.endsWith(".mavlink"))
{
fileName.append(".mavlink");
}
// Check that we can save the logfile
QFileInfo file(fileName);
if ((file.exists() && !file.isWritable()))
{
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Critical);
msgBox.setText(tr("The selected logfile is not writable"));
msgBox.setInformativeText(tr("Please make sure that the file %1 is writable or select a different file").arg(fileName));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.exec();
}
// Otherwise we're off and logging
else
{
MainWindow::instance()->getMAVLink()->setLogfileName(fileName);
MainWindow::instance()->getMAVLink()->enableLogging(true);
lastLogDirectory = file.absoluteDir().absolutePath(); //save last log directory
}
}
}
void QGCStatusBar::loadSettings()
......@@ -132,5 +74,4 @@ void QGCStatusBar::storeSettings()
QGCStatusBar::~QGCStatusBar()
{
storeSettings();
if (toggleLoggingButton) toggleLoggingButton->deleteLater();
}
......@@ -43,8 +43,6 @@ public:
~QGCStatusBar();
public slots:
/** @brief Start / stop logging */
void logging(bool checked);
/** @brief Set log playing component */
void setLogPlayer(QGCMAVLinkLogPlayer* player);
virtual void paintEvent(QPaintEvent * event);
......@@ -53,7 +51,6 @@ protected:
void storeSettings();
void loadSettings();
QPushButton* toggleLoggingButton;
QGCMAVLinkLogPlayer* player;
bool changed;
QString lastLogDirectory;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment