Dokit
Internal development documentation
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
pokitdevice.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2022-2025 Paul Colby <git@colby.id.au>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4/*!
5 * \file
6 * Defines the PokitDevice and PokitDevicePrivate classes.
7 */
8
10
14#include <qtpokit/dsoservice.h>
17
18#include "pokitdevice_p.h"
19#include "../stringliterals_p.h"
20
21#include <QMutexLocker>
22
25
26/*!
27 * \class PokitDevice
28 *
29 * The PokitDevice class simplifies Pokit device access.
30 *
31 * It does this by wrapping QLowEnergyController to provide:
32 * * convenient Pokit service factory methods (dataLogger(), deviceInformation(), dso(),
33 multimeter() and status()); and
34 * * consistent debug logging of QLowEnergyController events.
35 *
36 * But this class is entirely optional, in that all features of all other QtPokit classes can be
37 * used without this class. It's just a (meaningful) convenience.
38 */
39
40/*!
41 * Constructs a new Pokit device controller wrapper for \a deviceInfo, with \a parent.
42 *
43 * Though not strictly necessary, \a deviceInfo should normally come from a
44 * PokitDiscoveryAgent instance (or a QBluetoothDeviceDiscoveryAgent), otherwise connection
45 * is likely to fail with QLowEnergyController::UnknownRemoteDeviceError.
46 */
49{
50 Q_D(PokitDevice);
51 d->setController(QLowEnergyController::createCentral(deviceInfo, this));
52}
53
54/*!
55 * Constructs a new Pokit device controller wrapper for \a controller, with \a parent.
56 */
63
64/*!
65 * \cond internal
66 * Constructs a new Pokit device controller wrapper with \a parent, and private implementation \a d.
67 *
68 * Derived classes using this constructor should use PokitDevicePrivate::setController to assign
69 * the BLE controller as some point.
70 */
76/// \endcond
77
78/*!
79 * Destroys this PokitDevice object.
80 */
82{
83 delete d_ptr;
84}
85
86/*!
87 * Returns a non-const pointer to the controller used to access the Pokit device.
88 */
90{
91 Q_D(PokitDevice);
92 return d->controller;
93}
94
95/*!
96 * Returns a const pointer to the controller used to access the Pokit device.
97 */
99{
100 Q_D(const PokitDevice);
101 return d->controller;
102}
103
104/// \cond
105#define QTPOKIT_INTERNAL_GET_SERVICE(typeName, varName) \
106 Q_D(PokitDevice); \
107 const QMutexLocker scopedLock(&d->varName##Mutex);\
108 if (d->varName == nullptr) { \
109 d->varName = new typeName(d->controller); \
110 } \
111 return d->varName \
112/// \endcond
113
114/*!
115 * Returns a pointer to a CalibrationService instance that uses this device's controller for access.
116 *
117 * This is a convenience function, that always returns the same pointer (for this PokitDevice
118 * instance), but the service itself is lazily created (in a threadsafe manner) on the first
119 * invocation of this function.
120 */
122{
123 QTPOKIT_INTERNAL_GET_SERVICE(CalibrationService, calibration);
124}
125
126/*!
127 * Returns a pointer to a DataLoggerService instance that uses this device's controller for access.
128 *
129 * This is a convenience function, that always returns the same pointer (for this PokitDevice
130 * instance), but the service itself is lazily created (in a threadsafe manner) on the first
131 * invocation of this function.
132 */
134{
135 QTPOKIT_INTERNAL_GET_SERVICE(DataLoggerService, dataLogger);
136}
137
138/*!
139 * Returns a pointer to DeviceInformationService instance that uses this device's controller for
140 * access.
141 *
142 * This is a convenience function, that always returns the same pointer (for this PokitDevice
143 * instance), but the service itself is lazily created (in a threadsafe manner) on the first
144 * invocation of this function.
145 */
147{
148 QTPOKIT_INTERNAL_GET_SERVICE(DeviceInfoService, deviceInfo);
149}
150
151/*!
152 * Returns a pointer to DsoService instance that uses this device's controller for access.
153 *
154 * This is a convenience function, that always returns the same pointer (for this PokitDevice
155 * instance), but the service itself is lazily created (in a threadsafe manner) on the first
156 * invocation of this function.
157 */
159{
160 QTPOKIT_INTERNAL_GET_SERVICE(DsoService, dso);
161}
162
163/*!
164 * Returns a pointer to MultimeterService instance that uses this device's controller for access.
165 *
166 * This is a convenience function, that always returns the same pointer (for this PokitDevice
167 * instance), but the service itself is lazily created (in a threadsafe manner) on the first
168 * invocation of this function.
169 */
171{
172 QTPOKIT_INTERNAL_GET_SERVICE(MultimeterService, multimeter);
173}
174
175/*!
176 * Returns a pointer to StatusService instance that uses this device's controller for access.
177 *
178 * This is a convenience function, that always returns the same pointer (for this PokitDevice
179 * instance), but the service itself is lazily created (in a threadsafe manner) on the first
180 * invocation of this function.
181 */
183{
184 QTPOKIT_INTERNAL_GET_SERVICE(StatusService, status);
185}
186#undef QTPOKIT_INTERNAL_GET_SERVICE
187
188/*!
189 * Returns a human-readable name for the \a uuid service, or a null QString if unknown.
190 *
191 * This is equivalent to QBluetoothUuid::serviceClassToString() but for services provided by Pokit
192 * devices.
193 */
195{
196 static const QHash<QBluetoothUuid, QString> hash{
197 { CalibrationService::serviceUuid, tr("Calibration") },
198 { DataLoggerService::serviceUuid, tr("Data Logger") },
199 { DsoService::serviceUuid, tr("DSO") },
200 { MultimeterService::serviceUuid, tr("Multimeter") },
201 { StatusService::ServiceUuids::pokitMeter, tr("Status (Pokit Meter)") },
202 { StatusService::ServiceUuids::pokitPro, tr("Status (Pokit Pro)") },
204 QBluetoothUuid::serviceClassToString(QBluetoothUuid::ServiceClassUuid::DeviceInformation) },
205
206 // The following are not specifically supported by this library, but strings provided for nicer debug output.
207 { QBluetoothUuid::ServiceClassUuid::GenericAccess,
208 QBluetoothUuid::serviceClassToString(QBluetoothUuid::ServiceClassUuid::GenericAccess) },
209 { QBluetoothUuid::ServiceClassUuid::GenericAttribute,
210 QBluetoothUuid::serviceClassToString(QBluetoothUuid::ServiceClassUuid::GenericAttribute) },
211 { QBluetoothUuid(u"1d14d6ee-fd63-4fa1-bfa4-8f47b42119f0"_s), tr("OTA Firmware Update") },
212 };
213 return hash.value(uuid);
214}
215
216/*!
217 * Returns a human-readable name for the \a uuid characteristic, or a null QString if unknown.
218 *
219 * This is equivalent to QBluetoothUuid::characteristicToString() but for characteristics provided
220 * by Pokit devices.
221 */
223{
224 static const QHash<QBluetoothUuid, QString> hash{
228
232
236
239
246
248 QBluetoothUuid::characteristicToString(QBluetoothUuid::CharacteristicType::FirmwareRevisionString) },
250 QBluetoothUuid::characteristicToString(QBluetoothUuid::CharacteristicType::HardwareRevisionString) },
252 QBluetoothUuid::characteristicToString(QBluetoothUuid::CharacteristicType::ManufacturerNameString) },
254 QBluetoothUuid::characteristicToString(QBluetoothUuid::CharacteristicType::ModelNumberString) },
256 QBluetoothUuid::characteristicToString(QBluetoothUuid::CharacteristicType::SoftwareRevisionString) },
258 QBluetoothUuid::characteristicToString(QBluetoothUuid::CharacteristicType::SerialNumberString) },
259
260 // The next two are not specifically supported by this library, but strings provided for nicer debug output.
261 { QBluetoothUuid(u"f7bf3564-fb6d-4e53-88a4-5e37e0326063"_s), tr("OTA Control") },
262 { QBluetoothUuid(u"984227f3-34fc-4045-a5d0-2c581f81a153"_s), tr("OTA Data Transfer") },
263 };
264 return hash.value(uuid);
265}
266
267/*!
268 * \cond internal
269 * \class PokitDevicePrivate
270 *
271 * The PokitDevicePrivate class provides private implementation for PokitDevice.
272 */
273
274/*!
275 * Constructs a new PokitDevicePrivate object with public implementation \a q.
276 */
281
282/*!
283 * Sets \a newController to be used for accessing Pokit devices.
284 *
285 * If a controller has already been set (and is not the same pointer), then the previous controller
286 * will be disconnected, and replaced with \a newController.
287 *
288 * This function will not take ownership of the new controller. The caller is responsible for
289 * ensuring that \a newContorller remains valid for the lifetime of this instance, or until this
290 * function is used again to replace \a newController with another one (which may be a nullptr).
291 *
292 * \see controller
293 * \see PokitDevice::controller()
294 */
296{
297 if (newController == this->controller) {
298 qCDebug(lc).noquote() << tr("Controller already set to:") << newController;
299 return;
300 }
301
302 if (this->controller) {
303 qCDebug(lc).noquote() << tr("Disconnecting signals from previous controller:")
304 << controller;
305 disconnect(this->controller, nullptr, this, nullptr);
306 }
307
308 qCDebug(lc).noquote() << tr("Setting new controller:") << newController;
309 this->controller = newController;
310 if (!newController) {
311 return; // Don't bother continuing to connect if new controller is null.
312 }
313
314 qCDebug(lc).noquote() << tr(R"(Set new controller "%1" (%2) at (%3).)").arg(
315 controller->remoteName(), controller->remoteDeviceUuid().toString(),
316 controller->remoteAddress().toString());
317
320
323
326
329
330
332 #if (QT_VERSION < QT_VERSION_CHECK(6, 2, 0))
333 QOverload<QLowEnergyController::Error>::of(&QLowEnergyController::error),
334 #else
335 &QLowEnergyController::errorOccurred,
336 #endif
338
339
342
345}
346
347/*!
348 * Handle connected signals.
349 */
351{
352 if (controller == nullptr) {
353 qCCritical(lc).noquote() << tr("PokitDevicePrivate::connected slot invoked without a controller.");
354 return; // Just to avoid the nullptr dereference below.
355 }
356 qCDebug(lc).noquote() << tr(R"(Connected to "%1" (%2) at (%3).)").arg(
357 controller->remoteName(), controller->remoteDeviceUuid().toString(),
358 controller->remoteAddress().toString());
359}
360
361/*!
362 * Handle connectionUpdated signals.
363 */
365{
366 qCDebug(lc).noquote() << tr("Connection updated:") << newParameters.latency()
367 << newParameters.minimumInterval() << newParameters.maximumInterval()
368 << newParameters.supervisionTimeout();
369}
370
371/*!
372 * Handle disconnected signals.
373 */
375{
376 qCDebug(lc).noquote() << tr("Device disconnected.");
377}
378
379/*!
380 * Handle discoveryFinished signals.
381 */
383{
384 qCDebug(lc).noquote() << tr("Service discovery finished.");
385}
386
387/*!
388 * Handle error signals.
389 */
391{
392 qCDebug(lc).noquote() << tr("Controller error:") << newError;
393}
394
395/*!
396 * Handle serviceDiscovered signals.
397 */
399{
400 qCDebug(lc).noquote() << tr(R"(Service discovered: %1 "%2")")
401 .arg(newService.toString(), PokitDevice::serviceToString(newService));
402}
403
404/*!
405 * Handle stateChanged signals.
406 */
408{
409 qCDebug(lc).noquote() << tr("State changed to:") << state;
410}
411
412/// \endcond
413
Declares the CalibrationService class.
The CalibrationService class accesses the Calibrartion service of Pokit devices.
static const QBluetoothUuid serviceUuid
UUID of the Calibration service.
The DataLoggerService class accesses the Data Logger service of Pokit devices.
static const QBluetoothUuid serviceUuid
UUID of the "DataLogger" service.
The DeviceInfoService class accesses the Device Info service of Pokit devices.
static const QBluetoothUuid serviceUuid
UUID of the "Device Info" service.
The DsoService class accesses the DSO (Digital Storage Oscilloscope) service of Pokit devices.
Definition dsoservice.h:24
static const QBluetoothUuid serviceUuid
UUID of the "DSO" service.
Definition dsoservice.h:29
The MultimeterService class accesses the Multimeter service of Pokit devices.
static const QBluetoothUuid serviceUuid
UUID of the Multimeter service.
The PokitDevicePrivate class provides private implementation for PokitDevice.
PokitDevicePrivate(PokitDevice *const q)
Constructs a new PokitDevicePrivate object with public implementation q.
void connected() const
Handle connected signals.
void disconnected() const
Handle disconnected signals.
void errorOccurred(QLowEnergyController::Error newError) const
Handle error signals.
void stateChanged(QLowEnergyController::ControllerState state) const
Handle stateChanged signals.
void setController(QLowEnergyController *newController)
Sets newController to be used for accessing Pokit devices.
void connectionUpdated(const QLowEnergyConnectionParameters &newParameters) const
Handle connectionUpdated signals.
QLowEnergyController * controller
BLE controller for accessing the Pokit device.
void discoveryFinished() const
Handle discoveryFinished signals.
PokitDevice * q_ptr
Internal q-pointer.
void serviceDiscovered(const QBluetoothUuid &newService) const
Handle serviceDiscovered signals.
The PokitDevice class simplifies Pokit device access.
Definition pokitdevice.h:31
static QString charcteristicToString(const QBluetoothUuid &uuid)
Returns a human-readable name for the uuid characteristic, or a null QString if unknown.
DeviceInfoService * deviceInformation()
Returns a pointer to DeviceInformationService instance that uses this device's controller for access.
QLowEnergyController * controller()
Returns a non-const pointer to the controller used to access the Pokit device.
virtual ~PokitDevice()
Destroys this PokitDevice object.
DsoService * dso()
Returns a pointer to DsoService instance that uses this device's controller for access.
PokitDevice(const QBluetoothDeviceInfo &deviceInfo, QObject *parent=nullptr)
Constructs a new Pokit device controller wrapper for deviceInfo, with parent.
PokitDevicePrivate * d_ptr
Internal d-pointer.
Definition pokitdevice.h:58
DataLoggerService * dataLogger()
Returns a pointer to a DataLoggerService instance that uses this device's controller for access.
MultimeterService * multimeter()
Returns a pointer to MultimeterService instance that uses this device's controller for access.
CalibrationService * calibration()
Returns a pointer to a CalibrationService instance that uses this device's controller for access.
static QString serviceToString(const QBluetoothUuid &uuid)
Returns a human-readable name for the uuid service, or a null QString if unknown.
StatusService * status()
Returns a pointer to StatusService instance that uses this device's controller for access.
The StatusService class accesses the Pokit Status service of Pokit devices.
Declares the DataLoggerService class.
Declares the DeviceInfoService class.
Declares the DsoService class.
Declares the MultimeterService class.
Declares the PokitDevice class.
Declares the PokitDevicePrivate class.
QString characteristicToString(QBluetoothUuid::CharacteristicType uuid)
QString serviceClassToString(QBluetoothUuid::ServiceClassUuid uuid)
const T value(const Key &key) const const
double maximumInterval() const const
double minimumInterval() const const
int supervisionTimeout() const const
void connectionUpdated(const QLowEnergyConnectionParameters &newParameters)
QLowEnergyController * createCentral(const QBluetoothDeviceInfo &remoteDevice, QObject *parent)
QLowEnergyController::Error error() const const
void serviceDiscovered(const QBluetoothUuid &newService)
void stateChanged(QLowEnergyController::ControllerState state)
QObject(QObject *parent)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QObject * parent() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
#define QTPOKIT_BEGIN_NAMESPACE
Macro for starting the QtPokit library's top-most namespace (if one is defined).
#define QTPOKIT_END_NAMESPACE
Macro for ending the QtPokit library's top-most namespace (if one is defined).
QString toString() const const
Declares the StatusService class.
Declares the DOKIT_USE_STRINGLITERALS macro, and related functions.
#define DOKIT_USE_STRINGLITERALS
Internal macro for using either official Qt string literals (added in Qt 6.4), or our own equivalent ...
static const QBluetoothUuid temperature
UUID of the Calibration service's Temperature characteristic.
static const QBluetoothUuid metadata
UUID of the DataLogger service's Metadata characteristic.
static const QBluetoothUuid settings
UUID of the DataLogger service's Settings characteristic.
static const QBluetoothUuid reading
UUID of the DataLogger service's Reading characteristic.
static const QBluetoothUuid manufacturerName
UUID of the Device Info service's Manufacturer Name String characteristic.
static const QBluetoothUuid hardwareRevision
UUID of the Device Info service's Hardware Revision String characteristic.
static const QBluetoothUuid softwareRevision
UUID of the Device Info service's Software Revision String characteristic.
static const QBluetoothUuid serialNumber
UUID of the Device Info service's Serial Number String characteristic.
static const QBluetoothUuid firmwareRevision
UUID of the Device Info service's Firmware Revision String characteristic.
static const QBluetoothUuid modelNumber
UUID of the Device Info service's Model Number String characteristic.
static const QBluetoothUuid metadata
UUID of the DSO service's Metadata characteristic.
Definition dsoservice.h:37
static const QBluetoothUuid reading
UUID of the DSO service's Reading characteristic.
Definition dsoservice.h:40
static const QBluetoothUuid settings
UUID of the DSO service's Settings characteristic.
Definition dsoservice.h:34
static const QBluetoothUuid reading
UUID of the Multimeter service's Reading characteristic.
static const QBluetoothUuid settings
UUID of the Multimeter service's Settings characteristic.
static const QBluetoothUuid name
UUID of the Pokit Status service's Device Name characteristic.
static const QBluetoothUuid torch
UUID of the Pokit Status service's (undocumented) Torch characteristic.
static const QBluetoothUuid deviceCharacteristics
UUID of the Pokit Status service's Device Characteristics characteristic.
static const QBluetoothUuid buttonPress
UUID of the Pokit Status service's (undocumented) Button Press characteristic.
static const QBluetoothUuid flashLed
UUID of the Pokit Status service's Flash LED characteristic.
static const QBluetoothUuid status
UUID of the Pokit Status service's Status characteristic.
static const QBluetoothUuid pokitPro
UUID of the Pokit Pro's Pokit Status service.
static const QBluetoothUuid pokitMeter
UUID of the Pokit Meter's Pokit Status service.