diff options
author | Lubomir I. Ivanov (VMware) <neolit123@gmail.com> | 2017-11-28 17:26:09 +0100 |
---|---|---|
committer | Lubomir I. Ivanov <neolit123@gmail.com> | 2018-02-28 13:37:03 +0000 |
commit | 43eebc049aef3b858dd1d38ebe4bf9b395891722 (patch) | |
tree | 858ba6dc45e366192d90ecae0417551d75f2d0b5 | |
parent | ec54c731ad96df1b024fa4467a1f000672e5b7a6 (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.cpp | 126 | ||||
-rw-r--r-- | src/bluetooth/qlowenergycontroller_win_p.h | 43 |
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; }; |