Merged in f5soh/librepilot/update_credits (pull request #529)
[librepilot.git] / ground / gcs / src / plugins / usagetracker / usagetrackerplugin.cpp
blob232d0d7334d8def3dd3e53eb554e642f76ecb8d4
1 /**
2 ******************************************************************************
4 * @file usagetrackerplugin.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015
6 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2015.
7 * @addtogroup GCSPlugins GCS Plugins
8 * @{
9 * @addtogroup UsageTrackerPlugin Usage Tracker Plugin
10 * @{
11 * @brief A plugin tracking GCS usage
12 *****************************************************************************/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "usagetrackerplugin.h"
29 #include <QtPlugin>
30 #include <QStringList>
31 #include <QtNetwork/QNetworkAccessManager>
32 #include <QtNetwork/QNetworkRequest>
33 #include <QtNetwork/QNetworkReply>
34 #include <extensionsystem/pluginmanager.h>
35 #include <QCheckBox>
36 #include <QDebug>
37 #include <QMessageBox>
38 #include <uavobjectutil/devicedescriptorstruct.h>
39 #include <uavobjectutil/uavobjectutilmanager.h>
40 #include "version_info/version_info.h"
41 #include "coreplugin/icore.h"
42 #include <uavtalk/telemetrymanager.h>
44 UsageTrackerPlugin::UsageTrackerPlugin() :
45 m_telemetryManager(NULL)
48 UsageTrackerPlugin::~UsageTrackerPlugin()
51 bool UsageTrackerPlugin::initialize(const QStringList & args, QString *errMsg)
53 Q_UNUSED(args);
54 Q_UNUSED(errMsg);
56 return true;
59 void UsageTrackerPlugin::extensionsInitialized()
61 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
63 m_telemetryManager = pm->getObject<TelemetryManager>();
64 connect(m_telemetryManager, SIGNAL(connected()), this, SLOT(onAutopilotConnect()));
67 void UsageTrackerPlugin::shutdown()
69 if (m_telemetryManager != NULL) {
70 disconnect(m_telemetryManager, SIGNAL(connected()), this, SLOT(onAutopilotConnect()));
74 void UsageTrackerPlugin::onAutopilotConnect()
76 Core::Internal::GeneralSettings *settings = getGeneralSettings();
78 if (settings->collectUsageData()) {
79 if (settings->showUsageDataDisclaimer()) {
80 QMessageBox message;
81 // QMessageBox hack to define window size.
82 message.setStyleSheet("QLabel{min-width: 780px; max-height: 520px;}");
83 message.setWindowTitle(tr("Usage feedback"));
84 message.addButton(tr("Yes, count me in"), QMessageBox::AcceptRole);
85 message.addButton(tr("No, I will not help"), QMessageBox::RejectRole);
86 message.setText(tr("<p>%1 has a function to collect limited anonymous information about "
87 "the usage of the application itself and the hardware connected to it.</p>"
88 "<p>The intention is to not include anything that can be considered sensitive "
89 "or a threat to the users integrity. The collected information will be sent "
90 "using a secure protocol to an %2 web service and stored in a database "
91 "for later analysis and statistical purposes. "
92 "No information will be sold or given to any third party. The sole purpose is "
93 "to collect statistics about the usage of our software and hardware to enable us "
94 "to make things better for you.</p>"
95 "The following things are collected:<ul>"
96 "<li>Bootloader version</li>"
97 "<li>Firmware version, tag and git hash</li>"
98 "<li>Hardware type, revision and mcu serial number</li>"
99 "<li>Selected configuration parameters</li>"
100 "<li>GCS version</li>"
101 "<li>Operating system version and architecture</li>"
102 "<li>Current local time</li></ul>"
103 "<p>The information is collected only at the time when a board is connecting to GCS. "
104 "It is possible to enable or disable this functionality in the general "
105 "settings part of the options for the GCS application at any time.</p>"
106 "<p>We need your help, with your feedback we know where to improve things and what "
107 "platforms are in use. This is a community project that depends on people being involved.</p>"
108 "Thank You for helping us making things better and for supporting %2!").arg(GCS_BIG_NAME).arg(ORG_BIG_NAME));
109 QCheckBox *disclaimerCb = new QCheckBox(tr("&Don't show this message again."));
110 disclaimerCb->setChecked(true);
111 message.setCheckBox(disclaimerCb);
112 if (message.exec() != QMessageBox::AcceptRole) {
113 settings->setCollectUsageData(false);
114 settings->setShowUsageDataDisclaimer(!message.checkBox()->isChecked());
115 return;
116 } else {
117 settings->setCollectUsageData(true);
118 settings->setShowUsageDataDisclaimer(!message.checkBox()->isChecked());
121 QTimer::singleShot(1000, this, SLOT(trackUsage()));
125 void UsageTrackerPlugin::trackUsage()
127 QMap<QString, QString> parameters;
128 collectUsageParameters(parameters);
130 QUrlQuery query;
131 QMapIterator<QString, QString> iter(parameters);
132 while (iter.hasNext()) {
133 iter.next();
134 query.addQueryItem(iter.key(), iter.value());
137 // Add checksum
138 QString hash = getQueryHash(query.toString());
140 if (shouldSend(hash)) {
141 query.addQueryItem("hash", hash);
143 QUrl url(QString(USAGETRACKER_URL) + "?" + query.toString(QUrl::FullyEncoded));
145 QNetworkAccessManager *networkAccessManager = new QNetworkAccessManager();
147 // This will delete the network access manager instance when we're done
148 connect(networkAccessManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(onFinished(QNetworkReply *)));
149 connect(networkAccessManager, SIGNAL(finished(QNetworkReply *)), networkAccessManager, SLOT(deleteLater()));
151 qDebug() << "Sending usage tracking as:" << url.toEncoded(QUrl::FullyEncoded);
152 networkAccessManager->get(QNetworkRequest(QUrl(url.toEncoded(QUrl::FullyEncoded))));
156 void UsageTrackerPlugin::collectUsageParameters(QMap<QString, QString> &parameters)
158 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
159 UAVObjectUtilManager *utilMngr = pm->getObject<UAVObjectUtilManager>();
161 QByteArray description = utilMngr->getBoardDescription();
162 deviceDescriptorStruct devDesc;
164 if (UAVObjectUtilManager::descriptionToStructure(description, devDesc)) {
165 int boardModel = utilMngr->getBoardModel();
166 parameters["board_type"] = "0x" + QString::number(boardModel, 16).toLower();
167 parameters["board_serial"] = utilMngr->getBoardCPUSerial().toHex();
168 parameters["bl_version"] = QString::number(utilMngr->getBootloaderRevision());
169 parameters["fw_tag"] = devDesc.gitTag;
170 parameters["fw_hash"] = devDesc.gitHash;
171 #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
172 parameters["os_version"] = QSysInfo::prettyProductName() + " " + QSysInfo::currentCpuArchitecture();
173 #else
174 parameters["os_version"] = "none";
175 #endif
176 parameters["os_threads"] = QString::number(QThread::idealThreadCount());
177 parameters["os_timezone"] = QTimeZone::systemTimeZoneId();
178 parameters["gcs_version"] = VersionInfo::revision();
180 // Configuration parameters
181 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
182 UAVObjectManager *objManager = pm->getObject<UAVObjectManager>();
184 parameters["conf_receiver"] = getUAVFieldValue(objManager, "ManualControlSettings", "ChannelGroups", 0);
185 parameters["conf_vehicle"] = getUAVFieldValue(objManager, "SystemSettings", "AirframeType");
187 if ((boardModel & 0xff00) == 0x0400) {
188 // CopterControl family
189 parameters["conf_rport"] = getUAVFieldValue(objManager, "HwSettings", "CC_RcvrPort");
190 parameters["conf_mport"] = getUAVFieldValue(objManager, "HwSettings", "CC_MainPort");
191 parameters["conf_fport"] = getUAVFieldValue(objManager, "HwSettings", "CC_FlexiPort");
192 } else if ((boardModel & 0xff00) == 0x0900) {
193 // Revolution family
194 parameters["conf_rport"] = getUAVFieldValue(objManager, "HwSettings", "RM_RcvrPort");
195 parameters["conf_mport"] = getUAVFieldValue(objManager, "HwSettings", "RM_MainPort");
196 parameters["conf_fport"] = getUAVFieldValue(objManager, "HwSettings", "RM_FlexiPort");
197 parameters["conf_fusion"] = getUAVFieldValue(objManager, "RevoSettings", "FusionAlgorithm");
198 } else if ((boardModel & 0xff00) == 0x9200) {
199 // Sparky2
200 parameters["conf_rport"] = getUAVFieldValue(objManager, "HwSettings", "SPK2_RcvrPort");
201 parameters["conf_mport"] = getUAVFieldValue(objManager, "HwSettings", "SPK2_MainPort");
202 parameters["conf_fport"] = getUAVFieldValue(objManager, "HwSettings", "SPK2_FlexiPort");
203 parameters["conf_iport"] = getUAVFieldValue(objManager, "HwSettings", "SPK2_I2CPort");
204 parameters["conf_fusion"] = getUAVFieldValue(objManager, "RevoSettings", "FusionAlgorithm");
207 parameters["conf_uport"] = getUAVFieldValue(objManager, "HwSettings", "USB_HIDPort");
208 parameters["conf_vport"] = getUAVFieldValue(objManager, "HwSettings", "USB_VCPPort");
210 parameters["conf_acceltau"] = getUAVFieldValue(objManager, "AttitudeSettings", "AccelTau");
211 parameters["conf_rotation"] = QString("[%1:%2:%3]")
212 .arg(getUAVFieldValue(objManager, "AttitudeSettings", "BoardRotation", 0))
213 .arg(getUAVFieldValue(objManager, "AttitudeSettings", "BoardRotation", 1))
214 .arg(getUAVFieldValue(objManager, "AttitudeSettings", "BoardRotation", 2));
216 parameters["conf_pidr"] = QString("[%1:%2:%3:%4][%5:%6:%7:%8][%9:%10:%11:%12]")
217 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "RollRatePID", 0))
218 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "RollRatePID", 1))
219 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "RollRatePID", 2))
220 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "RollRatePID", 3))
221 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "PitchRatePID", 0))
222 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "PitchRatePID", 1))
223 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "PitchRatePID", 2))
224 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "PitchRatePID", 3))
225 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "YawRatePID", 0))
226 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "YawRatePID", 1))
227 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "YawRatePID", 2))
228 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "YawRatePID", 3));
229 parameters["conf_pia"] = QString("[%1:%2:%3][%4:%5:%6][%7:%8:%9]")
230 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "RollPI", 0))
231 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "RollPI", 1))
232 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "RollPI", 2))
233 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "PitchPI", 0))
234 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "PitchPI", 1))
235 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "PitchPI", 2))
236 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "YawPI", 0))
237 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "YawPI", 1))
238 .arg(getUAVFieldValue(objManager, "StabilizationSettingsBank1", "YawPI", 2));
240 parameters["conf_tps"] = getUAVFieldValue(objManager, "StabilizationSettingsBank1", "EnableThrustPIDScaling");
241 parameters["conf_piro"] = getUAVFieldValue(objManager, "StabilizationSettingsBank1", "EnablePiroComp");
243 parameters["conf_fmcount"] = getUAVFieldValue(objManager, "ManualControlSettings", "FlightModeNumber");
244 parameters["conf_fmodes"] = QString("[%1:%2:%3]").arg(getUAVFieldValue(objManager, "FlightModeSettings", "FlightModePosition", 0))
245 .arg(getUAVFieldValue(objManager, "FlightModeSettings", "FlightModePosition", 1))
246 .arg(getUAVFieldValue(objManager, "FlightModeSettings", "FlightModePosition", 2));
250 void UsageTrackerPlugin::onFinished(QNetworkReply *reply)
252 if (reply->error() == QNetworkReply::NoError) {
253 getGeneralSettings()->setLastUsageHash(m_lastHash);
254 qDebug() << "Updated last usage hash to:" << m_lastHash;
255 } else {
256 qDebug() << "Usage tracking failed with:" << reply->errorString();
260 QString UsageTrackerPlugin::getUAVFieldValue(UAVObjectManager *objManager, QString objectName, QString fieldName, int index) const
262 UAVObject *object = objManager->getObject(objectName);
264 if (object != NULL) {
265 UAVObjectField *field = object->getField(fieldName);
266 if (field != NULL) {
267 return field->getValue(index).toString();
270 return tr("Unknown");
273 QString UsageTrackerPlugin::getQueryHash(QString source) const
275 return QString(QCryptographicHash::hash(QByteArray(source.toStdString().c_str()), QCryptographicHash::Md5).toHex());
278 Core::Internal::GeneralSettings *UsageTrackerPlugin::getGeneralSettings() const
280 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
281 Core::Internal::GeneralSettings *settings = pm->getObject<Core::Internal::GeneralSettings>();
283 return settings;
286 bool UsageTrackerPlugin::shouldSend(const QString &hash)
288 if (getGeneralSettings()->lastUsageHash() == hash) {
289 return false;
290 } else {
291 m_lastHash = hash;
292 return true;