LCOV - code coverage report
Current view: top level - src/lib - abstractpokitservice.cpp (source / functions) Hit Total Coverage
Project: QtPokit Lines: 101 172 58.7 %
Version: Functions: 23 28 82.1 %

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

Generated by: LCOV version 1.14