QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
BluetoothClassicWorker.cc
Go to the documentation of this file.
2
4 : BluetoothWorker(config, parent)
5{
6}
7
9{
10 _setupClassicSocket();
11}
12
14{
15 qCDebug(BluetoothLinkLog) << "Attempting to connect to" << _device.name() << "Mode: Classic";
16
17 // Destroy stale socket — QBluetoothSocket must be recreated after errors
18 if (_socket) {
19 _socket->disconnect();
20 _socket->deleteLater();
21 _socket = nullptr;
22 }
23
24 _setupClassicSocket();
25
26 if (!_socket) {
27 emit errorOccurred(tr("Socket not available"));
28 return;
29 }
30
31 _socket->connectToService(_device.address(), SPP_UUID);
32}
33
35{
36 if (_classicDiscovery && _classicDiscovery->isActive()) {
37 _classicDiscovery->stop();
38 }
39
40 if (_socket) {
41 _socket->disconnectFromService();
42 }
43}
44
45void BluetoothClassicWorker::onWriteData(const QByteArray &data)
46{
47 _writeClassicData(data);
48}
49
51{
52 if (_classicDiscovery && _classicDiscovery->isActive()) {
53 _classicDiscovery->stop();
54 }
55}
56
58{
59 // Classic Bluetooth has no special reset — just let reconnect proceed
60}
61
63{
66
67 if (_socket) {
68 _socket->disconnect();
69 if (_socket->state() == QBluetoothSocket::SocketState::ConnectedState) {
70 _socket->disconnectFromService();
71 }
72 _socket->deleteLater();
73 }
74}
75
76void BluetoothClassicWorker::_setupClassicSocket()
77{
78 if (_socket) {
79 return;
80 }
81
82 _socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this);
83
84 (void) connect(_socket.data(), &QBluetoothSocket::connected, this, &BluetoothClassicWorker::_onSocketConnected);
85 (void) connect(_socket.data(), &QBluetoothSocket::disconnected, this, &BluetoothClassicWorker::_onSocketDisconnected);
86 (void) connect(_socket.data(), &QBluetoothSocket::readyRead, this, &BluetoothClassicWorker::_onSocketReadyRead);
87 (void) connect(_socket.data(), &QBluetoothSocket::errorOccurred, this, &BluetoothClassicWorker::_onSocketErrorOccurred);
88
89 if (BluetoothLinkLog().isDebugEnabled()) {
90 (void) connect(_socket.data(), &QBluetoothSocket::bytesWritten, this, &BluetoothClassicWorker::_onSocketBytesWritten);
91
92 (void) connect(_socket.data(), &QBluetoothSocket::stateChanged, this,
93 [](QBluetoothSocket::SocketState state) {
94 qCDebug(BluetoothLinkLog) << "Bluetooth Socket State Changed:" << state;
95 });
96 }
97}
98
99void BluetoothClassicWorker::_writeClassicData(const QByteArray &data)
100{
101 if (!_socket || !_socket->isWritable()) {
102 emit errorOccurred(tr("Socket is not writable"));
103 return;
104 }
105
106 qint64 totalBytesWritten = 0;
107 while (totalBytesWritten < data.size()) {
108 const qint64 bytesWritten = _socket->write(data.constData() + totalBytesWritten,
109 data.size() - totalBytesWritten);
110 if (bytesWritten == -1) {
111 emit errorOccurred(tr("Write failed: %1").arg(_socket->errorString()));
112 return;
113 } else if (bytesWritten == 0) {
114 emit errorOccurred(tr("Write returned 0 bytes"));
115 return;
116 }
117 totalBytesWritten += bytesWritten;
118 }
119
120 emit dataSent(data.first(totalBytesWritten));
121}
122
123void BluetoothClassicWorker::_onSocketConnected()
124{
125 qCDebug(BluetoothLinkLog) << "=== Classic Bluetooth Connection Established ===" << _device.name();
126 qCDebug(BluetoothLinkLog) << " Address:" << _device.address().toString();
127 qCDebug(BluetoothLinkLog) << " Protocol: RFCOMM (SPP)";
128
129 _connected = true;
132 emit connected();
133}
134
135void BluetoothClassicWorker::_onSocketDisconnected()
136{
137 qCDebug(BluetoothLinkLog) << "Socket disconnected from device:" << _device.name();
138 _connected = false;
139 emit disconnected();
140
141 if (!_intentionalDisconnect.load() && _socket && _reconnectTimer) {
142 qCDebug(BluetoothLinkLog) << "Starting reconnect timer";
143 _reconnectTimer->start();
144 }
145}
146
147void BluetoothClassicWorker::_onSocketReadyRead()
148{
149 if (!_socket) {
150 return;
151 }
152
153 const QByteArray data = _socket->readAll();
154 if (!data.isEmpty()) {
155 qCDebug(BluetoothLinkVerboseLog) << _device.name() << "Received" << data.size() << "bytes";
156 emit dataReceived(data);
157 }
158}
159
160void BluetoothClassicWorker::_onSocketBytesWritten(qint64 bytes)
161{
162 qCDebug(BluetoothLinkVerboseLog) << _device.name() << "Wrote" << bytes << "bytes";
163}
164
165void BluetoothClassicWorker::_onSocketErrorOccurred(QBluetoothSocket::SocketError socketError)
166{
167 QString errorString;
168 if (_socket) {
169 errorString = _socket->errorString();
170 } else {
171 errorString = tr("Socket error: Null Socket");
172 }
173
174 qCWarning(BluetoothLinkLog) << "Socket error:" << socketError << errorString;
176
178
179 // If we never successfully connected, emit disconnected to reset UI state
180 if (!_connected.load()) {
181 qCDebug(BluetoothLinkLog) << "Connection attempt failed, emitting disconnected signal";
182 emit disconnected();
183 }
184
185 // Attempt service discovery for service-related errors only
186 if ((socketError == QBluetoothSocket::SocketError::ServiceNotFoundError) ||
187 (socketError == QBluetoothSocket::SocketError::UnsupportedProtocolError)) {
188 qCDebug(BluetoothLinkLog) << "Service-related error, attempting fallback discovery";
189 _startClassicServiceDiscovery();
190 }
191}
192
193void BluetoothClassicWorker::_startClassicServiceDiscovery()
194{
195 if (_classicDiscovery && _classicDiscovery->isActive()) {
196 return;
197 }
198
199 if (!_classicDiscovery) {
200 _classicDiscovery = new QBluetoothServiceDiscoveryAgent(_device.address(), this);
201 (void) connect(_classicDiscovery.data(), &QBluetoothServiceDiscoveryAgent::serviceDiscovered,
202 this, &BluetoothClassicWorker::_onClassicServiceDiscovered);
203 (void) connect(_classicDiscovery.data(), &QBluetoothServiceDiscoveryAgent::finished,
204 this, &BluetoothClassicWorker::_onClassicServiceDiscoveryFinished);
205 (void) connect(_classicDiscovery.data(), &QBluetoothServiceDiscoveryAgent::canceled,
206 this, &BluetoothClassicWorker::_onClassicServiceDiscoveryCanceled);
207 (void) connect(_classicDiscovery.data(), &QBluetoothServiceDiscoveryAgent::errorOccurred,
208 this, &BluetoothClassicWorker::_onClassicServiceDiscoveryError);
209 }
210
211 qCDebug(BluetoothLinkLog) << "Starting classic service discovery on" << _device.name();
212 _classicDiscoveredService = QBluetoothServiceInfo();
213 _classicDiscovery->start(QBluetoothServiceDiscoveryAgent::FullDiscovery);
214
217 _serviceDiscoveryTimer->start();
218 }
219}
220
221void BluetoothClassicWorker::_onClassicServiceDiscovered(const QBluetoothServiceInfo &serviceInfo)
222{
223 qCDebug(BluetoothLinkLog) << "Classic service discovered: UUIDs=" << serviceInfo.serviceClassUuids()
224 << "RFCOMM channel=" << serviceInfo.serverChannel()
225 << "L2CAP PSM=" << serviceInfo.protocolServiceMultiplexer();
226
227 const QList<QBluetoothUuid> serviceUuids = serviceInfo.serviceClassUuids();
228 const bool isSerial = serviceUuids.contains(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::SerialPort));
229 const bool hasRfcommChannel = serviceInfo.serverChannel() > 0;
230
231 if (isSerial || (hasRfcommChannel && !_classicDiscoveredService.isValid())) {
232 _classicDiscoveredService = serviceInfo;
233 }
234}
235
236void BluetoothClassicWorker::_onClassicServiceDiscoveryFinished()
237{
240 }
241
242 if (!_classicDiscoveredService.isValid()) {
243 qCWarning(BluetoothLinkLog) << "No suitable classic service found";
244 return;
245 }
246
247 if (!_socket) {
248 _setupClassicSocket();
249 }
250
251 qCDebug(BluetoothLinkLog) << "Connecting using discovered service";
252 _socket->connectToService(_classicDiscoveredService);
253}
254
255void BluetoothClassicWorker::_onClassicServiceDiscoveryCanceled()
256{
259 }
260
261 qCDebug(BluetoothLinkLog) << "Classic service discovery canceled";
262}
263
264void BluetoothClassicWorker::_onClassicServiceDiscoveryError(QBluetoothServiceDiscoveryAgent::Error error)
265{
268 }
269
270 const QString e = _classicDiscovery ? _classicDiscovery->errorString() : tr("Service discovery error: %1").arg(error);
271 qCWarning(BluetoothLinkLog) << e;
272 emit errorOccurred(e);
273}
QString errorString
Error error
void onWriteData(const QByteArray &data) override
BluetoothClassicWorker(const BluetoothConfiguration *config, QObject *parent=nullptr)
void onResetAfterConsecutiveFailures() override
void onServiceDiscoveryTimeout() override
void dataReceived(const QByteArray &data)
QPointer< QTimer > _serviceDiscoveryTimer
void dataSent(const QByteArray &data)
void errorOccurred(const QString &errorString)
QPointer< QTimer > _reconnectTimer
std::atomic< int > _reconnectAttempts
std::atomic< bool > _connected
const QBluetoothDeviceInfo _device
std::atomic< bool > _intentionalDisconnect