summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLubomir I. Ivanov (VMware) <neolit123@gmail.com>2017-11-28 17:26:09 +0100
committerLubomir I. Ivanov <neolit123@gmail.com>2018-02-28 13:37:03 +0000
commit43eebc049aef3b858dd1d38ebe4bf9b395891722 (patch)
tree858ba6dc45e366192d90ecae0417551d75f2d0b5
parentec54c731ad96df1b024fa4467a1f000672e5b7a6 (diff)
Add a thread for QLowEnergyControllerPrivateWin32
Create a re-usable thread ('thread') and worker ('treadWorker') in QLowEnergyControllerPrivateWin32 that have the same lifespan as the controller object. Add "job" support (ThreadWorkerJob) for the thread so that jobs are scheduled and executed sequentially. Each job should have a data struct. For writing that is WriteCharData. Handle writing of characteristics in: QLowEnergyControllerPrivateWin32::writeCharacteristic() QLowEnergyControllerPrivateWin32::jobFinished() ThreadWorker::runPendingJob() The above jobFinished() and runPendingJob() use a `switch` to determine the ThreadWorkerJob type. Change-Id: I3331e0d4adc29565a88fd792f9a54833881ea694 Reviewed-by: Denis Shienkov <denis.shienkov@gmail.com> Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
-rw-r--r--src/bluetooth/qlowenergycontroller_win.cpp126
-rw-r--r--src/bluetooth/qlowenergycontroller_win_p.h43
2 files changed, 140 insertions, 29 deletions
diff --git a/src/bluetooth/qlowenergycontroller_win.cpp b/src/bluetooth/qlowenergycontroller_win.cpp
index f7a1611f..27bfb7c7 100644
--- a/src/bluetooth/qlowenergycontroller_win.cpp
+++ b/src/bluetooth/qlowenergycontroller_win.cpp
@@ -45,11 +45,10 @@
#include <QtCore/QIODevice> // for open modes
#include <QtCore/QEvent>
#include <QtCore/QMutex>
+#include <QtCore/QThread>
#include <algorithm> // for std::max
-#include <windows/qwinlowenergybluetooth_p.h>
-
#include <setupapi.h>
QT_BEGIN_NAMESPACE
@@ -690,12 +689,22 @@ QLowEnergyControllerPrivateWin32::QLowEnergyControllerPrivateWin32()
qCWarning(QT_BT_WINDOWS) << "LE is not supported on this OS";
return;
}
+
+ thread = new QThread;
+ threadWorker = new ThreadWorker;
+ threadWorker->moveToThread(thread);
+ connect(threadWorker, &ThreadWorker::jobFinished, this, &QLowEnergyControllerPrivateWin32::jobFinished);
+ connect(thread, &QThread::finished, threadWorker, &ThreadWorker::deleteLater);
+ connect(thread, &QThread::finished, thread, &QThread::deleteLater);
+ thread->start();
}
QLowEnergyControllerPrivateWin32::~QLowEnergyControllerPrivateWin32()
{
QMutexLocker locker(&controllersGuard);
qControllers()->removeAll(this);
+ if (thread)
+ thread->quit();
}
void QLowEnergyControllerPrivateWin32::init()
@@ -1020,17 +1029,20 @@ void QLowEnergyControllerPrivateWin32::writeCharacteristic(
QLowEnergyService::WriteMode mode)
{
Q_ASSERT(!service.isNull());
- if (!service->characteristicList.contains(charHandle))
- return;
- int systemErrorCode = NO_ERROR;
+ if (!service->characteristicList.contains(charHandle)) {
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ return;
+ }
- const HANDLE hService = openSystemService(
- remoteDevice, service->uuid, QIODevice::ReadWrite, &systemErrorCode);
+ WriteCharData data;
+ data.systemErrorCode = NO_ERROR;
+ data.hService = openSystemService(
+ remoteDevice, service->uuid, QIODevice::ReadWrite, &data.systemErrorCode);
- if (systemErrorCode != NO_ERROR) {
+ if (data.systemErrorCode != NO_ERROR) {
qCWarning(QT_BT_WINDOWS) << "Unable to open service" << service->uuid.toString()
- << ":" << qt_error_string(systemErrorCode);
+ << ":" << qt_error_string(data.systemErrorCode);
service->setError(QLowEnergyService::CharacteristicWriteError);
return;
}
@@ -1038,35 +1050,58 @@ void QLowEnergyControllerPrivateWin32::writeCharacteristic(
const QLowEnergyServicePrivate::CharData &charDetails
= service->characteristicList[charHandle];
- BTH_LE_GATT_CHARACTERISTIC gattCharacteristic = recoverNativeLeGattCharacteristic(
+ data.gattCharacteristic = recoverNativeLeGattCharacteristic(
service->startHandle, charHandle, charDetails);
- const DWORD flags = (mode == QLowEnergyService::WriteWithResponse)
+ data.flags = (mode == QLowEnergyService::WriteWithResponse)
? BLUETOOTH_GATT_FLAG_NONE
: BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE;
- // TODO: If a device is not connected, this function will block
- // for some time. So, need to re-implement of writeCharacteristic()
- // with use QFutureWatcher.
- setGattCharacteristicValue(hService, &gattCharacteristic,
- newValue, flags, &systemErrorCode);
- closeSystemDevice(hService);
+ ThreadWorkerJob job;
+ job.operation = ThreadWorkerJob::WriteChar;
+ data.newValue = newValue;
+ data.mode = mode;
+ job.data = QVariant::fromValue(data);
- if (systemErrorCode != NO_ERROR) {
- qCWarning(QT_BT_WINDOWS) << "Unable to set value for characteristic"
- << charDetails.uuid.toString()
- << "of the service" << service->uuid.toString()
- << ":" << qt_error_string(systemErrorCode);
- service->setError(QLowEnergyService::CharacteristicWriteError);
- return;
- }
+ QMetaObject::invokeMethod(threadWorker, "putJob", Qt::QueuedConnection,
+ Q_ARG(ThreadWorkerJob, job));
+}
- updateValueOfCharacteristic(charHandle, newValue, false);
+void QLowEnergyControllerPrivateWin32::jobFinished(const ThreadWorkerJob &job)
+{
+ switch (job.operation) {
+ case ThreadWorkerJob::WriteChar:
+ {
+ const WriteCharData data = job.data.value<WriteCharData>();
+ closeSystemDevice(data.hService);
+ const QLowEnergyHandle charHandle = static_cast<QLowEnergyHandle>(data.gattCharacteristic.AttributeHandle);
+ const QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+
+ if (data.systemErrorCode != NO_ERROR) {
+ const QLowEnergyServicePrivate::CharData &charDetails = service->characteristicList[charHandle];
+ qCWarning(QT_BT_WINDOWS) << "Unable to set value for characteristic"
+ << charDetails.uuid.toString()
+ << "of the service" << service->uuid.toString()
+ << ":" << qt_error_string(data.systemErrorCode);
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ return;
+ }
+
+ updateValueOfCharacteristic(charHandle, data.newValue, false);
- if (mode == QLowEnergyService::WriteWithResponse) {
- const QLowEnergyCharacteristic ch(service, charHandle);
- emit service->characteristicWritten(ch, newValue);
+ if (data.mode == QLowEnergyService::WriteWithResponse) {
+ const QLowEnergyCharacteristic ch = characteristicForHandle(charHandle);
+ emit service->characteristicWritten(ch, data.newValue);
+ }
+ }
+ break;
+ case ThreadWorkerJob::ReadChar:
+ case ThreadWorkerJob::WriteDescr:
+ case ThreadWorkerJob::ReadDescr:
+ break;
}
+
+ QMetaObject::invokeMethod(threadWorker, "runPendingJob", Qt::QueuedConnection);
}
void QLowEnergyControllerPrivateWin32::readDescriptor(
@@ -1219,4 +1254,37 @@ void QLowEnergyControllerPrivateWin32::addToGenericAttributeList(const QLowEnerg
Q_UNIMPLEMENTED();
}
+void ThreadWorker::putJob(const ThreadWorkerJob &job)
+{
+ m_jobs.append(job);
+ if (m_jobs.count() == 1)
+ runPendingJob();
+}
+
+void ThreadWorker::runPendingJob()
+{
+ if (!m_jobs.count())
+ return;
+
+ ThreadWorkerJob job = m_jobs.first();
+
+ switch (job.operation) {
+ case ThreadWorkerJob::WriteChar:
+ {
+ WriteCharData data = job.data.value<WriteCharData>();
+ setGattCharacteristicValue(data.hService, &data.gattCharacteristic,
+ data.newValue, data.flags, &data.systemErrorCode);
+ job.data = QVariant::fromValue(data);
+ }
+ break;
+ case ThreadWorkerJob::ReadChar:
+ case ThreadWorkerJob::WriteDescr:
+ case ThreadWorkerJob::ReadDescr:
+ break;
+ }
+
+ m_jobs.removeFirst();
+ emit jobFinished(job);
+}
+
QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller_win_p.h b/src/bluetooth/qlowenergycontroller_win_p.h
index 9c2d4f92..64f824e6 100644
--- a/src/bluetooth/qlowenergycontroller_win_p.h
+++ b/src/bluetooth/qlowenergycontroller_win_p.h
@@ -59,8 +59,47 @@
#include "qlowenergycontroller.h"
#include "qlowenergycontrollerbase_p.h"
+#include <windows/qwinlowenergybluetooth_p.h>
+
QT_BEGIN_NAMESPACE
+class QThread;
+class QLowEnergyControllerPrivateWin32;
+
+class ThreadWorkerJob
+{
+public:
+ enum Operation { WriteChar, ReadChar, WriteDescr, ReadDescr };
+ Operation operation;
+ QVariant data;
+};
+
+Q_DECLARE_METATYPE(ThreadWorkerJob)
+
+struct WriteCharData
+{
+ QByteArray newValue;
+ QLowEnergyService::WriteMode mode;
+ HANDLE hService;
+ DWORD flags;
+ BTH_LE_GATT_CHARACTERISTIC gattCharacteristic;
+ int systemErrorCode;
+};
+
+Q_DECLARE_METATYPE(WriteCharData)
+
+class ThreadWorker : public QObject
+{
+ Q_OBJECT
+public:
+ Q_INVOKABLE void putJob(const ThreadWorkerJob &job);
+ Q_INVOKABLE void runPendingJob();
+signals:
+ void jobFinished(const ThreadWorkerJob &job);
+private:
+ QVector<ThreadWorkerJob> m_jobs;
+};
+
class QLowEnergyServiceData;
extern void registerQLowEnergyControllerMetaType();
@@ -105,9 +144,13 @@ public:
void addToGenericAttributeList(const QLowEnergyServiceData &service,
QLowEnergyHandle startHandle) override;
+public slots:
+ void jobFinished(const ThreadWorkerJob &job);
protected:
void customEvent(QEvent *e);
private:
+ QThread *thread = nullptr;
+ ThreadWorker *threadWorker = nullptr;
QString deviceSystemPath;
};