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: 89.3 % 28 25

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

Generated by: LCOV version 2.3.1-1