dRonin  adbada4
dRonin GCS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
loggingplugin.cpp
Go to the documentation of this file.
1 
15 /*
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 3 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful, but
22  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24  * for more details.
25  *
26  * You should have received a copy of the GNU General Public License along
27  * with this program; if not, see <http://www.gnu.org/licenses/>
28  */
29 
30 #include "loggingplugin.h"
31 #include "loggingdevice.h"
32 #include "logginggadgetfactory.h"
33 #include "flightlogdownload.h"
34 
35 #include <QDebug>
36 #include <QtPlugin>
37 #include <QThread>
38 #include <QStringList>
39 #include <QDir>
40 #include <QFileDialog>
41 #include <QList>
42 #include <QErrorMessage>
43 #include <QMainWindow>
44 #include <QWriteLocker>
45 
47 #include <QKeySequence>
49 
51 {
52  // We only ever use one (virtual) logging device, so let's just
53  // initialize it once upon connection init:
54  logDevice.setDisplayName("Logfile replay...");
55  logDevice.setName("Logfile replay...");
56 }
57 
59 
61 {
62  emit availableDevChanged(this);
63 }
64 
66 {
68  list.append(&logDevice);
69  return list;
70 }
71 
72 QIODevice *LoggingConnection::openDevice(IDevice *deviceName)
73 {
74  Q_UNUSED(deviceName)
75 
76  if (logFile.isOpen()) {
77  logFile.close();
78  }
79 
80  QString fileName = QFileDialog::getOpenFileName(
81  dynamic_cast<QWidget *>(Core::ICore::instance()->mainWindow()), tr("Open file"),
82  QString(""), tr("dRonin Log Files (*.drlog *.tll)"));
83 
84  if (!fileName.isNull()) {
85  startReplay(fileName);
86  return &logFile;
87  }
88  return NULL;
89 }
90 
92 {
93  logFile.setFileName(file);
94  if (logFile.open(QIODevice::ReadOnly)) {
95  qDebug() << "Replaying " << file;
96  // state = REPLAY;
97  logFile.startReplay();
98  }
99 }
100 
101 void LoggingConnection::closeDevice(const QString &deviceName)
102 {
103  Q_UNUSED(deviceName);
104  // we have to delete the serial connection we created
105  if (logFile.isOpen()) {
106  logFile.close();
107  m_deviceOpened = false;
108  }
109 }
110 
112 {
113  return QString("Logfile replay");
114 }
115 
117 {
118  return QString("Logfile");
119 }
120 
125 
133 {
134  logFile.setFileName(file);
135  if (!logFile.open(QIODevice::WriteOnly)) {
136  return false;
137  }
138  ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
139  UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
140 
141  uavTalk = new UAVTalk(&logFile, objManager, false);
142  connect(parent, SIGNAL(stopLoggingSignal()), this, SLOT(stopLogging()));
143 
144  return true;
145 };
146 
153 void LoggingThread::objectUpdated(UAVObject *obj)
154 {
155  QWriteLocker locker(&lock);
156  if (!uavTalk->sendObject(obj, false, false))
157  qDebug() << "Error logging " << obj->getName();
158 };
159 
165 {
166  ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
167  UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
168 
169  QVector<QVector<UAVObject *>> list = objManager->getObjectsVector();
170  QVector<QVector<UAVObject *>>::const_iterator i;
171  QVector<UAVObject *>::const_iterator j;
172  int objects = 0;
173 
174  const QVector<QVector<UAVObject *>>::iterator iEnd = list.end();
175  for (i = list.constBegin(); i != iEnd; ++i) {
176  QVector<UAVObject *>::const_iterator jEnd = (*i).constEnd();
177  for (j = (*i).constBegin(); j != jEnd; ++j) {
178  connect(*j, SIGNAL(objectUpdated(UAVObject *)), (LoggingThread *)this,
179  SLOT(objectUpdated(UAVObject *)));
180  objects++;
181  }
182  }
183 
184  GCSTelemetryStats *gcsStatsObj = GCSTelemetryStats::GetInstance(objManager);
185  GCSTelemetryStats::DataFields gcsStats = gcsStatsObj->getData();
186  if (gcsStats.Status == GCSTelemetryStats::STATUS_CONNECTED) {
187  qDebug() << "Logging: connected already, ask for all settings";
188  retrieveSettings();
189  } else {
190  qDebug() << "Logging: not connected, do no ask for settings";
191  }
192 
193  exec();
194 }
195 
200 {
201  QWriteLocker locker(&lock);
202 
203  // Disconnect all objects we registered with:
204  ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
205  UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
206 
207  QVector<QVector<UAVObject *>> list = objManager->getObjectsVector();
208  QVector<QVector<UAVObject *>>::const_iterator i;
209  QVector<UAVObject *>::const_iterator j;
210 
211  const QVector<QVector<UAVObject *>>::iterator iEnd = list.end();
212  for (i = list.constBegin(); i != iEnd; ++i) {
213  QVector<UAVObject *>::const_iterator jEnd = (*i).constEnd();
214  for (j = (*i).constBegin(); j != jEnd; ++j) {
215  disconnect(*j, SIGNAL(objectUpdated(UAVObject *)), (LoggingThread *)this,
216  SLOT(objectUpdated(UAVObject *)));
217  }
218  }
219 
220  logFile.close();
221  qDebug() << "File closed";
222  quit();
223 }
224 
228 void LoggingThread::retrieveSettings()
229 {
230  // Clear object queue
231  queue.clear();
232  // Get all objects, add metaobjects, settings and data objects with OnChange update mode to the
233  // queue
234  // Get UAVObjectManager instance
235  ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
236  UAVObjectManager *objMngr = pm->getObject<UAVObjectManager>();
237  QVector<QVector<UAVDataObject *>> objs = objMngr->getDataObjectsVector();
238  for (int n = 0; n < objs.size(); ++n) {
239  UAVDataObject *obj = objs[n][0];
240  if (obj->isSettings()) {
241  queue.enqueue(obj);
242  }
243  }
244  // Start retrieving
245  qDebug() << tr("Logging: retrieve settings objects from the autopilot (%1 objects)")
246  .arg(queue.length());
247  retrieveNextObject();
248 }
249 
253 void LoggingThread::retrieveNextObject()
254 {
255  // If queue is empty return
256  if (queue.isEmpty()) {
257  qDebug() << "Logging: Object retrieval completed";
258  return;
259  }
260  // Get next object from the queue
261  UAVObject *obj = queue.dequeue();
262  // Connect to object
263  connect(obj, SIGNAL(transactionCompleted(UAVObject *, bool)), this,
264  SLOT(transactionCompleted(UAVObject *, bool)));
265  // Request update
266  obj->requestUpdate();
267 }
268 
272 void LoggingThread::transactionCompleted(UAVObject *obj, bool success)
273 {
274  Q_UNUSED(success);
275  // Disconnect from sending object
276  obj->disconnect(this);
277  // Process next object if telemetry is still available
278  // Get stats objects
279  ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
280  UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
281  GCSTelemetryStats *gcsStatsObj = GCSTelemetryStats::GetInstance(objManager);
282  GCSTelemetryStats::DataFields gcsStats = gcsStatsObj->getData();
283  if (gcsStats.Status == GCSTelemetryStats::STATUS_CONNECTED) {
284  retrieveNextObject();
285  } else {
286  qDebug() << "Logging: Object retrieval has been cancelled";
287  queue.clear();
288  }
289 }
290 
291 /****************************************************************
292  Logging plugin
293  ********************************/
294 
295 LoggingPlugin::LoggingPlugin()
296  : state(IDLE)
297 {
298  logConnection = new LoggingConnection();
299 }
300 
301 LoggingPlugin::~LoggingPlugin()
302 {
303  if (loggingThread)
304  delete loggingThread;
305 
306  // Don't delete it, the plugin manager will do it:
307  // delete logConnection;
308 }
309 
313 bool LoggingPlugin::initialize(const QStringList &args, QString *errMsg)
314 {
315  Q_UNUSED(args);
316  Q_UNUSED(errMsg);
317 
318  loggingThread = NULL;
319 
320  // Add Menu entry
323 
324  // Command to start logging
325  cmdLogging = am->registerAction(new QAction(this), "LoggingPlugin.Logging",
327  cmdLogging->setDefaultKeySequence(QKeySequence("Ctrl+L"));
328  cmdLogging->action()->setText("Start logging...");
329 
330  ac->menu()->addSeparator();
331  ac->appendGroup("Logging");
332  ac->addAction(cmdLogging, "Logging");
333 
334  connect(cmdLogging->action(), SIGNAL(triggered(bool)), this, SLOT(toggleLogging()));
335 
336  // Command to downlaod log
337  cmdDownload = am->registerAction(new QAction(this), "LoggingPlugin.Download",
339  cmdDownload->setDefaultKeySequence(QKeySequence("Ctrl+D"));
340  cmdDownload->action()->setText("Download log...");
341  ac->addAction(cmdDownload, "Logging");
342  connect(cmdDownload->action(), SIGNAL(triggered(bool)), this, SLOT(downloadLog()));
343 
344  mf = new LoggingGadgetFactory(this);
346 
347  // Map signal from end of replay to replay stopped
348  connect(getLogfile(), SIGNAL(replayFinished()), this, SLOT(replayStopped()));
349  connect(getLogfile(), SIGNAL(replayStarted()), this, SLOT(replayStarted()));
350 
351  return true;
352 }
353 
354 void LoggingPlugin::downloadLog()
355 {
356  FlightLogDownload download;
357  download.exec();
358 }
359 
364 void LoggingPlugin::toggleLogging()
365 {
366  if (state == IDLE) {
367 
368  QString fileName = QFileDialog::getSaveFileName(
369  dynamic_cast<QWidget *>(Core::ICore::instance()->mainWindow()), tr("Start Log"),
370  QDir::homePath() + QDir::separator()
371  + tr("dRonin-%0.drlog")
372  .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss")),
373  tr("dRonin Log (*.drlog)"));
374 
375  if (fileName.isEmpty())
376  return;
377 
378  startLogging(fileName);
379  cmdLogging->action()->setText(tr("Stop logging"));
380 
381  } else if (state == LOGGING) {
382  stopLogging();
383  cmdLogging->action()->setText(tr("Start logging..."));
384  }
385 }
386 
390 void LoggingPlugin::startLogging(QString file)
391 {
392  qDebug() << "Logging to " << file;
393  // We have to delete the previous logging thread if is was still there!
394  if (loggingThread)
395  delete loggingThread;
397  if (loggingThread->openFile(file, this)) {
398  connect(loggingThread, SIGNAL(finished()), this, SLOT(loggingStopped()));
399  state = LOGGING;
400  loggingThread->start();
401  emit stateChanged("LOGGING");
402  } else {
403  QErrorMessage err;
404  err.showMessage("Unable to open file for logging");
405  err.exec();
406  }
407 }
408 
412 void LoggingPlugin::stopLogging()
413 {
414  emit stopLoggingSignal();
415  disconnect(this, SIGNAL(stopLoggingSignal()), nullptr, nullptr);
416 }
417 
422 void LoggingPlugin::loggingStopped()
423 {
424  if (state == LOGGING)
425  state = IDLE;
426 
427  emit stateChanged("IDLE");
428 
429  delete loggingThread;
430  loggingThread = NULL;
431 }
432 
436 void LoggingPlugin::replayStopped()
437 {
438  state = IDLE;
439  emit stateChanged("IDLE");
440 }
441 
445 void LoggingPlugin::replayStarted()
446 {
447  state = REPLAY;
448  emit stateChanged("REPLAY");
449 }
450 
451 void LoggingPlugin::extensionsInitialized()
452 {
454 }
455 
456 void LoggingPlugin::shutdown()
457 {
458  if (state == LOGGING) {
459  stopLogging();
460 
461  loggingThread->wait();
462  }
463 
464  if (loggingThread != NULL) {
465  delete loggingThread;
466  loggingThread = NULL;
467  }
468 }
469 
void availableDevChanged(IConnection *)
~LoggingThread()
LoggingThread::~LoggingThread Destructor.
virtual QList< Core::IDevice * > availableDevices()
virtual QIODevice * openDevice(Core::IDevice *deviceName)
LogFile logFile
Definition: loggingplugin.h:98
void requestUpdate()
Definition: uavobject.cpp:163
void stateChanged(QString)
bool open(OpenMode mode)
Definition: logfile.cpp:49
virtual QAction * action() const =0
void setName(QString theName)
Definition: idevice.h:50
virtual Command * registerAction(QAction *action, const QString &id, const QList< int > &context)=0
Makes an action known to the system under the specified string id.
const char *const M_TOOLS
Definition: coreconstants.h:83
virtual ActionContainer * actionContainer(const QString &id) const =0
Returns the IActionContainter object that is know to the system under the given string id...
LoggingThread * loggingThread
QVector< QVector< UAVObject * > > getObjectsVector()
Core plugin system that manages the plugins, their life cycle and their registered objects...
Definition: pluginmanager.h:53
bool startReplay()
Definition: logfile.cpp:255
void startReplay(QString file)
virtual ActionManager * actionManager() const =0
Returns the application's action manager.
for i
Definition: OPPlots.m:140
bool sendObject(UAVObject *obj, bool acked, bool allInstances)
Definition: uavtalk.cpp:155
UAVTalk * uavTalk
Definition: loggingplugin.h:99
virtual QString shortName()
enum LoggingPlugin::@8 state
virtual QMenu * menu() const =0
const int C_GLOBAL_ID
Definition: coreconstants.h:90
Parse log file
void setDisplayName(QString dn)
Definition: idevice.h:52
virtual void closeDevice(const QString &deviceName)
static ICore * instance()
Definition: coreimpl.cpp:46
Eccentricity n
Definition: OPPlots.m:137
virtual void appendGroup(const QString &group)=0
virtual void addAction(Core::Command *action, const QString &group=QString())=0
void stopLoggingSignal(void)
void setFileName(QString name)
Definition: logfile.h:47
LoggingConnection * logConnection
virtual void setDefaultKeySequence(const QKeySequence &key)=0
QReadWriteLock lock
Definition: loggingplugin.h:97
virtual QString connectionName()
void close()
Definition: logfile.cpp:160
virtual ~LoggingConnection()
The action manager is responsible for registration of menus and menu items and keyboard shortcuts...
Definition: actionmanager.h:47
bool openFile(QString file, LoggingPlugin *parent)
QString getName()
Definition: uavobject.cpp:131
void addAutoReleasedObject(QObject *obj)
Definition: iplugin.cpp:306
Import/Export Plugin.
QVector< QVector< UAVDataObject * > > getDataObjectsVector()