QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
BluetoothLink.cc
Go to the documentation of this file.
1#include "BluetoothLink.h"
2#include "BluetoothWorker.h"
4
5#include <QtCore/QCoreApplication>
6#include <QtCore/QPermissions>
7#include <QtCore/QThread>
8
9QGC_LOGGING_CATEGORY(BluetoothLinkLog, "Comms.Bluetooth.BluetoothLink")
10
11/*===========================================================================*/
12
14 : LinkInterface(config, parent)
15 , _bluetoothConfig(qobject_cast<BluetoothConfiguration*>(config.get()))
16 , _workerThread(new QThread(this))
17{
18 Q_ASSERT(_bluetoothConfig);
19 if (!_bluetoothConfig) {
20 qCCritical(BluetoothLinkLog) << "Invalid BluetoothConfiguration";
21 return;
22 }
23
24 _worker = BluetoothWorker::create(_bluetoothConfig);
25
26 qCDebug(BluetoothLinkLog) << this;
27
28 const QString threadName = (_bluetoothConfig->mode() == BluetoothConfiguration::BluetoothMode::ModeLowEnergy)
29 ? QStringLiteral("BLE_%1") : QStringLiteral("Bluetooth_%1");
30 _workerThread->setObjectName(threadName.arg(_bluetoothConfig->name()));
31
32 _worker->moveToThread(_workerThread.data());
33
34 (void) connect(_workerThread.data(), &QThread::started, _worker.data(), &BluetoothWorker::setupConnection);
35 (void) connect(_workerThread.data(), &QThread::finished, _worker.data(), &QObject::deleteLater);
36
37 (void) connect(_worker.data(), &BluetoothWorker::connected, this, &BluetoothLink::_onConnected, Qt::QueuedConnection);
38 (void) connect(_worker.data(), &BluetoothWorker::disconnected, this, &BluetoothLink::_onDisconnected, Qt::QueuedConnection);
39 (void) connect(_worker.data(), &BluetoothWorker::errorOccurred, this, &BluetoothLink::_onErrorOccurred, Qt::QueuedConnection);
40 (void) connect(_worker.data(), &BluetoothWorker::dataReceived, this, &BluetoothLink::_onDataReceived, Qt::QueuedConnection);
41 (void) connect(_worker.data(), &BluetoothWorker::dataSent, this, &BluetoothLink::_onDataSent, Qt::QueuedConnection);
42 (void) connect(_worker.data(), &BluetoothWorker::rssiUpdated, this, &BluetoothLink::_onRssiUpdated, Qt::QueuedConnection);
43
44 (void) connect(_bluetoothConfig, &BluetoothConfiguration::errorOccurred, this, &BluetoothLink::_onErrorOccurred);
45
46 _checkPermission();
47}
48
50{
51 if (_workerThread) {
52 if (_workerThread->isRunning() && _connectedCache && _worker) {
53 (void) QMetaObject::invokeMethod(_worker, "disconnectLink", Qt::QueuedConnection);
54 }
55
56 _workerThread->quit();
57 if (!_workerThread->wait(5000)) {
58 qCWarning(BluetoothLinkLog) << "Worker thread did not stop within timeout, terminating";
59 _workerThread->terminate();
60 (void) _workerThread->wait(1000);
61 }
62 }
63
64 qCDebug(BluetoothLinkLog) << this;
65}
66
68{
69 return _connectedCache;
70}
71
72bool BluetoothLink::_connect()
73{
74 if (!_worker) {
75 return false;
76 }
77 if (_bluetoothConfig) {
78 _bluetoothConfig->stopScan();
79 }
80 return QMetaObject::invokeMethod(_worker.data(), "connectLink", Qt::QueuedConnection);
81}
82
84{
85 if (_worker) {
86 (void) QMetaObject::invokeMethod(_worker.data(), "disconnectLink", Qt::QueuedConnection);
87 }
88}
89
90void BluetoothLink::_onConnected()
91{
92 _connectedCache = true;
93 _disconnectedEmitted = false;
94 emit connected();
95}
96
97void BluetoothLink::_onDisconnected()
98{
99 _connectedCache = false;
100 if (!_disconnectedEmitted.exchange(true)) {
101 emit disconnected();
102 }
103}
104
105void BluetoothLink::_onErrorOccurred(const QString &errorString)
106{
107 qCWarning(BluetoothLinkLog) << "Communication error:" << errorString;
108
109 if (!_bluetoothConfig) {
110 emit communicationError(tr("Bluetooth Link Error"), errorString);
111 return;
112 }
113
114 const QString linkType = (_bluetoothConfig->mode() == BluetoothConfiguration::BluetoothMode::ModeLowEnergy)
115 ? tr("Bluetooth Low Energy") : tr("Bluetooth");
116
117 emit communicationError(tr("%1 Link Error").arg(linkType),
118 tr("Link %1: (Device: %2) %3").arg(_bluetoothConfig->name(),
119 _bluetoothConfig->device().name(),
120 errorString));
121}
122
123void BluetoothLink::_onDataReceived(const QByteArray &data)
124{
125 emit bytesReceived(this, data);
126}
127
128void BluetoothLink::_onDataSent(const QByteArray &data)
129{
130 emit bytesSent(this, data);
131}
132
133void BluetoothLink::_onRssiUpdated(qint16 rssi)
134{
135 if (_bluetoothConfig) {
136 _bluetoothConfig->setConnectedRssi(rssi);
137 }
138}
139
140void BluetoothLink::_writeBytes(const QByteArray &bytes)
141{
142 if (_worker) {
143 (void) QMetaObject::invokeMethod(_worker.data(), "writeData", Qt::QueuedConnection, Q_ARG(QByteArray, bytes));
144 }
145}
146
147void BluetoothLink::_checkPermission()
148{
149 QBluetoothPermission permission;
150 permission.setCommunicationModes(QBluetoothPermission::Access);
151
152 const Qt::PermissionStatus permissionStatus = QCoreApplication::instance()->checkPermission(permission);
153 if (permissionStatus == Qt::PermissionStatus::Undetermined) {
154 QCoreApplication::instance()->requestPermission(permission, this, [this](const QPermission &perm) {
155 _handlePermissionStatus(perm.status());
156 });
157 } else {
158 _handlePermissionStatus(permissionStatus);
159 }
160}
161
162void BluetoothLink::_handlePermissionStatus(Qt::PermissionStatus permissionStatus)
163{
164 if (permissionStatus != Qt::PermissionStatus::Granted) {
165 qCWarning(BluetoothLinkLog) << "Bluetooth Permission Denied";
166 _onErrorOccurred(tr("Bluetooth Permission Denied"));
167 _onDisconnected();
168 return;
169 }
170
171 qCDebug(BluetoothLinkLog) << "Bluetooth Permission Granted";
172
173 if (_workerThread && !_workerThread->isRunning()) {
174 _workerThread->start();
175 if (!_workerThread->isRunning()) {
176 qCCritical(BluetoothLinkLog) << "Failed to start worker thread";
177 _onErrorOccurred(tr("Failed to start Bluetooth worker thread"));
178 _worker->deleteLater();
179 _worker = nullptr;
180 }
181 }
182}
std::shared_ptr< LinkConfiguration > SharedLinkConfigurationPtr
QString errorString
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void errorOccurred(const QString &errorString)
const QBluetoothDeviceInfo & device() const
BluetoothMode mode() const
void dataReceived(const QByteArray &data)
void dataSent(const QByteArray &data)
void errorOccurred(const QString &errorString)
void rssiUpdated(qint16 rssi)
static BluetoothWorker * create(const BluetoothConfiguration *config, QObject *parent=nullptr)
QString name() const
The link interface defines the interface for all links used to communicate with the ground station ap...
void bytesReceived(LinkInterface *link, const QByteArray &data)
void disconnected()
void communicationError(const QString &title, const QString &error)
void bytesSent(LinkInterface *link, const QByteArray &data)
void connected()