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