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
9 * @addtogroup UsageTrackerPlugin Usage Tracker Plugin
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
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"
30 #include <QStringList>
31 #include <QtNetwork/QNetworkAccessManager>
32 #include <QtNetwork/QNetworkRequest>
33 #include <QtNetwork/QNetworkReply>
34 #include <extensionsystem/pluginmanager.h>
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
)
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()) {
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());
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
);
131 QMapIterator
<QString
, QString
> iter(parameters
);
132 while (iter
.hasNext()) {
134 query
.addQueryItem(iter
.key(), iter
.value());
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
> ¶meters
)
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();
174 parameters
["os_version"] = "none";
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) {
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) {
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
;
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
);
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
>();
286 bool UsageTrackerPlugin::shouldSend(const QString
&hash
)
288 if (getGeneralSettings()->lastUsageHash() == hash
) {