1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2025-02-20 10:54:14 +01:00

Merge branch 'thread/OP-1119_Flight_Side_Logs_Plugin' into next

This commit is contained in:
Fredrik Arvidsson 2013-12-07 19:12:15 +01:00
commit 1d6df555eb
21 changed files with 1238 additions and 80 deletions

View File

@ -4,12 +4,14 @@
LogFile::LogFile(QObject *parent) :
QIODevice(parent),
lastTimeStamp(0),
lastPlayed(0),
timeOffset(0),
playbackSpeed(1.0)
m_lastTimeStamp(0),
m_lastPlayed(0),
m_timeOffset(0),
m_playbackSpeed(1.0),
m_nextTimeStamp(0),
m_useProvidedTimeStamp(false)
{
connect(&timer, SIGNAL(timeout()), this, SLOT(timerFired()));
connect(&m_timer, SIGNAL(timeout()), this, SLOT(timerFired()));
}
/**
@ -20,8 +22,8 @@ LogFile::LogFile(QObject *parent) :
bool LogFile::open(OpenMode mode)
{
// start a timer for playback
myTime.restart();
if (file.isOpen()) {
m_myTime.restart();
if (m_file.isOpen()) {
// We end up here when doing a replay, because the connection
// manager will also try to open the QIODevice, even though we just
// opened it after selecting the file, which happens before the
@ -29,8 +31,8 @@ bool LogFile::open(OpenMode mode)
return true;
}
if (file.open(mode) == false) {
qDebug() << "Unable to open " << file.fileName() << " for logging";
if (m_file.open(mode) == false) {
qDebug() << "Unable to open " << m_file.fileName() << " for logging";
return false;
}
@ -49,25 +51,27 @@ void LogFile::close()
{
emit aboutToClose();
if (timer.isActive()) {
timer.stop();
if (m_timer.isActive()) {
m_timer.stop();
}
file.close();
m_file.close();
QIODevice::close();
}
qint64 LogFile::writeData(const char *data, qint64 dataSize)
{
if (!file.isWritable()) {
if (!m_file.isWritable()) {
return dataSize;
}
quint32 timeStamp = myTime.elapsed();
// If m_nextTimeStamp != -1 then use this timestamp instead of the timer
// This is used when saving logs from on-board logging
quint32 timeStamp = m_useProvidedTimeStamp ? m_nextTimeStamp : m_myTime.elapsed();
file.write((char *)&timeStamp, sizeof(timeStamp));
file.write((char *)&dataSize, sizeof(dataSize));
m_file.write((char *)&timeStamp, sizeof(timeStamp));
m_file.write((char *)&dataSize, sizeof(dataSize));
qint64 written = file.write(data, dataSize);
qint64 written = m_file.write(data, dataSize);
if (written != -1) {
emit bytesWritten(written);
}
@ -77,36 +81,36 @@ qint64 LogFile::writeData(const char *data, qint64 dataSize)
qint64 LogFile::readData(char *data, qint64 maxSize)
{
QMutexLocker locker(&mutex);
qint64 toRead = qMin(maxSize, (qint64)dataBuffer.size());
QMutexLocker locker(&m_mutex);
qint64 toRead = qMin(maxSize, (qint64)m_dataBuffer.size());
memcpy(data, dataBuffer.data(), toRead);
dataBuffer.remove(0, toRead);
memcpy(data, m_dataBuffer.data(), toRead);
m_dataBuffer.remove(0, toRead);
return toRead;
}
qint64 LogFile::bytesAvailable() const
{
return dataBuffer.size();
return m_dataBuffer.size();
}
void LogFile::timerFired()
{
qint64 dataSize;
if (file.bytesAvailable() > 4) {
if (m_file.bytesAvailable() > 4) {
int time;
time = myTime.elapsed();
time = m_myTime.elapsed();
// TODO: going back in time will be a problem
while ((lastPlayed + ((time - timeOffset) * playbackSpeed) > lastTimeStamp)) {
lastPlayed += ((time - timeOffset) * playbackSpeed);
if (file.bytesAvailable() < sizeof(dataSize)) {
while ((m_lastPlayed + ((time - m_timeOffset) * m_playbackSpeed) > m_lastTimeStamp)) {
m_lastPlayed += ((time - m_timeOffset) * m_playbackSpeed);
if (m_file.bytesAvailable() < sizeof(dataSize)) {
stopReplay();
return;
}
file.read((char *)&dataSize, sizeof(dataSize));
m_file.read((char *)&dataSize, sizeof(dataSize));
if (dataSize < 1 || dataSize > (1024 * 1024)) {
qDebug() << "Error: Logfile corrupted! Unlikely packet size: " << dataSize << "\n";
@ -114,34 +118,34 @@ void LogFile::timerFired()
return;
}
if (file.bytesAvailable() < dataSize) {
if (m_file.bytesAvailable() < dataSize) {
stopReplay();
return;
}
mutex.lock();
dataBuffer.append(file.read(dataSize));
mutex.unlock();
m_mutex.lock();
m_dataBuffer.append(m_file.read(dataSize));
m_mutex.unlock();
emit readyRead();
if (file.bytesAvailable() < sizeof(lastTimeStamp)) {
if (m_file.bytesAvailable() < sizeof(m_lastTimeStamp)) {
stopReplay();
return;
}
int save = lastTimeStamp;
file.read((char *)&lastTimeStamp, sizeof(lastTimeStamp));
int save = m_lastTimeStamp;
m_file.read((char *)&m_lastTimeStamp, sizeof(m_lastTimeStamp));
// some validity checks
if (lastTimeStamp < save // logfile goes back in time
|| (lastTimeStamp - save) > (60 * 60 * 1000)) { // gap of more than 60 minutes)
qDebug() << "Error: Logfile corrupted! Unlikely timestamp " << lastTimeStamp << " after " << save << "\n";
if (m_lastTimeStamp < save // logfile goes back in time
|| (m_lastTimeStamp - save) > (60 * 60 * 1000)) { // gap of more than 60 minutes)
qDebug() << "Error: Logfile corrupted! Unlikely timestamp " << m_lastTimeStamp << " after " << save << "\n";
stopReplay();
return;
}
timeOffset = time;
time = myTime.elapsed();
m_timeOffset = time;
time = m_myTime.elapsed();
}
} else {
stopReplay();
@ -150,13 +154,13 @@ void LogFile::timerFired()
bool LogFile::startReplay()
{
dataBuffer.clear();
myTime.restart();
timeOffset = 0;
lastPlayed = 0;
file.read((char *)&lastTimeStamp, sizeof(lastTimeStamp));
timer.setInterval(10);
timer.start();
m_dataBuffer.clear();
m_myTime.restart();
m_timeOffset = 0;
m_lastPlayed = 0;
m_file.read((char *)&m_lastTimeStamp, sizeof(m_lastTimeStamp));
m_timer.setInterval(10);
m_timer.start();
emit replayStarted();
return true;
}
@ -170,11 +174,11 @@ bool LogFile::stopReplay()
void LogFile::pauseReplay()
{
timer.stop();
m_timer.stop();
}
void LogFile::resumeReplay()
{
timeOffset = myTime.elapsed();
timer.start();
m_timeOffset = m_myTime.elapsed();
m_timer.start();
}

View File

@ -7,22 +7,22 @@
#include <QMutexLocker>
#include <QDebug>
#include <QBuffer>
#include "uavobjectmanager.h"
#include <math.h>
#include <QFile>
#include "utils_global.h"
class LogFile : public QIODevice {
class QTCREATOR_UTILS_EXPORT LogFile : public QIODevice {
Q_OBJECT
public:
explicit LogFile(QObject *parent = 0);
qint64 bytesAvailable() const;
qint64 bytesToWrite()
{
return file.bytesToWrite();
return m_file.bytesToWrite();
};
bool open(OpenMode mode);
void setFileName(QString name)
{
file.setFileName(name);
m_file.setFileName(name);
};
void close();
qint64 writeData(const char *data, qint64 dataSize);
@ -30,12 +30,21 @@ public:
bool startReplay();
bool stopReplay();
void useProvidedTimeStamp(bool useProvidedTimeStamp)
{
m_useProvidedTimeStamp = useProvidedTimeStamp;
}
void setNextTimeStamp(quint32 nextTimestamp)
{
m_nextTimeStamp = nextTimestamp;
}
public slots:
void setReplaySpeed(double val)
{
playbackSpeed = val;
qDebug() << "Playback speed is now" << playbackSpeed;
m_playbackSpeed = val;
qDebug() << "Playback speed is now" << m_playbackSpeed;
};
void pauseReplay();
void resumeReplay();
@ -49,17 +58,21 @@ signals:
void replayFinished();
protected:
QByteArray dataBuffer;
QTimer timer;
QTime myTime;
QFile file;
qint32 lastTimeStamp;
qint32 lastPlayed;
QMutex mutex;
QByteArray m_dataBuffer;
QTimer m_timer;
QTime m_myTime;
QFile m_file;
qint32 m_lastTimeStamp;
qint32 m_lastPlayed;
QMutex m_mutex;
int timeOffset;
double playbackSpeed;
int m_timeOffset;
double m_playbackSpeed;
private:
quint32 m_nextTimeStamp;
bool m_useProvidedTimeStamp;
};
#endif // LOGFILE_H

View File

@ -54,7 +54,8 @@ SOURCES += reloadpromptutils.cpp \
mytabwidget.cpp \
cachedsvgitem.cpp \
svgimageprovider.cpp \
hostosinfo.cpp
hostosinfo.cpp \
logfile.cpp
SOURCES += xmlconfig.cpp
@ -111,7 +112,8 @@ HEADERS += utils_global.h \
mytabwidget.h \
cachedsvgitem.h \
svgimageprovider.h \
hostosinfo.h
hostosinfo.h \
logfile.h
HEADERS += xmlconfig.h

View File

@ -0,0 +1,10 @@
<plugin name="FlightLog" version="1.0.0" compatVersion="1.0.0">
<vendor>The OpenPilot Project</vendor>
<copyright>(C) 2013 OpenPilot Project</copyright>
<license>The GNU Public License (GPL) Version 3</license>
<description>A plugin to manage flight side logs, viewing and downloading.</description>
<url>http://www.openpilot.org</url>
<dependencyList>
<dependency name="Core" version="1.0.0"/>
</dependencyList>
</plugin>

View File

@ -0,0 +1,189 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import org.openpilot 1.0
import "functions.js" as Functions
Rectangle {
width: 600
height: 400
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
spacing: 10
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
border.width: 1
radius: 4
ColumnLayout {
anchors.margins: 10
anchors.fill: parent
Text {
Layout.fillWidth: true
text: "<b>" + qsTr("Log entries") + "</b>"
font.pixelSize: 12
}
TableView {
Layout.fillWidth: true
Layout.fillHeight: true
model: logManager.logEntries
itemDelegate: Text {
anchors.fill: parent
anchors.margins: 2
anchors.leftMargin: 5
font.pixelSize: 12
text: styleData.value
}
TableViewColumn {
role: "Flight"; title: qsTr("Flight"); width: 50;
delegate:
Text {
anchors.fill: parent
anchors.margins: 2
anchors.leftMargin: 5
font.pixelSize: 12
text: styleData.value + 1
}
}
TableViewColumn {
role: "FlightTime"; title: qsTr("Time"); width: 80;
delegate:
Text {
anchors.fill: parent
anchors.margins: 2
anchors.leftMargin: 5
font.pixelSize: 12
text: Functions.millisToTime(styleData.value)
}
}
TableViewColumn {
role: "Type"; title: "Type"; width: 50;
delegate:
Text {
anchors.fill: parent
anchors.margins: 2
anchors.leftMargin: 5
font.pixelSize: 12
text: {
switch(styleData.value) {
case 0 : text: qsTr("Empty"); break;
case 1 : text: qsTr("Text"); break;
case 2 : text: qsTr("UAVO"); break;
default: text: qsTr("Unknown"); break;
}
}
}
}
TableViewColumn { role: "LogString"; title: qsTr("Data"); width: 280}
}
RowLayout {
anchors.margins: 10
spacing: 10
ColumnLayout {
spacing: 10
Text {
id: totalFlights
font.pixelSize: 12
text: "<b>" + qsTr("Flights recorded: ") + "</b>" + (logStatus.Flight + 1)
}
Text {
id: totalEntries
font.pixelSize: 12
text: "<b>" + qsTr("Entries logged (free): ") + "</b>" +
logStatus.UsedSlots + " (" + logStatus.FreeSlots + ")"
}
}
Rectangle {
Layout.fillWidth: true
}
ColumnLayout {
spacing: 10
RowLayout {
Rectangle {
Layout.fillWidth: true
}
Text {
font.pixelSize: 12
text: "<b>" + qsTr("Flight to download:") + "</b>"
}
ComboBox {
id: flightCombo
enabled: !logManager.disableControls
model: logManager.flightEntries
}
}
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Rectangle {
Layout.fillWidth: true
}
Button {
text: qsTr("Download logs")
enabled: !logManager.disableControls
activeFocusOnPress: true
onClicked: logManager.retrieveLogs(flightCombo.currentIndex - 1)
}
}
}
}
}
}
RowLayout {
Layout.fillWidth: true
height: 40
Button {
id: exportButton
enabled: !logManager.disableControls && !logManager.disableExport
text: qsTr("Export...")
activeFocusOnPress: true
onClicked: logManager.exportLogs()
}
CheckBox {
id: exportRelativeTimeCB
enabled: !logManager.disableControls && !logManager.disableExport
text: qsTr("Adjust timestamps")
activeFocusOnPress: true
checked: logManager.adjustExportedTimestamps
onCheckedChanged: logManager.setAdjustExportedTimestamps(checked)
}
Button {
id: clearButton
enabled: !logManager.disableControls
text: qsTr("Clear all logs")
activeFocusOnPress: true
onClicked: logManager.clearAllLogs()
}
Rectangle {
Layout.fillWidth: true
}
Button {
id: cancelButton
enabled: logManager.disableControls
text: qsTr("Cancel")
activeFocusOnPress: true
onClicked: logManager.cancelExportLogs()
}
Button {
id: okButton
enabled: !logManager.disableControls
text: qsTr("OK")
isDefault: true
activeFocusOnPress: true
onClicked: dialog.close()
}
}
}
}

View File

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/flightlog">
<file>FlightLogDialog.qml</file>
<file>functions.js</file>
</qresource>
</RCC>

View File

@ -0,0 +1,25 @@
TEMPLATE = lib
TARGET = FlightLog
QT += qml quick
include(../../openpilotgcsplugin.pri)
include(../../plugins/coreplugin/coreplugin.pri)
include(../../plugins/uavobjects/uavobjects.pri)
include(../../plugins/uavtalk/uavtalk.pri)
HEADERS += flightlogplugin.h \
flightlogmanager.h \
flightlogdialog.h
SOURCES += flightlogplugin.cpp \
flightlogmanager.cpp \
flightlogdialog.cpp
OTHER_FILES += Flightlog.pluginspec \
FlightLogDialog.qml \
functions.js
FORMS +=
RESOURCES += \
flightLog.qrc

View File

@ -0,0 +1,66 @@
/**
******************************************************************************
*
* @file flightlogdialog.cpp
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup [Group]
* @{
* @addtogroup FlightLogDialog
* @{
* @brief [Brief]
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "flightlogdialog.h"
#include <QVBoxLayout>
#include <QtQuick>
#include <QQuickView>
#include <QQmlEngine>
#include <QQmlContext>
#include "flightlogmanager.h"
FlightLogDialog::FlightLogDialog(QWidget *parent, FlightLogManager *flightLogManager) :
QDialog(parent)
{
qmlRegisterType<ExtendedDebugLogEntry>("org.openpilot", 1, 0, "DebugLogEntry");
setWindowIcon(QIcon(":/core/images/openpilot_logo_32.png"));
setWindowTitle(tr("Manage flight side logs"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setMinimumSize(600, 400);
QQuickView *view = new QQuickView();
view->rootContext()->setContextProperty("dialog", this);
view->rootContext()->setContextProperty("logStatus", flightLogManager->flightLogStatus());
view->rootContext()->setContextProperty("logManager", flightLogManager);
view->setResizeMode(QQuickView::SizeRootObjectToView);
view->setSource(QUrl("qrc:/flightlog/FlightLogDialog.qml"));
QWidget *container = QWidget::createWindowContainer(view);
container->setMinimumSize(600, 400);
container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QVBoxLayout *lay = new QVBoxLayout();
lay->setContentsMargins(0, 0, 0, 0);
setLayout(lay);
layout()->addWidget(container);
}
FlightLogDialog::~FlightLogDialog()
{}

View File

@ -0,0 +1,15 @@
#ifndef FLIGHTLOGDIALOG_H
#define FLIGHTLOGDIALOG_H
#include <QDialog>
#include "flightlogmanager.h"
class FlightLogDialog : public QDialog {
Q_OBJECT
public:
explicit FlightLogDialog(QWidget *parent, FlightLogManager *flightLogManager);
~FlightLogDialog();
};
#endif // FLIGHTLOGDIALOG_H

View File

@ -0,0 +1,311 @@
/**
******************************************************************************
*
* @file flightlogmanager.cpp
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup [Group]
* @{
* @addtogroup FlightLogManager
* @{
* @brief [Brief]
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "flightlogmanager.h"
#include "extensionsystem/pluginmanager.h"
#include <QApplication>
#include <QFileDialog>
#include "debuglogcontrol.h"
#include "uavobjecthelper.h"
#include "uavtalk/uavtalk.h"
#include "utils/logfile.h"
FlightLogManager::FlightLogManager(QObject *parent) :
QObject(parent), m_disableControls(false),
m_disableExport(true), m_cancelDownload(false),
m_adjustExportedTimestamps(true)
{
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
m_objectManager = pm->getObject<UAVObjectManager>();
Q_ASSERT(m_objectManager);
m_flightLogControl = DebugLogControl::GetInstance(m_objectManager);
Q_ASSERT(m_flightLogControl);
m_flightLogStatus = DebugLogStatus::GetInstance(m_objectManager);
Q_ASSERT(m_flightLogStatus);
connect(m_flightLogStatus, SIGNAL(FlightChanged(quint16)), this, SLOT(updateFlightEntries(quint16)));
m_flightLogEntry = DebugLogEntry::GetInstance(m_objectManager);
Q_ASSERT(m_flightLogEntry);
updateFlightEntries(m_flightLogStatus->getFlight());
}
FlightLogManager::~FlightLogManager()
{
while (!m_logEntries.isEmpty()) {
delete m_logEntries.takeFirst();
}
}
void addLogEntries(QQmlListProperty<ExtendedDebugLogEntry> *list, ExtendedDebugLogEntry *entry)
{
Q_UNUSED(list);
Q_UNUSED(entry);
}
int countLogEntries(QQmlListProperty<ExtendedDebugLogEntry> *list)
{
return static_cast< QList<ExtendedDebugLogEntry *> *>(list->data)->size();
}
ExtendedDebugLogEntry *logEntryAt(QQmlListProperty<ExtendedDebugLogEntry> *list, int index)
{
return static_cast< QList<ExtendedDebugLogEntry *> *>(list->data)->at(index);
}
void clearLogEntries(QQmlListProperty<ExtendedDebugLogEntry> *list)
{
return static_cast< QList<ExtendedDebugLogEntry *> *>(list->data)->clear();
}
QQmlListProperty<ExtendedDebugLogEntry> FlightLogManager::logEntries()
{
return QQmlListProperty<ExtendedDebugLogEntry>(this, &m_logEntries, &addLogEntries, &countLogEntries, &logEntryAt, &clearLogEntries);
}
QStringList FlightLogManager::flightEntries()
{
return m_flightEntries;
}
void FlightLogManager::clearAllLogs()
{
setDisableControls(true);
QApplication::setOverrideCursor(Qt::WaitCursor);
// Clear on flight side
UAVObjectUpdaterHelper updateHelper;
m_flightLogControl->setFlight(0);
m_flightLogControl->setEntry(0);
m_flightLogControl->setOperation(DebugLogControl::OPERATION_FORMATFLASH);
if (updateHelper.doObjectAndWait(m_flightLogControl, UAVTALK_TIMEOUT) == UAVObjectUpdaterHelper::SUCCESS) {
// Then empty locally
clearLogList();
}
QApplication::restoreOverrideCursor();
setDisableControls(false);
}
void FlightLogManager::clearLogList()
{
QList<ExtendedDebugLogEntry*> tmpList(m_logEntries);
m_logEntries.clear();
emit logEntriesChanged();
setDisableExport(true);
while (!tmpList.isEmpty()) {
delete tmpList.takeFirst();
}
}
void FlightLogManager::retrieveLogs(int flightToRetrieve)
{
setDisableControls(true);
QApplication::setOverrideCursor(Qt::WaitCursor);
m_cancelDownload = false;
UAVObjectUpdaterHelper updateHelper;
UAVObjectRequestHelper requestHelper;
clearLogList();
// Set up what to retrieve
int startFlight = (flightToRetrieve == -1) ? 0 : flightToRetrieve;
int endFlight = (flightToRetrieve == -1) ? m_flightLogStatus->getFlight() : flightToRetrieve;
// Prepare to send request for event retrieval
m_flightLogControl->setOperation(DebugLogControl::OPERATION_RETRIEVE);
for (int flight = startFlight; flight <= endFlight; flight++) {
m_flightLogControl->setFlight(flight);
bool gotLast = false;
int entry = 0;
while (!gotLast) {
// Send request for loading flight entry on flight side and wait for ack/nack
m_flightLogControl->setEntry(entry);
if (updateHelper.doObjectAndWait(m_flightLogControl, UAVTALK_TIMEOUT) == UAVObjectUpdaterHelper::SUCCESS &&
requestHelper.doObjectAndWait(m_flightLogEntry, UAVTALK_TIMEOUT) == UAVObjectUpdaterHelper::SUCCESS) {
if (m_flightLogEntry->getType() != DebugLogEntry::TYPE_EMPTY) {
// Ok, we retrieved the entry, and it was the correct one. clone it and add it to the list
ExtendedDebugLogEntry *logEntry = new ExtendedDebugLogEntry();
logEntry->setData(m_flightLogEntry->getData(), m_objectManager);
m_logEntries << logEntry;
// Increment to get next entry from flight side
entry++;
} else {
// We are done, not more entries on this flight
gotLast = true;
}
} else {
// We failed for some reason
break;
}
if (m_cancelDownload) {
break;
}
}
if (m_cancelDownload) {
break;
}
}
if (m_cancelDownload) {
clearLogList();
m_cancelDownload = false;
}
emit logEntriesChanged();
setDisableExport(m_logEntries.count() == 0);
QApplication::restoreOverrideCursor();
setDisableControls(false);
}
void FlightLogManager::exportLogs()
{
if(m_logEntries.isEmpty()) {
return;
}
setDisableControls(true);
QApplication::setOverrideCursor(Qt::WaitCursor);
QString fileName = QFileDialog::getSaveFileName(NULL, tr("Save Log"),
tr("OP-%0.opl").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss")),
tr("OpenPilot Log (*.opl)"));
if (!fileName.isEmpty()) {
// Loop and create a new file for each flight.
fileName = fileName.replace(QString(".opl"), QString("%1.opl"));
int currentEntry = 0;
int currentFlight = 0;
quint32 adjustedBaseTime = 0;
// Continue until all entries are exported
while(currentEntry < m_logEntries.count()) {
if (m_adjustExportedTimestamps) {
adjustedBaseTime = m_logEntries[currentEntry]->getFlightTime();
}
// Get current flight
currentFlight = m_logEntries[currentEntry]->getFlight();
LogFile logFile;
logFile.useProvidedTimeStamp(true);
// Set the file name to contain flight number
logFile.setFileName(fileName.arg(tr("_flight-%1").arg(currentFlight + 1)));
logFile.open(QIODevice::WriteOnly);
UAVTalk uavTalk(&logFile, m_objectManager);
// Export entries until no more available or flight changes
while(currentEntry < m_logEntries.count() && m_logEntries[currentEntry]->getFlight() == currentFlight) {
ExtendedDebugLogEntry* entry = m_logEntries[currentEntry];
// Only log uavobjects
if (entry->getType() == ExtendedDebugLogEntry::TYPE_UAVOBJECT) {
// Set timestamp that should be logged for this entry
logFile.setNextTimeStamp(entry->getFlightTime() - adjustedBaseTime);
// Use UAVTalk to log complete message to file
uavTalk.sendObject(entry->uavObject(), false, false);
qDebug() << entry->getFlightTime() - adjustedBaseTime << "=" << entry->toStringBrief();
}
currentEntry++;
}
logFile.close();
}
}
QApplication::restoreOverrideCursor();
setDisableControls(false);
}
void FlightLogManager::cancelExportLogs()
{
m_cancelDownload = true;
}
void FlightLogManager::updateFlightEntries(quint16 currentFlight)
{
Q_UNUSED(currentFlight);
int flights = m_flightLogStatus->getFlight();
if (m_flightEntries.count() == 0 || (m_flightEntries.count() - 1 != flights)) {
m_flightEntries.clear();
m_flightEntries << tr("All");
for(int i = 0; i <= flights; i++) {
m_flightEntries << QString::number(i + 1);
}
emit flightEntriesChanged();
}
}
ExtendedDebugLogEntry::ExtendedDebugLogEntry() : DebugLogEntry(),
m_object(0)
{}
ExtendedDebugLogEntry::~ExtendedDebugLogEntry()
{
if (m_object) {
delete m_object;
m_object = 0;
}
}
QString ExtendedDebugLogEntry::getLogString()
{
if (getType() == DebugLogEntry::TYPE_TEXT) {
return QString((const char *)getData().Data);
} else if (getType() == DebugLogEntry::TYPE_UAVOBJECT) {
return m_object->toString().replace("\n", " ").replace("\t", " ");
} else {
return "";
}
}
void ExtendedDebugLogEntry::setData(const DebugLogEntry::DataFields &data, UAVObjectManager *objectManager)
{
DebugLogEntry::setData(data);
if (getType() == DebugLogEntry::TYPE_UAVOBJECT) {
UAVDataObject *object = (UAVDataObject *)objectManager->getObject(getObjectID(), getInstanceID());
Q_ASSERT(object);
m_object = object->clone(getInstanceID());
m_object->unpack(getData().Data);
}
}

View File

@ -0,0 +1,162 @@
/**
******************************************************************************
*
* @file flightlogmanager.h
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup [Group]
* @{
* @addtogroup FlightLogManager
* @{
* @brief [Brief]
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef FLIGHTLOGMANAGER_H
#define FLIGHTLOGMANAGER_H
#include <QObject>
#include <QList>
#include <QQmlListProperty>
#include <QSemaphore>
#include "uavobjectmanager.h"
#include "debuglogentry.h"
#include "debuglogstatus.h"
#include "debuglogcontrol.h"
class ExtendedDebugLogEntry : public DebugLogEntry {
Q_OBJECT Q_PROPERTY(QString LogString READ getLogString WRITE setLogString NOTIFY LogStringUpdated)
public:
explicit ExtendedDebugLogEntry();
~ExtendedDebugLogEntry();
QString getLogString();
UAVDataObject *uavObject()
{
return m_object;
}
void setData(const DataFields& data, UAVObjectManager *objectManager);
public slots:
void setLogString(QString arg)
{
Q_UNUSED(arg);
}
signals:
void LogStringUpdated(QString arg);
private:
UAVDataObject *m_object;
};
class FlightLogManager : public QObject {
Q_OBJECT Q_PROPERTY(DebugLogStatus *flightLogStatus READ flightLogStatus)
Q_PROPERTY(QQmlListProperty<ExtendedDebugLogEntry> logEntries READ logEntries NOTIFY logEntriesChanged)
Q_PROPERTY(QStringList flightEntries READ flightEntries NOTIFY flightEntriesChanged)
Q_PROPERTY(bool disableControls READ disableControls WRITE setDisableControls NOTIFY disableControlsChanged)
Q_PROPERTY(bool disableExport READ disableExport WRITE setDisableExport NOTIFY disableExportChanged)
Q_PROPERTY(bool adjustExportedTimestamps READ adjustExportedTimestamps WRITE setAdjustExportedTimestamps NOTIFY adjustExportedTimestampsChanged)
public:
explicit FlightLogManager(QObject *parent = 0);
~FlightLogManager();
QQmlListProperty<ExtendedDebugLogEntry> logEntries();
QStringList flightEntries();
DebugLogStatus *flightLogStatus() const
{
return m_flightLogStatus;
}
bool disableControls() const
{
return m_disableControls;
}
bool disableExport() const
{
return m_disableExport;
}
void clearLogList();
bool adjustExportedTimestamps() const
{
return m_adjustExportedTimestamps;
}
signals:
void logEntriesChanged();
void flightEntriesChanged();
void disableControlsChanged(bool arg);
void disableExportChanged(bool arg);
void adjustExportedTimestampsChanged(bool arg);
public slots:
void clearAllLogs();
void retrieveLogs(int flightToRetrieve = -1);
void exportLogs();
void cancelExportLogs();
void setDisableControls(bool arg)
{
if (m_disableControls != arg) {
m_disableControls = arg;
emit disableControlsChanged(arg);
}
}
void setDisableExport(bool arg)
{
if (m_disableExport != arg) {
m_disableExport = arg;
emit disableExportChanged(arg);
}
}
void setAdjustExportedTimestamps(bool arg)
{
if (m_adjustExportedTimestamps != arg) {
m_adjustExportedTimestamps = arg;
emit adjustExportedTimestampsChanged(arg);
}
}
private slots:
void updateFlightEntries(quint16 currentFlight);
private:
UAVObjectManager *m_objectManager;
DebugLogControl *m_flightLogControl;
DebugLogStatus *m_flightLogStatus;
DebugLogEntry *m_flightLogEntry;
QList<ExtendedDebugLogEntry *> m_logEntries;
QStringList m_flightEntries;
static const int UAVTALK_TIMEOUT = 4000;
bool m_disableControls;
bool m_disableExport;
bool m_cancelDownload;
bool m_adjustExportedTimestamps;
};
#endif // FLIGHTLOGMANAGER_H

View File

@ -0,0 +1,98 @@
/**
******************************************************************************
*
* @file flightlogplugin.cpp
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @addtogroup GCSPlugins GCS Plugins
* @brief A plugin to view and download flight side logs.
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "flightlogplugin.h"
#include <QDebug>
#include <QtPlugin>
#include <QStringList>
#include <extensionsystem/pluginmanager.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
#include <QKeySequence>
#include <coreplugin/modemanager.h>
#include "flightlogdialog.h"
FlightLogPlugin::FlightLogPlugin() : m_logDialog(0)
{}
FlightLogPlugin::~FlightLogPlugin()
{
shutdown();
}
bool FlightLogPlugin::initialize(const QStringList & args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
// Add Menu entry
Core::ActionManager *am = Core::ICore::instance()->actionManager();
Core::ActionContainer *ac = am->actionContainer(Core::Constants::M_TOOLS);
Core::Command *cmd = am->registerAction(new QAction(this),
"FlightLogPlugin.ShowFlightLogDialog",
QList<int>() <<
Core::Constants::C_GLOBAL_ID);
cmd->setDefaultKeySequence(QKeySequence("Ctrl+F"));
cmd->action()->setText(tr("Manage flight side logs..."));
Core::ModeManager::instance()->addAction(cmd, 1);
ac->menu()->addSeparator();
ac->appendGroup("FlightLogs");
ac->addAction(cmd, "FlightLogs");
connect(cmd->action(), SIGNAL(triggered(bool)), this, SLOT(ShowLogManagementDialog()));
return true;
}
void FlightLogPlugin::ShowLogManagementDialog()
{
if (!m_logDialog) {
m_logDialog = new FlightLogDialog(0, new FlightLogManager());
connect(m_logDialog, SIGNAL(finished(int)), this, SLOT(LogManagementDialogClosed()));
m_logDialog->show();
}
}
void FlightLogPlugin::LogManagementDialogClosed()
{
if (m_logDialog) {
m_logDialog->deleteLater();
m_logDialog = 0;
}
}
void FlightLogPlugin::extensionsInitialized()
{}
void FlightLogPlugin::shutdown()
{
if (m_logDialog) {
m_logDialog->close();
LogManagementDialogClosed();
}
}

View File

@ -0,0 +1,53 @@
/**
******************************************************************************
*
* @file flightlogplugin.h
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @addtogroup GCSPlugins GCS Plugins
* @{
* @brief A plugin to view and download flight side logs.
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef FLIGHTLOGPLUGIN_H_
#define FLIGHTLOGPLUGIN_H_
#include <extensionsystem/iplugin.h>
#include "flightlogmanager.h"
#include "flightlogdialog.h"
class FlightLogPlugin : public ExtensionSystem::IPlugin {
Q_OBJECT
Q_PLUGIN_METADATA(IID "OpenPilot.FlightLog")
public:
FlightLogPlugin();
~FlightLogPlugin();
void extensionsInitialized();
bool initialize(const QStringList & arguments, QString *errorString);
void shutdown();
private slots:
void ShowLogManagementDialog();
void LogManagementDialogClosed();
private:
FlightLogDialog *m_logDialog;
};
#endif /* FLIGHTLOGPLUGIN_H_ */

