dRonin  adbada4
dRonin GCS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
usagestatsplugin.cpp
Go to the documentation of this file.
1 
12 /*
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful, but
19  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  * for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with this program; if not, see <http://www.gnu.org/licenses/>
25  */
26 #include "usagestatsplugin.h"
27 #include <QCryptographicHash>
28 #include <QDebug>
29 #include <QtPlugin>
30 #include <QStringList>
33 #include "coreplugin/icore.h"
34 #include <QMainWindow>
35 #include <QPushButton>
36 #include <QAbstractSlider>
37 #include <QAbstractSpinBox>
38 #include <QTabBar>
39 #include <QJsonObject>
40 #include <QJsonDocument>
41 #include <QJsonArray>
43 #include <QTimer>
44 #include <QScrollBar>
45 #include <QSysInfo>
46 #include "usagestatsoptionpage.h"
47 
48 UsageStatsPlugin::UsageStatsPlugin()
49  : sendUsageStats(true)
50  , sendPrivateData(true)
51  , installationUUID("")
52  , debugLogLevel(DebugEngine::WARNING)
53 {
54  loop = new QEventLoop(this);
55  connect(&netMngr, &QNetworkAccessManager::finished, loop, &QEventLoop::quit);
56 }
57 
58 UsageStatsPlugin::~UsageStatsPlugin()
59 {
61 }
62 
63 void UsageStatsPlugin::readConfig(QSettings *qSettings, Core::UAVConfigInfo *configInfo)
64 {
65  Q_UNUSED(configInfo)
66  qSettings->beginGroup(QLatin1String("UsageStatistics"));
67  sendUsageStats = (qSettings->value(QLatin1String("SendUsageStats"), sendUsageStats).toBool());
68  sendPrivateData =
69  (qSettings->value(QLatin1String("SendPrivateData"), sendPrivateData).toBool());
70  // Check the Installation UUID and Generate a new one if required
71  installationUUID = QUuid(qSettings->value(QLatin1String("InstallationUUID"), "").toString());
72  if (installationUUID.isNull()) { // Create new UUID
73  installationUUID = QUuid::createUuid();
74  }
75  debugLogLevel = (qSettings->value(QLatin1String("DebugLogLevel"), debugLogLevel).toInt());
76 
77  qSettings->endGroup();
78 }
79 
80 void UsageStatsPlugin::saveConfig(QSettings *qSettings, Core::UAVConfigInfo *configInfo)
81 {
82  Q_UNUSED(configInfo)
83  qSettings->beginGroup(QLatin1String("UsageStatistics"));
84  qSettings->setValue(QLatin1String("SendUsageStats"), sendUsageStats);
85  qSettings->setValue(QLatin1String("SendPrivateData"), sendPrivateData);
86  qSettings->setValue(QLatin1String("InstallationUUID"), installationUUID.toString());
87  qSettings->setValue(QLatin1String("DebugLogLevel"), debugLogLevel);
88 
89  qSettings->endGroup();
90 }
91 
92 void UsageStatsPlugin::shutdown()
93 {
94 }
95 
96 bool UsageStatsPlugin::initialize(const QStringList &arguments, QString *errorString)
97 {
98  Q_UNUSED(arguments)
99  Q_UNUSED(errorString)
100  Core::ICore::instance()->readSettings(this);
104  return true;
105 }
106 
107 void UsageStatsPlugin::extensionsInitialized()
108 {
109  pluginManager = ExtensionSystem::PluginManager::instance();
110  Q_ASSERT(pluginManager);
111  connect(pluginManager, &ExtensionSystem::PluginManager::pluginsLoadEnded, this,
112  &UsageStatsPlugin::pluginsLoadEnded);
113 }
114 
115 bool UsageStatsPlugin::coreAboutToClose()
116 {
117  if (!sendUsageStats)
118  return true;
119  QTimer::singleShot(10000, loop, &QEventLoop::quit);
120  QUrl url("http://dronin-autotown.appspot.com/usageStats");
121  QNetworkRequest request(url);
122  request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json; charset=utf-8");
123  netMngr.post(request, processJson());
124  loop->exec();
125  return true;
126 }
127 
128 void UsageStatsPlugin::pluginsLoadEnded()
129 {
130  updateSettings();
131 }
132 
134 {
135  UploaderGadgetFactory *uploader = pluginManager->getObject<UploaderGadgetFactory>();
136  QMainWindow *mw = Core::ICore::instance()->mainWindow();
137  if (sendUsageStats) {
139  &UsageStatsPlugin::onDebugMessage,
140  static_cast<Qt::ConnectionType>(Qt::QueuedConnection
141  | Qt::UniqueConnection) /* sigh */);
142  if (uploader)
143  connect(uploader, &uploader::UploaderGadgetFactory::newBoardSeen, this,
144  &UsageStatsPlugin::addNewBoardSeen, Qt::UniqueConnection);
145  if (mw)
146  searchForWidgets(mw, true);
147  } else {
148  if (uploader)
149  disconnect(uploader, &uploader::UploaderGadgetFactory::newBoardSeen, this,
150  &UsageStatsPlugin::addNewBoardSeen);
151  if (mw)
152  searchForWidgets(mw, false);
153  }
155 }
156 
157 void UsageStatsPlugin::addNewBoardSeen(deviceInfo board, deviceDescriptorStruct device)
158 {
159  boardLog b;
160  b.time = QDateTime::currentDateTime();
161  b.board = board;
162  b.device = device;
163  boardLogList.append(b);
164 }
165 
166 void UsageStatsPlugin::searchForWidgets(QObject *mw, bool conn)
167 {
168  foreach (QObject *obj, mw->children()) {
169  QAbstractButton *button = qobject_cast<QAbstractButton *>(obj);
170  QAbstractSlider *slider = qobject_cast<QAbstractSlider *>(obj);
171  QScrollBar *bar = qobject_cast<QScrollBar *>(obj);
172  QTabBar *tab = qobject_cast<QTabBar *>(obj);
173  if (button) {
174  if (conn) {
175  connect(button, &QAbstractButton::clicked, this, &UsageStatsPlugin::onButtonClicked,
176  Qt::UniqueConnection);
177  } else {
178  disconnect(button, &QAbstractButton::clicked, this,
179  &UsageStatsPlugin::onButtonClicked);
180  }
181  } else if (slider && !bar) {
182  if (conn) {
183  connect(slider, &QAbstractSlider::valueChanged, this,
184  &UsageStatsPlugin::onSliderValueChanged, Qt::UniqueConnection);
185  } else {
186  disconnect(slider, &QAbstractSlider::valueChanged, this,
187  &UsageStatsPlugin::onSliderValueChanged);
188  }
189  } else if (tab) {
190  if (conn) {
191  connect(tab, &QTabBar::currentChanged, this, &UsageStatsPlugin::onTabCurrentChanged,
192  Qt::UniqueConnection);
193  } else {
194  disconnect(tab, &QTabBar::currentChanged, this,
195  &UsageStatsPlugin::onTabCurrentChanged);
196  }
197  } else {
198  searchForWidgets(obj, conn);
199  }
200  }
201 }
202 
203 void UsageStatsPlugin::onButtonClicked()
204 {
206  QAbstractButton *button = qobject_cast<QAbstractButton *>(sender());
207  info.objectName = button->objectName();
208  info.data1 = button->text();
209  info.data2 = QString::number(button->isChecked());
210  info.className = button->metaObject()->className();
211  info.parentName = button->parent()->objectName();
212  info.time = QDateTime::currentDateTime();
213  info.type = WIDGET_BUTTON;
214  widgetLogList.append(info);
215 }
216 
217 void UsageStatsPlugin::onSliderValueChanged(int value)
218 {
219  widgetActionInfo info;
220  QAbstractSlider *slider = qobject_cast<QAbstractSlider *>(sender());
221  info.objectName = slider->objectName();
222  info.data1 = QString::number(value);
223  info.className = slider->metaObject()->className();
224  info.parentName = slider->parent()->objectName();
225  info.time = QDateTime::currentDateTime();
226  info.type = WIDGET_SLIDER;
227  widgetLogList.append(info);
228 }
229 
230 void UsageStatsPlugin::onTabCurrentChanged(int value)
231 {
232  widgetActionInfo info;
233  QTabBar *tab = qobject_cast<QTabBar *>(sender());
234  info.objectName = tab->objectName();
235  info.data2 = QString::number(value);
236  info.data1 = tab->tabText(value);
237  info.className = tab->metaObject()->className();
238  info.parentName = tab->parent()->objectName();
239  info.time = QDateTime::currentDateTime();
240  info.type = WIDGET_TAB;
241  widgetLogList.append(info);
242 }
243 
244 QByteArray UsageStatsPlugin::processJson()
245 {
246  QJsonObject json;
247  if (sendPrivateData)
248  json["shareIP"] = "true";
249  else
250  json["shareIP"] = "false";
251  json["gcs_version"] = QLatin1String(Core::Constants::GCS_VERSION_LONG);
252  json["gcs_revision"] = QLatin1String(Core::Constants::GCS_REVISION_PRETTY_STR);
253  json["currentOS"] = QSysInfo::prettyProductName();
254  json["currentArch"] = QSysInfo::currentCpuArchitecture();
255  json["buildInfo"] = QSysInfo::buildAbi();
256 
257  if (!installationUUID.isNull())
258  json["installationUUID"] = getInstallationUUID();
259 
260  QJsonArray boardArray;
261  foreach (boardLog board, boardLogList) {
262  QJsonObject b;
263  b["time"] = board.time.toUTC().toString("yyyy-MM-ddThh:mm:ss.zzzZ");
264  b["name"] = board.board.board.data()->getBoardNameFromID(board.device.boardID());
265  b["uavoHash"] = QString(board.device.uavoHash.toHex());
266  b["ID"] = board.device.boardID();
267  b["fwHash"] = QString(board.device.fwHash.toHex());
268  b["gitDate"] = board.device.gitDate;
269  b["gitHash"] = board.device.gitHash;
270  b["gitTag"] = board.device.gitTag;
271  b["nextAncestor"] = board.device.nextAncestor;
272  if (sendPrivateData)
273  b["UUID"] = QString(
274  QCryptographicHash::hash(QByteArray::fromHex(board.board.cpu_serial.toUtf8()),
275  QCryptographicHash::Sha256)
276  .toHex());
277  boardArray.append(b);
278  }
279  json["boardsSeen"] = boardArray;
280  QJsonArray widgetArray;
281  foreach (widgetActionInfo w, widgetLogList) {
282  QJsonObject j;
283  j["time"] = w.time.toUTC().toString("yyyy-MM-ddThh:mm:ss.zzzZ");
284  j["className"] = w.className;
285  j["objectName"] = w.objectName;
286  j["parentName"] = w.parentName;
287  switch (w.type) {
288  case WIDGET_BUTTON:
289  j["text"] = w.data1;
290  j["checked"] = w.data2;
291  break;
292  case WIDGET_SLIDER:
293  j["value"] = w.data1;
294  break;
295  case WIDGET_TAB:
296  j["text"] = w.data1;
297  j["index"] = w.data2;
298  break;
299  default:
300  break;
301  }
302  widgetArray.append(j);
303  }
304  json["widgets"] = widgetArray;
305 
306  QJsonArray debugLogArray;
307  foreach (const DebugMessage msg, debugMessageList) {
308  QJsonObject d;
309  d["time"] = msg.time.toUTC().toString("yyyy-MM-ddThh:mm:ss.zzzZ");
310  d["level"] = msg.level;
311  d["levelString"] = msg.levelString;
312  d["message"] = msg.message;
313  d["file"] = msg.file;
314  d["line"] = msg.line;
315  d["function"] = msg.function;
316  debugLogArray.append(d);
317  }
318  json["debugLog"] = debugLogArray;
319  json["debugLevel"] = debugLogLevel;
320 
321  return QJsonDocument(json).toJson();
322 }
323 bool UsageStatsPlugin::getSendPrivateData() const
324 {
325  return sendPrivateData;
326 }
327 
328 void UsageStatsPlugin::setSendPrivateData(bool value)
329 {
330  sendPrivateData = value;
331 }
332 
333 bool UsageStatsPlugin::getSendUsageStats() const
334 {
335  return sendUsageStats;
336 }
337 
338 void UsageStatsPlugin::setSendUsageStats(bool value)
339 {
340  sendUsageStats = value;
341 }
342 
343 QString UsageStatsPlugin::getInstallationUUID() const
344 {
345  return installationUUID.toString().remove(QRegExp("[{}]*"));
346 }
347 
348 int UsageStatsPlugin::getDebugLogLevel() const
349 {
350  return debugLogLevel;
351 }
352 
353 void UsageStatsPlugin::setDebugLogLevel(int value)
354 {
355  debugLogLevel = value;
356 }
357 
358 void UsageStatsPlugin::onDebugMessage(DebugEngine::Level level, const QString &msg,
359  const QString &file, const int line, const QString &function)
360 {
361  if (level < debugLogLevel)
362  return;
363 
364  DebugMessage info;
365  switch (level) {
366  case DebugEngine::DEBUG:
367  info.levelString = "debug";
368  break;
369  case DebugEngine::INFO:
370  info.levelString = "info";
371  break;
373  info.levelString = "warning";
374  break;
376  info.levelString = "critical";
377  break;
378  case DebugEngine::FATAL:
379  info.levelString = "fatal";
380  break;
381  }
382 
383  info.time = QDateTime::currentDateTime();
384  info.level = level;
385  info.message = msg;
386  info.file = file;
387  info.line = line;
388  info.function = function;
389 
390  debugMessageList.append(info);
391 }
392 
394  : Core::ICoreListener(parent)
395  , m_parent(parent)
396 {
397 }
398 
400 {
401  return m_parent->coreAboutToClose();
402 }
const char *const GCS_REVISION_PRETTY_STR
Definition: coreconstants.h:53
void message(DebugEngine::Level level, const QString &msg, const QString &file="", const int line=0, const QString &function="")
deviceDescriptorStruct device
AppCloseHook(UsageStatsPlugin *parent)
Parse log file
const char *const GCS_VERSION_LONG
Definition: coreconstants.h:51
virtual QMainWindow * mainWindow() const =0
Returns the main application window.
static ICore * instance()
Definition: coreimpl.cpp:46
deviceInfo board
void newBoardSeen(deviceInfo board, deviceDescriptorStruct device)
The Config Info is a helper-class to handle version changes in GCS configuration files.
Definition: uavconfiginfo.h:53
QPointer< Core::IBoardType > board
static DebugEngine * getInstance()
Definition: debugengine.cpp:35
virtual void saveSettings(IConfigurablePlugin *plugin, QSettings *qs=nullptr)=0
void addAutoReleasedObject(QObject *obj)
Definition: iplugin.cpp:306
QDateTime time
QString cpu_serial