mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2025-01-28 13:52:10 +01:00
476d548da9
git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@2192 ebee16cc-31ac-478f-84a7-5cbb03baadba
444 lines
12 KiB
C++
444 lines
12 KiB
C++
/**
|
|
******************************************************************************
|
|
*
|
|
* @file logging.cpp
|
|
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
|
|
* @see The GNU Public License (GPL) Version 3
|
|
* @brief Import/Export Plugin
|
|
* @addtogroup GCSPlugins GCS Plugins
|
|
* @{
|
|
* @addtogroup Logging
|
|
* @{
|
|
*
|
|
*****************************************************************************/
|
|
/*
|
|
* 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 "loggingplugin.h"
|
|
#include "logginggadgetfactory.h"
|
|
#include <QDebug>
|
|
#include <QtPlugin>
|
|
#include <QThread>
|
|
#include <QStringList>
|
|
#include <QDir>
|
|
#include <QFileDialog>
|
|
#include <QList>
|
|
#include <QErrorMessage>
|
|
#include <QWriteLocker>
|
|
|
|
#include <extensionsystem/pluginmanager.h>
|
|
#include <coreplugin/coreconstants.h>
|
|
#include <coreplugin/actionmanager/actionmanager.h>
|
|
#include <coreplugin/icore.h>
|
|
#include <QKeySequence>
|
|
#include "uavobjects/uavobjectmanager.h"
|
|
#include <uavobjects/uavobjectmanager.h>
|
|
|
|
/**
|
|
* Sets the file to use for logging and takes the parent plugin
|
|
* to connect to stop logging signal
|
|
* @param[in] file File name to write to
|
|
* @param[in] parent plugin
|
|
*/
|
|
bool LoggingThread::openFile(QString file, LoggingPlugin * parent)
|
|
{
|
|
logFile.setFileName(file);
|
|
logFile.open(QIODevice::WriteOnly);
|
|
|
|
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
|
|
UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
|
|
|
|
uavTalk = new UAVTalk(&logFile, objManager);
|
|
connect(parent,SIGNAL(stopLoggingSignal()),this,SLOT(stopLogging()));
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Logs an object update to the file. Data format is the
|
|
* timestamp as a 32 bit uint counting ms from start of
|
|
* file writing (flight time will be embedded in stream),
|
|
* then object packet size, then the packed UAVObject.
|
|
*/
|
|
void LoggingThread::objectUpdated(UAVObject * obj)
|
|
{
|
|
QWriteLocker locker(&lock);
|
|
if(!uavTalk->sendObject(obj,false,false) )
|
|
qDebug() << "Error logging " << obj->getName();
|
|
};
|
|
|
|
/**
|
|
* Connect signals from all the objects updates to the write routine then
|
|
* run event loop
|
|
*/
|
|
void LoggingThread::run()
|
|
{
|
|
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
|
|
UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
|
|
|
|
QList< QList<UAVObject*> > list;
|
|
list = objManager->getObjects();
|
|
QList< QList<UAVObject*> >::const_iterator i;
|
|
QList<UAVObject*>::const_iterator j;
|
|
int objects = 0;
|
|
|
|
for (i = list.constBegin(); i != list.constEnd(); ++i)
|
|
{
|
|
for (j = (*i).constBegin(); j != (*i).constEnd(); ++j)
|
|
{
|
|
connect(*j, SIGNAL(objectUpdated(UAVObject*)), (LoggingThread*) this, SLOT(objectUpdated(UAVObject*)));
|
|
objects++;
|
|
//qDebug() << "Detected " << j[0];
|
|
}
|
|
}
|
|
|
|
GCSTelemetryStats* gcsStatsObj = GCSTelemetryStats::GetInstance(objManager);
|
|
GCSTelemetryStats::DataFields gcsStats = gcsStatsObj->getData();
|
|
if ( gcsStats.Status == GCSTelemetryStats::STATUS_CONNECTED )
|
|
{
|
|
qDebug() << "Logging: connected already, ask for all settings";
|
|
retrieveSettings();
|
|
} else {
|
|
qDebug() << "Logging: not connected, do no ask for settings";
|
|
}
|
|
|
|
|
|
exec();
|
|
}
|
|
|
|
|
|
/**
|
|
* Pass this command to the correct thread then close the file
|
|
*/
|
|
void LoggingThread::stopLogging()
|
|
{
|
|
QWriteLocker locker(&lock);
|
|
|
|
// Disconnect all objects we registered with:
|
|
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
|
|
UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
|
|
|
|
QList< QList<UAVObject*> > list;
|
|
list = objManager->getObjects();
|
|
QList< QList<UAVObject*> >::const_iterator i;
|
|
QList<UAVObject*>::const_iterator j;
|
|
|
|
for (i = list.constBegin(); i != list.constEnd(); ++i)
|
|
{
|
|
for (j = (*i).constBegin(); j != (*i).constEnd(); ++j)
|
|
{
|
|
disconnect(*j, SIGNAL(objectUpdated(UAVObject*)), (LoggingThread*) this, SLOT(objectUpdated(UAVObject*)));
|
|
}
|
|
}
|
|
|
|
logFile.close();
|
|
qDebug() << "File closed";
|
|
quit();
|
|
}
|
|
|
|
/**
|
|
* Initialize queue with settings objects to be retrieved.
|
|
*/
|
|
void LoggingThread::retrieveSettings()
|
|
{
|
|
// Clear object queue
|
|
queue.clear();
|
|
// Get all objects, add metaobjects, settings and data objects with OnChange update mode to the queue
|
|
// Get UAVObjectManager instance
|
|
ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance();
|
|
UAVObjectManager *objMngr = pm->getObject<UAVObjectManager>();
|
|
QList< QList<UAVDataObject*> > objs = objMngr->getDataObjects();
|
|
for (int n = 0; n < objs.length(); ++n)
|
|
{
|
|
UAVDataObject* obj = objs[n][0];
|
|
if ( obj->isSettings() )
|
|
{
|
|
queue.enqueue(obj);
|
|
}
|
|
}
|
|
// Start retrieving
|
|
qDebug() << tr("Logging: retrieve settings objects from the autopilot (%1 objects)")
|
|
.arg( queue.length());
|
|
retrieveNextObject();
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieve the next object in the queue
|
|
*/
|
|
void LoggingThread::retrieveNextObject()
|
|
{
|
|
// If queue is empty return
|
|
if ( queue.isEmpty() )
|
|
{
|
|
qDebug() << "Logging: Object retrieval completed";
|
|
return;
|
|
}
|
|
// Get next object from the queue
|
|
UAVObject* obj = queue.dequeue();
|
|
// Connect to object
|
|
connect(obj, SIGNAL(transactionCompleted(UAVObject*,bool)), this, SLOT(transactionCompleted(UAVObject*,bool)));
|
|
// Request update
|
|
obj->requestUpdate();
|
|
}
|
|
|
|
/**
|
|
* Called by the retrieved object when a transaction is completed.
|
|
*/
|
|
void LoggingThread::transactionCompleted(UAVObject* obj, bool success)
|
|
{
|
|
Q_UNUSED(success);
|
|
// Disconnect from sending object
|
|
obj->disconnect(this);
|
|
// Process next object if telemetry is still available
|
|
// Get stats objects
|
|
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
|
|
UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
|
|
GCSTelemetryStats* gcsStatsObj = GCSTelemetryStats::GetInstance(objManager);
|
|
GCSTelemetryStats::DataFields gcsStats = gcsStatsObj->getData();
|
|
if ( gcsStats.Status == GCSTelemetryStats::STATUS_CONNECTED )
|
|
{
|
|
retrieveNextObject();
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "Logging: Object retrieval has been cancelled";
|
|
queue.clear();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************
|
|
Logging plugin
|
|
********************************/
|
|
|
|
|
|
LoggingPlugin::LoggingPlugin() : state(IDLE)
|
|
{
|
|
// Do nothing
|
|
}
|
|
|
|
LoggingPlugin::~LoggingPlugin()
|
|
{
|
|
if (loggingThread)
|
|
delete loggingThread;
|
|
}
|
|
|
|
/**
|
|
* Add Logging plugin to File menu
|
|
*/
|
|
bool LoggingPlugin::initialize(const QStringList& args, QString *errMsg)
|
|
{
|
|
Q_UNUSED(args);
|
|
Q_UNUSED(errMsg);
|
|
|
|
loggingThread = NULL;
|
|
|
|
|
|
// Add Menu entry
|
|
Core::ActionManager* am = Core::ICore::instance()->actionManager();
|
|
Core::ActionContainer* ac = am->actionContainer(Core::Constants::M_TOOLS);
|
|
|
|
// Command to start logging
|
|
Core::Command* cmd = am->registerAction(new QAction(this),
|
|
"LoggingPlugin.Logging",
|
|
QList<int>() <<
|
|
Core::Constants::C_GLOBAL_ID);
|
|
cmd->setDefaultKeySequence(QKeySequence("Ctrl+L"));
|
|
cmd->action()->setText("Logging...");
|
|
|
|
ac->menu()->addSeparator();
|
|
ac->appendGroup("Logging");
|
|
ac->addAction(cmd, "Logging");
|
|
|
|
connect(cmd->action(), SIGNAL(triggered(bool)), this, SLOT(toggleLogging()));
|
|
|
|
// Command to replay logging
|
|
Core::Command* cmd2 = am->registerAction(new QAction(this),
|
|
"LoggingPlugin.Playback",
|
|
QList<int>() <<
|
|
Core::Constants::C_GLOBAL_ID);
|
|
cmd2->setDefaultKeySequence(QKeySequence("Ctrl+R"));
|
|
cmd2->action()->setText("Replay...");
|
|
|
|
ac->appendGroup("Replay");
|
|
ac->addAction(cmd2, "Replay");
|
|
|
|
connect(cmd2->action(), SIGNAL(triggered(bool)), this, SLOT(toggleReplay()));
|
|
|
|
mf = new LoggingGadgetFactory(this);
|
|
addAutoReleasedObject(mf);
|
|
|
|
// Map signal from end of replay to replay stopped
|
|
connect(&logFile,SIGNAL(replayFinished()), this, SLOT(replayStopped()));
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* The action that is triggered by the menu item which opens the
|
|
* file and begins logging if successful
|
|
*/
|
|
void LoggingPlugin::toggleLogging()
|
|
{
|
|
if(state == IDLE)
|
|
{
|
|
QFileDialog * fd = new QFileDialog();
|
|
fd->setAcceptMode(QFileDialog::AcceptSave);
|
|
fd->setNameFilter("OpenPilot Log (*.opl)");
|
|
connect(fd, SIGNAL(fileSelected(QString)), this, SLOT(startLogging(QString)));
|
|
fd->exec();
|
|
}
|
|
else if(state == LOGGING)
|
|
{
|
|
stopLogging();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The action that is triggered by the menu item which opens the
|
|
* file and begins replay if successful
|
|
*/
|
|
void LoggingPlugin::toggleReplay()
|
|
{
|
|
if(state == IDLE)
|
|
{
|
|
QFileDialog * fd = new QFileDialog();
|
|
fd->setAcceptMode(QFileDialog::AcceptOpen);
|
|
fd->setNameFilter("OpenPilot Log (*.opl)");
|
|
connect(fd, SIGNAL(fileSelected(QString)), this, SLOT(startReplay(QString)));
|
|
fd->exec();
|
|
}
|
|
else if(state == REPLAY)
|
|
{
|
|
logFile.stopReplay();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Starts the logging thread to a certain file
|
|
*/
|
|
void LoggingPlugin::startLogging(QString file)
|
|
{
|
|
qDebug() << "Logging to " << file;
|
|
// We have to delete the previous logging thread if is was still there!
|
|
if (loggingThread)
|
|
delete loggingThread;
|
|
loggingThread = new LoggingThread();
|
|
if(loggingThread->openFile(file,this))
|
|
{
|
|
connect(loggingThread,SIGNAL(finished()),this,SLOT(loggingStopped()));
|
|
state = LOGGING;
|
|
loggingThread->start();
|
|
emit stateChanged("LOGGING");
|
|
} else {
|
|
QErrorMessage err;
|
|
err.showMessage("Unable to open file for logging");
|
|
err.exec();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Starts the logging thread replaying a certain file
|
|
*/
|
|
void LoggingPlugin::startReplay(QString file)
|
|
{
|
|
|
|
logFile.setFileName(file);
|
|
if(logFile.open(QIODevice::ReadOnly)) {
|
|
qDebug() << "Replaying " << file;
|
|
state = REPLAY;
|
|
|
|
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
|
|
UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
|
|
|
|
uavTalk = new UAVTalk(&logFile, objManager);
|
|
logFile.startReplay();
|
|
|
|
emit stateChanged("REPLAY");
|
|
} else {
|
|
QErrorMessage err;
|
|
err.showMessage("Unable to open file for replay");
|
|
err.exec();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send the stop logging signal to the LoggingThread
|
|
*/
|
|
void LoggingPlugin::stopLogging()
|
|
{
|
|
emit stopLoggingSignal();
|
|
disconnect( this,SIGNAL(stopLoggingSignal()),0,0);
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Receive the logging stopped signal from the LoggingThread
|
|
* and change status to not logging
|
|
*/
|
|
void LoggingPlugin::loggingStopped()
|
|
{
|
|
if(state == LOGGING)
|
|
state = IDLE;
|
|
|
|
emit stateChanged("IDLE");
|
|
|
|
free(loggingThread);
|
|
loggingThread = NULL;
|
|
}
|
|
|
|
/**
|
|
* Received the replay stoppedsignal from the LogFile
|
|
*/
|
|
void LoggingPlugin::replayStopped()
|
|
{
|
|
Q_ASSERT(state == REPLAY);
|
|
|
|
if(uavTalk)
|
|
delete(uavTalk);
|
|
|
|
logFile.close();
|
|
|
|
uavTalk = 0;
|
|
state = IDLE;
|
|
|
|
emit stateChanged("IDLE");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LoggingPlugin::extensionsInitialized()
|
|
{
|
|
// Do nothing
|
|
}
|
|
|
|
void LoggingPlugin::shutdown()
|
|
{
|
|
// Do nothing
|
|
}
|
|
Q_EXPORT_PLUGIN(LoggingPlugin)
|
|
|
|
/**
|
|
* @}
|
|
* @}
|
|
*/
|