LCOV - code coverage report
Current view: top level - src/lib - abstractpokitservice.cpp (source / functions) Coverage Total Hit
Project: Dokit Lines: 62.4 % 298 186
Version: Functions: 78.1 % 32 25

            Line data    Source code
       1              : // SPDX-FileCopyrightText: 2022-2024 Paul Colby <git@colby.id.au>
       2              : // SPDX-License-Identifier: LGPL-3.0-or-later
       3              : 
       4              : /*!
       5              :  * \file
       6              :  * Defines the AbstractPokitService and AbstractPokitServicePrivate classes.
       7              :  */
       8              : 
       9              : #include <qtpokit/abstractpokitservice.h>
      10              : #include "abstractpokitservice_p.h"
      11              : #include "pokitproducts_p.h"
      12              : 
      13              : #include <qtpokit/pokitdevice.h>
      14              : 
      15              : #include <QLowEnergyController>
      16              : 
      17              : /*!
      18              :  * \class AbstractPokitService
      19              :  *
      20              :  * The AbstractPokitService class provides a common base for Pokit services classes.
      21              :  */
      22              : 
      23              : /*!
      24              :  * \cond internal
      25              :  * Constructs a new Pokit service with \a parent, and private implementation \a d.
      26              :  */
      27        14684 : AbstractPokitService::AbstractPokitService(
      28        14684 :     AbstractPokitServicePrivate * const d, QObject * const parent)
      29        20880 :     : QObject(parent), d_ptr(d)
      30        10228 : {
      31              : 
      32        24912 : }
      33              : /// \endcond
      34              : 
      35              : /*!
      36              :  * Destroys this AbstractPokitService object.
      37              :  */
      38         5548 : AbstractPokitService::~AbstractPokitService()
      39         6200 : {
      40        11748 :     delete d_ptr;
      41        11748 : }
      42              : 
      43              : /*!
      44              :  * \fn virtual bool AbstractPokitService::readCharacteristics() = 0
      45              :  *
      46              :  * Read all characteristics.
      47              :  *
      48              :  * This convenience function will queue refresh requests of all characteristics supported by this
      49              :  * service.
      50              :  *
      51              :  * Relevant `*Service::*Read` signals will be emitted by derived class objects as each
      52              :  * characteristic is successfully read.
      53              :  */
      54              : 
      55              : /*!
      56              :  * Returns `true` if autodiscovery of services and service details is enabled, `false` otherwise.
      57              :  *
      58              :  * \see setAutoDiscover for more information on what autodiscovery provides.
      59              :  */
      60          114 : bool AbstractPokitService::autoDiscover() const
      61          132 : {
      62          132 :     Q_D(const AbstractPokitService);
      63          246 :     return d->autoDiscover;
      64          132 : }
      65              : 
      66              : /*!
      67              :  * If \a discover is \c true, autodiscovery will be attempted.
      68              :  *
      69              :  * Specifically, this may resulting in automatic invocation of:
      70              :  * * QLowEnergyController::discoverServices if/when the internal controller is connected; and
      71              :  * * QLowEnergyService::discoverDetails if/when an internal service object is created.
      72              :  *
      73              :  * \see autoDiscover
      74              :  */
      75           76 : void AbstractPokitService::setAutoDiscover(const bool discover)
      76           88 : {
      77           88 :     Q_D(AbstractPokitService);
      78          164 :     d->autoDiscover = discover;
      79          164 : }
      80              : 
      81              : /*!
      82              :  * Returns the Pokit product this service is attached to.
      83              :  *
      84              :  * \see setPokitProduct
      85              :  */
      86        10830 : std::optional<PokitProduct> AbstractPokitService::pokitProduct() const
      87         5904 : {
      88         5904 :     Q_D(const AbstractPokitService);
      89        16734 :     return d->pokitProduct;
      90         5904 : }
      91              : 
      92              : /*!
      93              :  * Sets the current Pokit \a product.
      94              :  *
      95              :  * This must be called to set the product before this object's BLE controller's services are discovered. If
      96              :  * autoDiscover() is enabled, then this should be done before the controller's \c connectToDevice() is called.
      97              :  *
      98              :  * For example:
      99              :  * ```
     100              :  * Q_ASSERT(isPokitProduct(deviceInfo));
     101              :  * auto controller = QLowEnergyController::createCentral(deviceInfo);
     102              :  * auto service = new DsoService(controller);
     103              :  * service->setPokitProduct(pokitProduct(deviceInfo));
     104              :  * controller->connectToDevice();
     105              :  * ```
     106              :  *
     107              :  * \see autoDiscover
     108              :  * \see pokitProduct
     109              :  */
     110         9462 : void AbstractPokitService::setPokitProduct(const PokitProduct product)
     111         5216 : {
     112         5216 :     Q_D(AbstractPokitService);
     113        14678 :     d->pokitProduct = product;
     114        14678 : }
     115              : 
     116              : /*!
     117              :  * Returns a non-const pointer to the internal service object, if any.
     118              :  */
     119          342 : QLowEnergyService * AbstractPokitService::service()
     120          256 : {
     121          256 :     Q_D(AbstractPokitService);
     122          598 :     return d->service;
     123          256 : }
     124              : 
     125              : /*!
     126              :  * Returns a const pointer to the internal service object, if any.
     127              :  */
     128           38 : const QLowEnergyService * AbstractPokitService::service() const
     129           44 : {
     130           44 :     Q_D(const AbstractPokitService);
     131           82 :     return d->service;
     132           44 : }
     133              : 
     134              : /*!
     135              :  * \fn void AbstractPokitService::serviceDetailsDiscovered()
     136              :  *
     137              :  * This signal is emitted when the Pokit service details have been discovered.
     138              :  *
     139              :  * Once this signal has been emitted, cached characteristics values should be immediately available
     140              :  * via derived classes' accessor functions, and refreshes can be queued via readCharacteristics()
     141              :  * and any related read functions provided by derived classes.
     142              :  */
     143              : 
     144              : /*!
     145              :  * \fn void AbstractPokitService::serviceErrorOccurred(QLowEnergyService::ServiceError newError)
     146              :  *
     147              :  *  This signal is emitted whenever an error occurs on the underlying QLowEnergyService.
     148              :  */
     149              : 
     150              : /*!
     151              :  * \cond internal
     152              :  * \class AbstractPokitServicePrivate
     153              :  *
     154              :  * The AbstractPokitServicePrivate class provides private implementation for AbstractPokitService.
     155              :  */
     156              : 
     157              : /*!
     158              :  * \internal
     159              :  * Constructs a new AbstractPokitServicePrivate object with public implementation \a q.
     160              :  *
     161              :  * Note, typically the \a serviceUuid should be set validly, however, in the rare case that a
     162              :  * service's UUID can vary (ie the Status Service), \a serviceUuid may be set to a `null`
     163              :  * QBluetoothUuid here, and updated when the correct service UUID is known.
     164              :  *
     165              :  * \see StatusService::ServiceUuids
     166              :  * \see StatusServicePrivate::serviceDiscovered
     167              :  */
     168        14684 : AbstractPokitServicePrivate::AbstractPokitServicePrivate(const QBluetoothUuid &serviceUuid,
     169        14684 :     QLowEnergyController * controller, AbstractPokitService * const q)
     170        20880 :     : controller(controller), serviceUuid(serviceUuid), q_ptr(q)
     171        10228 : {
     172        24912 :     if (controller) {
     173        12888 :         connect(controller, &QLowEnergyController::connected,
     174         5286 :                 this, &AbstractPokitServicePrivate::connected);
     175              : 
     176        12888 :         connect(controller, &QLowEnergyController::discoveryFinished,
     177         5286 :                 this, &AbstractPokitServicePrivate::discoveryFinished);
     178              : 
     179        12888 :         connect(controller, &QLowEnergyController::serviceDiscovered,
     180         5286 :                 this, &AbstractPokitServicePrivate::serviceDiscovered);
     181              : 
     182        12888 :         createServiceObject();
     183         3828 :     }
     184        24912 : }
     185              : 
     186              : /*!
     187              :  * Creates an internal service object from the internal controller.
     188              :  *
     189              :  * Any existing service object will *not* be replaced.
     190              :  *
     191              :  * Returns \c true if a service was created successfully, either now, or sometime previously.
     192              :  */
     193         9364 : bool AbstractPokitServicePrivate::createServiceObject()
     194         4040 : {
     195        13404 :     if (!controller) {
     196          148 :         return false;
     197          148 :     }
     198              : 
     199        13104 :     if (service) {
     200           62 :         qCDebug(lc).noquote() << tr("Already have service object:") << service;
     201           46 :         return true;
     202           16 :     }
     203              : 
     204        13050 :     if (serviceUuid.isNull()) {
     205          620 :         qCDebug(lc).noquote() << tr("Service UUID not assigned yet.");
     206          460 :         return false;
     207          160 :     }
     208              : 
     209        12510 :     service = controller->createServiceObject(serviceUuid, this);
     210        12510 :     if (!service) {
     211         3716 :         return false;
     212         3716 :     }
     213            0 :     qCDebug(lc).noquote() << tr("Service object created for %1 device:").arg(toString(*this->pokitProduct)) << service;
     214              : 
     215            0 :     connect(service, &QLowEnergyService::stateChanged,
     216            0 :             this, &AbstractPokitServicePrivate::stateChanged);
     217            0 :     connect(service, &QLowEnergyService::characteristicRead,
     218            0 :             this, &AbstractPokitServicePrivate::characteristicRead);
     219            0 :     connect(service, &QLowEnergyService::characteristicWritten,
     220            0 :             this, &AbstractPokitServicePrivate::characteristicWritten);
     221            0 :     connect(service, &QLowEnergyService::characteristicChanged,
     222            0 :             this, &AbstractPokitServicePrivate::characteristicChanged);
     223              : 
     224            0 :     connect(service, &QLowEnergyService::descriptorRead, this,
     225            0 :         [](const QLowEnergyDescriptor &descriptor, const QByteArray &value){
     226            0 :             qCDebug(lc).noquote() << tr(R"(Descriptor "%1" (%2) read.)")
     227            0 :                 .arg(descriptor.name(), descriptor.uuid().toString());
     228            0 :             Q_UNUSED(value)
     229            0 :         });
     230              : 
     231            0 :     connect(service, &QLowEnergyService::descriptorWritten, this,
     232            0 :         [](const QLowEnergyDescriptor &descriptor, const QByteArray &newValue){
     233            0 :             qCDebug(lc).noquote() << tr(R"(Descriptor "%1" (%2) written.)")
     234            0 :                 .arg(descriptor.name(), descriptor.uuid().toString());
     235            0 :             Q_UNUSED(newValue)
     236            0 :         });
     237              : 
     238            0 :     connect(service,
     239            0 :     #if (QT_VERSION < QT_VERSION_CHECK(6, 2, 0))
     240            0 :         QOverload<QLowEnergyService::ServiceError>::of(&QLowEnergyService::error),
     241              :     #else
     242            0 :         &QLowEnergyService::errorOccurred,
     243            0 :     #endif
     244            0 :         this, &AbstractPokitServicePrivate::errorOccurred);
     245              : 
     246            0 :     if (autoDiscover) {
     247            0 :         service->discoverDetails();
     248            0 :     }
     249            0 :     return true;
     250         3716 : }
     251              : 
     252              : /*!
     253              :  * Get \a uuid characteristc from the underlying service. This helper function is equivalent to
     254              :  *
     255              :  * ```
     256              :  * return service->characteristic(uuid);
     257              :  * ```
     258              :  *
     259              :  * except that it performs some sanity checks, such as checking the service object pointer has been
     260              :  * assigned first, and also logs failures in a consistent manner.
     261              :  *
     262              :  * \param uuid
     263              :  * \return
     264              :  */
     265         8798 : QLowEnergyCharacteristic AbstractPokitServicePrivate::getCharacteristic(const QBluetoothUuid &uuid) const
     266         5912 : {
     267        14710 :     if (!service) {
     268        16782 :         qCDebug(lc).noquote() << tr(R"(Characteristic %1 "%2" requested before service assigned.)")
     269            0 :             .arg(uuid.toString(), PokitDevice::charcteristicToString(uuid));
     270        14710 :         return QLowEnergyCharacteristic();
     271         5912 :     }
     272              : 
     273            0 :     if (const QLowEnergyCharacteristic characteristic = service->characteristic(uuid); characteristic.isValid()) {
     274            0 :         return characteristic;
     275            0 :     }
     276              : 
     277            0 :     if (service->state() != QLowEnergyService::
     278            0 :         #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
     279            0 :         ServiceDiscovered
     280              :         #else
     281            0 :         RemoteServiceDiscovered
     282            0 :         #endif
     283            0 :     ) {
     284            0 :         qCWarning(lc).noquote() << tr(R"(Characteristic %1 "%2" requested before service %3 "%4" discovered.)")
     285            0 :             .arg(uuid.toString(), PokitDevice::charcteristicToString(uuid),
     286            0 :             service->serviceUuid().toString(), PokitDevice::serviceToString(service->serviceUuid()));
     287            0 :         qCInfo(lc).noquote() << tr("Current service state:") << service->state();
     288            0 :         return QLowEnergyCharacteristic();
     289            0 :     }
     290              : 
     291            0 :     qCWarning(lc).noquote() << tr(R"(Characteristic %1 "%2" not found in service %3 "%4".)")
     292            0 :         .arg(uuid.toString(), PokitDevice::charcteristicToString(uuid),
     293            0 :         service->serviceUuid().toString(), PokitDevice::serviceToString(service->serviceUuid()));
     294            0 :     return QLowEnergyCharacteristic();
     295            0 : }
     296              : 
     297              : /*!
     298              :  * Read the \a uuid characteristic.
     299              :  *
     300              :  * If succesful, the `QLowEnergyService::characteristicRead` signal will be emitted by the internal
     301              :  * service object.  For convenience, derived classes should implement the characteristicRead()
     302              :  * virtual function to handle the read value.
     303              :  *
     304              :  * Returns \c true if the characteristic read request was successfully queued, \c false otherwise.
     305              :  *
     306              :  * \see AbstractPokitService::readCharacteristics()
     307              :  * \see AbstractPokitServicePrivate::characteristicRead()
     308              :  */
     309          988 : bool AbstractPokitServicePrivate::readCharacteristic(const QBluetoothUuid &uuid)
     310         1144 : {
     311         2132 :     const QLowEnergyCharacteristic characteristic = getCharacteristic(uuid);
     312         2132 :     if (!characteristic.isValid()) {
     313         1144 :         return false;
     314         1144 :     }
     315            0 :     qCDebug(lc).noquote() << tr(R"(Reading characteristic %1 "%2".)")
     316            0 :         .arg(uuid.toString(), PokitDevice::charcteristicToString(uuid));
     317            0 :     service->readCharacteristic(characteristic);
     318            0 :     return true;
     319         2132 : }
     320              : 
     321              : /*!
     322              :  * Enables client (Pokit device) side notification for characteristic \a uuid.
     323              :  *
     324              :  * Returns \c true if the notication enable request was successfully queued, \c false otherwise.
     325              :  *
     326              :  * \see AbstractPokitServicePrivate::characteristicChanged
     327              :  * \see AbstractPokitServicePrivate::disableCharacteristicNotificatons
     328              :  */
     329          342 : bool AbstractPokitServicePrivate::enableCharacteristicNotificatons(const QBluetoothUuid &uuid)
     330          396 : {
     331          810 :     qCDebug(lc).noquote() << tr(R"(Enabling CCCD for characteristic %1 "%2".)")
     332            0 :         .arg(uuid.toString(), PokitDevice::charcteristicToString(uuid));
     333          738 :     QLowEnergyCharacteristic characteristic = getCharacteristic(uuid);
     334          738 :     if (!characteristic.isValid()) {
     335          396 :         return false;
     336          396 :     }
     337              : 
     338            0 :     QLowEnergyDescriptor descriptor = characteristic.descriptor(
     339            0 :         QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration); // 0x2902
     340            0 :     if (!descriptor.isValid()) {
     341            0 :         qCWarning(lc).noquote() << tr(R"(Characteristic %1 "%2" has no client configuration descriptor.)")
     342            0 :             .arg(uuid.toString(), PokitDevice::charcteristicToString(uuid));
     343            0 :         return false;
     344            0 :     }
     345              : 
     346            0 :     service->writeDescriptor(descriptor,
     347            0 :         #if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 0))
     348            0 :         QLowEnergyCharacteristic::CCCDEnableNotification
     349              :         #else
     350            0 :         QByteArray::fromHex("0100") // See Qt6's QLowEnergyCharacteristic::CCCDEnableNotification.
     351            0 :         #endif
     352            0 :     );
     353            0 :     return true;
     354          342 : }
     355              : 
     356              : /*!
     357              :  * Disables client (Pokit device) side notification for characteristic \a uuid.
     358              :  *
     359              :  * Returns \c true if the notication disable request was successfully queued, \c false otherwise.
     360              :  *
     361              :  * \see AbstractPokitServicePrivate::characteristicChanged
     362              :  * \see AbstractPokitServicePrivate::enableCharacteristicNotificatons
     363              :  */
     364          342 : bool AbstractPokitServicePrivate::disableCharacteristicNotificatons(const QBluetoothUuid &uuid)
     365          396 : {
     366          810 :     qCDebug(lc).noquote() << tr(R"(Disabling CCCD for characteristic %1 "%2".)")
     367            0 :         .arg(uuid.toString(), PokitDevice::charcteristicToString(uuid));
     368          738 :     QLowEnergyCharacteristic characteristic = getCharacteristic(uuid);
     369          738 :     if (!characteristic.isValid()) {
     370          396 :         return false;
     371          396 :     }
     372              : 
     373            0 :     QLowEnergyDescriptor descriptor = characteristic.descriptor(
     374            0 :         QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration); // 0x2902
     375            0 :     if (!descriptor.isValid()) {
     376            0 :         qCWarning(lc).noquote() << tr(R"(Characteristic %1 "%2" has no client configuration descriptor.)")
     377            0 :             .arg(uuid.toString(), PokitDevice::charcteristicToString(uuid));
     378            0 :         return false;
     379            0 :     }
     380              : 
     381            0 :     service->writeDescriptor(descriptor,
     382            0 :         #if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 0))
     383            0 :         QLowEnergyCharacteristic::CCCDDisable
     384              :         #else
     385            0 :         QByteArray::fromHex("0000") // See Qt6's QLowEnergyCharacteristic::CCCDDisable.
     386            0 :         #endif
     387            0 :     );
     388            0 :     return true;
     389          342 : }
     390              : 
     391              : /*!
     392              :  * Returns `false` if \a data is smaller than \a minSize, otherwise returns \a failOnMax if \a data
     393              :  * is bigger than \a maxSize, otherwise returns `true`.
     394              :  *
     395              :  * A warning is logged if either \a minSize or \a maxSize is violated, regardless of the returned
     396              :  * value; ie this funcion can be used to simply warn if \a data is too big, or it can be used to
     397              :  * failed (return `false`) in that case.
     398              :  */
     399         1596 : bool AbstractPokitServicePrivate::checkSize(const QString &label, const QByteArray &data,
     400              :                                             const int minSize, const int maxSize,
     401              :                                             const bool failOnMax)
     402         1848 : {
     403         3444 :     if (data.size() < minSize) {
     404         1664 :         qCWarning(lc).noquote() << tr("%1 requires %n byte/s, but only %2 present: %3", nullptr, minSize)
     405         1560 :             .arg(label).arg(data.size()).arg(toHexString(data));
     406          962 :         return false;
     407          572 :     }
     408         2378 :     if ((maxSize >= 0) && (data.size() > maxSize)) {
     409          755 :         qCWarning(lc).noquote() << tr("%1 has %n extraneous byte/s: %2", nullptr, data.size()-maxSize)
     410          555 :             .arg(label, toHexString(data.mid(maxSize)));
     411          410 :         return (!failOnMax);
     412          220 :     }
     413         1056 :     return true;
     414         1276 : }
     415              : 
     416              : /*!
     417              :  * Returns up to \a maxSize bytes of \a data as a human readable hexadecimal string. If \a data
     418              :  * exceeds \a maxSize, then \a data is elided in the middle. For example:
     419              :  *
     420              :  * ```
     421              :  * toHex(QBytArray("\x1\x2\x3\x4\x5\x6", 4); // "0x01,02,...,05,06"
     422              :  * ```
     423              :  */
     424          988 : QString AbstractPokitServicePrivate::toHexString(const QByteArray &data, const int maxSize)
     425         1144 : {
     426         1534 :     return (data.size() <= maxSize)
     427         4018 :         ? QString::fromLatin1("0x%1").arg(QLatin1String(data.toHex(',')))
     428         1270 :         : QString::fromLatin1("0x%1,...,%2").arg(
     429         1279 :             QLatin1String(data.left(maxSize/2-1).toHex(',')),
     430         4168 :             QLatin1String(data.right(maxSize/2-1).toHex(',')));
     431         1144 : }
     432              : 
     433              : /*!
     434              :  * Handles `QLowEnergyController::connected` events.
     435              :  *
     436              :  * If `autoDiscover` is enabled, this will begin service discovery on the newly connected contoller.
     437              :  *
     438              :  * \see AbstractPokitService::autoDiscover()
     439              :  */
     440           76 : void AbstractPokitServicePrivate::connected()
     441           32 : {
     442          108 :     if (!controller) {
     443          115 :         qCWarning(lc).noquote() << tr("Connected with no controller set") << sender();
     444           46 :         return;
     445           16 :     }
     446              : 
     447           62 :     qCDebug(lc).noquote() << tr(R"(Connected to "%1" (%2) at %3.)").arg(
     448            0 :         controller->remoteName(), controller->remoteDeviceUuid().toString(),
     449            0 :         controller->remoteAddress().toString());
     450           54 :     if (autoDiscover) {
     451           54 :         controller->discoverServices();
     452           16 :     }
     453           16 : }
     454              : 
     455              : /*!
     456              :  * Handles `QLowEnergyController::discoveryFinished` events.
     457              :  *
     458              :  * As this event indicates that the conroller has finished discovering services, this function will
     459              :  * invoke createServiceObject() to create the internal service object (if not already created).
     460              :  */
     461           76 : void AbstractPokitServicePrivate::discoveryFinished()
     462           32 : {
     463          108 :     if (!controller) {
     464          115 :         qCWarning(lc).noquote() << tr("Discovery finished with no controller set") << sender();
     465           46 :         return;
     466           16 :     }
     467              : 
     468           62 :     qCDebug(lc).noquote() << tr(R"(Discovery finished for "%1" (%2) at %3.)").arg(
     469            0 :         controller->remoteName(), controller->remoteDeviceUuid().toString(),
     470            0 :         controller->remoteAddress().toString());
     471              : 
     472           54 :     if (!createServiceObject()) {
     473          115 :         qCWarning(lc).noquote() << tr("Discovery finished, but service not found.");
     474           16 :         Q_Q(AbstractPokitService);
     475           54 :         Q_EMIT q->serviceErrorOccurred(QLowEnergyService::ServiceError::UnknownError);
     476           16 :     }
     477           16 : }
     478              : 
     479              : /*!
     480              :  * Handles `QLowEnergyController::errorOccurred` events.
     481              :  *
     482              :  * This function simply re-emits \a newError as AbstractPokitService::serviceErrorOccurred.
     483              :  */
     484           38 : void AbstractPokitServicePrivate::errorOccurred(const QLowEnergyService::ServiceError newError)
     485           44 : {
     486           44 :     Q_Q(AbstractPokitService);
     487           90 :     qCDebug(lc).noquote() << tr("Service error") << newError;
     488           82 :     Q_EMIT q->serviceErrorOccurred(newError);
     489           82 : }
     490              : 
     491              : /*!
     492              :  * Handles `QLowEnergyController::serviceDiscovered` events.
     493              :  *
     494              :  * If the discovered service is the one this (or rather the derived) class wraps, then
     495              :  * createServiceObject() will be invoked immediately (otherwise it will be invoked after full
     496              :  * service discovery has completed, ie in discoveryFinished()).
     497              :  */
     498          228 : void AbstractPokitServicePrivate::serviceDiscovered(const QBluetoothUuid &newService)
     499          264 : {
     500          492 :     if ((!service) && (newService == serviceUuid)) {
     501          270 :         qCDebug(lc).noquote() << tr("Service discovered") << newService;
     502          246 :         createServiceObject();
     503          132 :     }
     504          492 : }
     505              : 
     506              : /*!
     507              :  * Handles `QLowEnergyController::stateChanged` events.
     508              :  *
     509              :  * If \a newState indicates that service details have now been discovered, then
     510              :  * AbstractPokitService::serviceDetailsDiscovered will be emitted.
     511              :  *
     512              :  * \see AbstractPokitService::autoDiscover()
     513              :  */
     514          114 : void AbstractPokitServicePrivate::stateChanged(QLowEnergyService::ServiceState newState)
     515          132 : {
     516          270 :     qCDebug(lc).noquote() << tr("State changed to") << newState;
     517              : 
     518          246 :     if (lc().isDebugEnabled()) {
     519            0 :         for (const auto &characteristic: service->characteristics()) {
     520            0 :             QStringList properties;
     521              :             /// \cond no-doxygen
     522            0 :             #define QTPOKIT_INTERNAL_TEST_AND_APPEND(property) \
     523            0 :             if (characteristic.properties().testFlag(QLowEnergyCharacteristic::property)) { \
     524            0 :                 properties.append(QStringLiteral(#property).toLower());\
     525            0 :             }
     526              :             /// \endcond
     527            0 :             QTPOKIT_INTERNAL_TEST_AND_APPEND(Broadcasting)
     528            0 :             QTPOKIT_INTERNAL_TEST_AND_APPEND(Read)
     529            0 :             QTPOKIT_INTERNAL_TEST_AND_APPEND(WriteNoResponse)
     530            0 :             QTPOKIT_INTERNAL_TEST_AND_APPEND(Write)
     531            0 :             QTPOKIT_INTERNAL_TEST_AND_APPEND(Notify)
     532            0 :             QTPOKIT_INTERNAL_TEST_AND_APPEND(Indicate)
     533            0 :             QTPOKIT_INTERNAL_TEST_AND_APPEND(WriteSigned)
     534            0 :             QTPOKIT_INTERNAL_TEST_AND_APPEND(ExtendedProperty)
     535            0 :             #undef QTPOKIT_INTERNAL_TEST_AND_APPEND
     536            0 :             qCDebug(lc).noquote() << tr(R"(Characteristic %1 "%2" supports %3.)").arg(characteristic.uuid().toString(),
     537            0 :                 PokitDevice::charcteristicToString(characteristic.uuid()), properties.join(QStringLiteral(", ")));
     538            0 :         }
     539            0 :     }
     540              : 
     541          246 :     if (newState == QLowEnergyService::
     542           66 :             #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
     543           66 :             ServiceDiscovered
     544              :             #else
     545           66 :             RemoteServiceDiscovered
     546           66 :             #endif
     547          132 :         ) {
     548           44 :         Q_Q(AbstractPokitService);
     549           90 :         qCDebug(lc).noquote() << tr("Service details discovered.");
     550           82 :         Q_EMIT q->serviceDetailsDiscovered();
     551           44 :     }
     552          246 : }
     553              : 
     554              : /*!
     555              :  * Handles `QLowEnergyService::characteristicRead` events. This base implementation simply debug
     556              :  * logs the event.
     557              :  *
     558              :  * Derived classes should implement this function to handle the successful reads of
     559              :  * \a characteristic, typically by parsing \a value, then emitting a speciailised signal.
     560              :  */
     561          228 : void AbstractPokitServicePrivate::characteristicRead(
     562              :     const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
     563          264 : {
     564          540 :     qCDebug(lc).noquote() << tr(R"(Characteristic %1 "%2" read %n byte/s: %3)", nullptr, value.size()).arg(
     565            0 :         characteristic.uuid().toString(), PokitDevice::charcteristicToString(characteristic.uuid()), toHexString(value));
     566          492 : }
     567              : 
     568              : /*!
     569              :  * Handles `QLowEnergyService::characteristicWritten` events. This base implementation simply debug
     570              :  * logs the event.
     571              :  *
     572              :  * Derived classes should implement this function to handle the successful writes of
     573              :  * \a characteristic, typically by parsing \a newValue, then emitting a speciailised signal.
     574              :  */
     575          228 : void AbstractPokitServicePrivate::characteristicWritten(
     576              :     const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)
     577          264 : {
     578          540 :     qCDebug(lc).noquote() << tr(R"(Characteristic %1 "%2" written with %Ln byte/s: %3)", nullptr, newValue.size())
     579            0 :         .arg(characteristic.uuid().toString(), PokitDevice::charcteristicToString(characteristic.uuid()), toHexString(newValue));
     580          492 : }
     581              : 
     582              : /*!
     583              :  * Handles `QLowEnergyService::characteristicChanged` events. This base implementation simply debug
     584              :  * logs the event.
     585              :  *
     586              :  * If derived classes support characteristics with client-side notification (ie Notify, as opposed
     587              :  * to Read or Write operations), they should implement this function to handle the successful reads of
     588              :  * \a characteristic, typically by parsing \a value, then emitting a speciailised signal.
     589              :  */
     590          152 : void AbstractPokitServicePrivate::characteristicChanged(
     591              :     const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)
     592          176 : {
     593          360 :     qCDebug(lc).noquote() << tr(R"(Characteristic %1 "%2" changed to %Ln byte/s: %3)", nullptr, newValue.size())
     594            0 :         .arg(characteristic.uuid().toString(), PokitDevice::charcteristicToString(characteristic.uuid()), toHexString(newValue));
     595          328 : }
     596              : 
     597              : /// \endcond
        

Generated by: LCOV version 2.0-1