View File

@ -0,0 +1,20 @@
.pragma library
function millisToTime(ms) {
var secs = Math.floor(ms / 1000);
var msleft = ms % 1000;
var hours = Math.floor(secs / (60 * 60));
var divisor_for_minutes = secs % (60 * 60);
var minutes = Math.floor(divisor_for_minutes / 60);
var divisor_for_seconds = divisor_for_minutes % 60;
var seconds = Math.ceil(divisor_for_seconds);
return pad(hours, 2) + ":" + pad(minutes, 2) + ":" + pad(seconds, 2) + ":" + pad(msleft, 3);
}
function pad(number, length) {
var str = '' + number;
while (str.length < length) {
str = '0' + str;
}
return str;
}

View File

@ -1,25 +1,22 @@
TEMPLATE = lib
TARGET = LoggingGadget
DEFINES += LOGGING_LIBRARY
QT += svg
include(../../openpilotgcsplugin.pri)
include(logging_dependencies.pri)
HEADERS += loggingplugin.h \
logfile.h \
logginggadgetwidget.h \
logginggadget.h \
logginggadgetfactory.h
# logginggadgetconfiguration.h
# logginggadgetoptionspage.h
SOURCES += loggingplugin.cpp \
logfile.cpp \
logginggadgetwidget.cpp \
logginggadget.cpp \
logginggadgetfactory.cpp
# logginggadgetconfiguration.cpp \
# logginggadgetoptionspage.cpp
OTHER_FILES += LoggingGadget.pluginspec
FORMS += logging.ui
# logginggadgetwidget.ui \
# loggingdialog.ui

View File

@ -323,7 +323,7 @@ bool LoggingPlugin::initialize(const QStringList & args, QString *errMsg)
QList<int>() <<
Core::Constants::C_GLOBAL_ID);
cmd->setDefaultKeySequence(QKeySequence("Ctrl+L"));
cmd->action()->setText("Start logging...");
cmd->action()->setText(tr("Start logging..."));
ac->menu()->addSeparator();
ac->appendGroup("Logging");

View File

@ -35,7 +35,7 @@
#include "uavobjectmanager.h"
#include "gcstelemetrystats.h"
#include <uavtalk/uavtalk.h>
#include <logfile.h>
#include <utils/logfile.h>
#include <QThread>
#include <QQueue>

View File

@ -244,3 +244,10 @@ plugin_setupwizard.depends += plugin_config
plugin_setupwizard.depends += plugin_uploader
SUBDIRS += plugin_setupwizard
# Flight Logs plugin
plugin_flightlog.subdir = flightlog
plugin_flightlog.depends = plugin_coreplugin
plugin_flightlog.depends += plugin_uavobjects
plugin_flightlog.depends += plugin_uavtalk
SUBDIRS += plugin_flightlog

View File

@ -0,0 +1,100 @@
/**
******************************************************************************
*
* @file uavobjecthelper.cpp
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup [Group]
* @{
* @addtogroup UAVObjectHelper
* @{
* @brief [Brief]
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "uavobjecthelper.h"
#include <QTimer>
AbstractUAVObjectHelper::AbstractUAVObjectHelper(QObject *parent) :
QObject(parent), m_transactionResult(false), m_transactionCompleted(false)
{}
AbstractUAVObjectHelper::Result AbstractUAVObjectHelper::doObjectAndWait(UAVObject *object, int timeout)
{
// Lock, we can't call this twice from different threads
QMutexLocker locker(&m_mutex);
m_object = object;
// Reset variables
m_transactionResult = false;
m_transactionCompleted = false;
// Create timer and connect it, connect object tx completed to local slot
QTimer timeoutTimer;
timeoutTimer.setSingleShot(true);
connect(&timeoutTimer, SIGNAL(timeout()), &m_eventLoop, SLOT(quit()));
connect(object, SIGNAL(transactionCompleted(UAVObject *, bool)), this, SLOT(transactionCompleted(UAVObject *, bool)));
// Start timeout timer
timeoutTimer.start(timeout);
// Call the actual implementation in concrete subclass
doObjectAndWaitImpl();
// Wait if not completed
if (!m_transactionCompleted) {
m_eventLoop.exec();
}
timeoutTimer.stop();
// Disconnect
disconnect(object, SIGNAL(transactionCompleted(UAVObject *, bool)), this, SLOT(transactionCompleted(UAVObject *, bool)));
disconnect(&timeoutTimer, SIGNAL(timeout()), &m_eventLoop, SLOT(quit()));
// Return result
if (!m_transactionCompleted) {
return TIMEOUT;
} else {
return m_transactionResult ? SUCCESS : FAIL;
}
}
void AbstractUAVObjectHelper::transactionCompleted(UAVObject *object, bool success)
{
Q_UNUSED(object)
// Set variables and quit event loop
m_transactionResult = success;
m_transactionCompleted = true;
m_eventLoop.quit();
}
UAVObjectUpdaterHelper::UAVObjectUpdaterHelper(QObject *parent) : AbstractUAVObjectHelper(parent)
{}
void UAVObjectUpdaterHelper::doObjectAndWaitImpl()
{
m_object->updated();
}
UAVObjectRequestHelper::UAVObjectRequestHelper(QObject *parent) : AbstractUAVObjectHelper(parent)
{}
void UAVObjectRequestHelper::doObjectAndWaitImpl()
{
m_object->requestUpdate();
}

View File

@ -0,0 +1,78 @@
/**
******************************************************************************
*
* @file uavobjecthelper.h
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @addtogroup [Group]
* @{
* @addtogroup UAVObjectHelper
* @{
* @brief [Brief]
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef UAVOBJECTHELPER_H
#define UAVOBJECTHELPER_H
#include <QObject>
#include <QEventLoop>
#include <QMutex>
#include <QMutexLocker>
#include "uavobject.h"
class UAVOBJECTS_EXPORT AbstractUAVObjectHelper : public QObject {
Q_OBJECT
public:
explicit AbstractUAVObjectHelper(QObject *parent = 0);
enum Result { SUCCESS, FAIL, TIMEOUT };
Result doObjectAndWait(UAVObject *object, int timeout);
protected:
virtual void doObjectAndWaitImpl() = 0;
UAVObject *m_object;
private slots:
void transactionCompleted(UAVObject *object, bool success);
private:
QMutex m_mutex;
QEventLoop m_eventLoop;
bool m_transactionResult;
bool m_transactionCompleted;
};
class UAVOBJECTS_EXPORT UAVObjectUpdaterHelper : public AbstractUAVObjectHelper {
Q_OBJECT
public:
explicit UAVObjectUpdaterHelper(QObject *parent = 0);
protected:
virtual void doObjectAndWaitImpl();
};
class UAVOBJECTS_EXPORT UAVObjectRequestHelper : public AbstractUAVObjectHelper {
Q_OBJECT
public:
explicit UAVObjectRequestHelper(QObject *parent = 0);
protected:
virtual void doObjectAndWaitImpl();
};
#endif // UAVOBJECTHELPER_H

View File

@ -11,14 +11,16 @@ HEADERS += uavobjects_global.h \
uavdataobject.h \
uavobjectfield.h \
uavobjectsinit.h \
uavobjectsplugin.h
uavobjectsplugin.h \
uavobjecthelper.h
SOURCES += uavobject.cpp \
uavmetaobject.cpp \
uavobjectmanager.cpp \
uavdataobject.cpp \
uavobjectfield.cpp \
uavobjectsplugin.cpp
uavobjectsplugin.cpp \
uavobjecthelper.cpp
OTHER_FILES += UAVObjects.pluginspec