LCOV - code coverage report
Current view: top level - src/lib - dataloggerservice.cpp (source / functions) Coverage Total Hit
Project: Dokit Lines: 83.0 % 264 219
Version: Functions: 96.0 % 25 24

            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 DataLoggerService and DataLoggerServicePrivate classes.
       7              :  */
       8              : 
       9              : #include <qtpokit/dataloggerservice.h>
      10              : #include "dataloggerservice_p.h"
      11              : #include "pokitproducts_p.h"
      12              : #include "../stringliterals_p.h"
      13              : 
      14              : #include <qtpokit/statusservice.h>
      15              : 
      16              : #include <QDataStream>
      17              : #include <QIODevice>
      18              : #include <QLowEnergyController>
      19              : #include <QtEndian>
      20              : 
      21              : QTPOKIT_BEGIN_NAMESPACE
      22              : DOKIT_USE_STRINGLITERALS
      23              : 
      24              : /*!
      25              :  * \class DataLoggerService
      26              :  *
      27              :  * The DataLoggerService class accesses the `Data Logger` service of Pokit devices.
      28              :  */
      29              : 
      30              : /// Returns \a mode as a user-friendly string.
      31        12240 : QString DataLoggerService::toString(const Mode &mode)
      32         3928 : {
      33        16168 :     switch (mode) {
      34          136 :     case Mode::Idle:        return tr("Idle");
      35         3152 :     case Mode::DcVoltage:   return tr("DC voltage");
      36         3152 :     case Mode::AcVoltage:   return tr("AC voltage");
      37         3152 :     case Mode::DcCurrent:   return tr("DC current");
      38         3152 :     case Mode::AcCurrent:   return tr("AC current");
      39         3152 :     case Mode::Temperature: return tr("Temperature");
      40          112 :     default:                return QString();
      41         3928 :     }
      42         3928 : }
      43              : 
      44              : /// Returns \a range as a user-friendly string, or a null QString if \a mode has no ranges.
      45         8160 : QString DataLoggerService::toString(const PokitProduct product, const quint8 range, const Mode mode)
      46         2832 : {
      47        10992 :     switch (mode) {
      48          112 :     case Mode::Idle:
      49          112 :         break;
      50         3632 :     case Mode::DcVoltage:
      51         1088 :     case Mode::AcVoltage:
      52         4288 :         return VoltageRange::toString(product, range);
      53         3856 :     case Mode::DcCurrent:
      54         1088 :     case Mode::AcCurrent:
      55         4288 :         return CurrentRange::toString(product, range);
      56          544 :     case Mode::Temperature:
      57          544 :         break;
      58         2832 :     }
      59          656 :     return QString();
      60         2832 : }
      61              : 
      62              : /// Returns \a range as a user-friendly string, or a null QString if \a mode has no ranges.
      63         7680 : QString DataLoggerService::toString(const quint8 range, const Mode mode) const
      64         2496 : {
      65        10176 :     return toString(*pokitProduct(), range, mode);
      66         2496 : }
      67              : 
      68              : /*!
      69              :  * Returns the maximum value for \a range, or 0 if \a range is not a known value for \a product's \a mode.
      70              :  */
      71          960 : quint32 DataLoggerService::maxValue(const PokitProduct product, const quint8 range, const Mode mode)
      72          672 : {
      73         1632 :     switch (mode) {
      74          112 :     case Mode::Idle:
      75          112 :         break;
      76          320 :     case Mode::DcVoltage:
      77          224 :     case Mode::AcVoltage:
      78          544 :         return VoltageRange::maxValue(product, range);
      79          544 :     case Mode::DcCurrent:
      80          224 :     case Mode::AcCurrent:
      81          544 :         return CurrentRange::maxValue(product, range);
      82          112 :     case Mode::Temperature:
      83          112 :         break;
      84          672 :     }
      85          224 :     return 0;
      86          672 : }
      87              : 
      88              : /*!
      89              :  * Returns the maximum value for \a range, or 0 \a range is not a known value for the current \a product's \a mode.
      90              :  */
      91          480 : quint32 DataLoggerService::maxValue(const quint8 range, const Mode mode) const
      92          336 : {
      93          816 :     return maxValue(*pokitProduct(), range, mode);
      94          336 : }
      95              : 
      96              : /*!
      97              :  * \typedef DataLoggerService::Samples
      98              :  *
      99              :  * Raw samples from the `Reading` characteristic. These raw samples are (supposedly) within the
     100              :  * range -2048 to +2047, and need to be multiplied by the Metadata::scale value from the `Metadata`
     101              :  * characteristic to get the true values.
     102              :  *
     103              :  * Also supposedly, there should be no more than 10 samples at a time, according to Pokit's current
     104              :  * API docs. There is not artificial limitation imposed by QtPokit, so devices may begin batching
     105              :  * more samples in future. Specifically, the Pokit Pro seems to send 88 samples (in 176 bytes) at a
     106              :  * time.
     107              :  */
     108              : 
     109              : /*!
     110              :  * Constructs a new Pokit service with \a parent.
     111              :  */
     112         8240 : DataLoggerService::DataLoggerService(QLowEnergyController * const controller, QObject * parent)
     113        11160 :     : AbstractPokitService(new DataLoggerServicePrivate(controller, this), parent)
     114         3368 : {
     115              : 
     116        11608 : }
     117              : 
     118              : /*!
     119              :  * \cond internal
     120              :  * Constructs a new Pokit service with \a parent, and private implementation \a d.
     121              :  */
     122            0 : DataLoggerService::DataLoggerService(
     123            0 :     DataLoggerServicePrivate * const d, QObject * const parent)
     124            0 :     : AbstractPokitService(d, parent)
     125            0 : {
     126              : 
     127            0 : }
     128              : /// \endcond
     129              : 
     130           80 : bool DataLoggerService::readCharacteristics()
     131           56 : {
     132          136 :     return readMetadataCharacteristic();
     133           56 : }
     134              : 
     135              : /*!
     136              :  * Reads the `DataLogger` service's `Metadata` characteristic.
     137              :  *
     138              :  * Returns `true` is the read request is successfully queued, `false` otherwise (ie if the
     139              :  * underlying controller it not yet connected to the Pokit device, or the device's services have
     140              :  * not yet been discovered).
     141              :  *
     142              :  * Emits metadataRead() if/when the characteristic has been read successfully.
     143              :  */
     144          125 : bool DataLoggerService::readMetadataCharacteristic()
     145          112 : {
     146          112 :     Q_D(DataLoggerService);
     147          272 :     return d->readCharacteristic(CharacteristicUuids::metadata);
     148          112 : }
     149              : 
     150              : /*!
     151              :  * Configures the Pokit device's data logger mode.
     152              :  *
     153              :  * Returns `true` if the write request was successfully queued, `false` otherwise.
     154              :  *
     155              :  * Emits settingsWritten() if/when the \a settings have been written successfully.
     156              :  */
     157          320 : bool DataLoggerService::setSettings(const Settings &settings)
     158          224 : {
     159          224 :     Q_D(const DataLoggerService);
     160          224 :     const QLowEnergyCharacteristic characteristic =
     161          544 :         d->getCharacteristic(CharacteristicUuids::settings);
     162          544 :     if (!characteristic.isValid()) {
     163          224 :         return false;
     164          224 :     }
     165              : 
     166            0 :     const bool updateIntervalIs32bit =
     167            0 :         (d->getCharacteristic(CharacteristicUuids::metadata).value().size() >= 23);
     168            0 :     const QByteArray value = DataLoggerServicePrivate::encodeSettings(settings, updateIntervalIs32bit);
     169            0 :     if (value.isNull()) {
     170            0 :         return false;
     171            0 :     }
     172              : 
     173            0 :     d->service->writeCharacteristic(characteristic, value);
     174            0 :     return (d->service->error() != QLowEnergyService::ServiceError::CharacteristicWriteError);
     175          320 : }
     176              : 
     177              : /*!
     178              :  * Start the data logger with \a settings.
     179              :  *
     180              :  * This is just a synonym for setSettings() except makes the caller's intention more explicit, and
     181              :  * sanity-checks that the settings's command is DataLoggerService::Command::Start.
     182              :  */
     183          240 : bool DataLoggerService::startLogger(const Settings &settings)
     184          168 : {
     185          168 :     Q_D(const DataLoggerService);
     186          168 :     Q_ASSERT(settings.command == DataLoggerService::Command::Start);
     187          408 :     if (settings.command != DataLoggerService::Command::Start) {
     188          576 :         qCWarning(d->lc).noquote() << tr("Settings command must be 'Start'.");
     189          214 :         return false;
     190          112 :     }
     191          136 :     return setSettings(settings);
     192          168 : }
     193              : 
     194              : /*!
     195              :  * Stop the data logger.
     196              :  *
     197              :  * This is just a convenience function equivalent to calling setSettings() with the command set to
     198              :  * DataLoggerService::Command::Stop.
     199              :  */
     200           80 : bool DataLoggerService::stopLogger()
     201           56 : {
     202              :     // Note, only the Settings::command member need be set, since the others are all ignored by the
     203              :     // Pokit device when the command is Stop. However, we still explicitly initialise all other
     204              :     // members just to ensure we're never exposing uninitialised RAM to an external device.
     205          136 :     return setSettings({ DataLoggerService::Command::Stop,  0, DataLoggerService::Mode::Idle, 0, 0, 0 });
     206           56 : }
     207              : 
     208              : /*!
     209              :  * Start the data logger.
     210              :  *
     211              :  * This is just a convenience function equivalent to calling setSettings() with the command set to
     212              :  * DataLoggerService::Command::Refresh.
     213              :  *
     214              :  * Once the Pokit device has processed this request successfully, the device will begin notifying
     215              :  * the `Metadata` and `Reading` characteristic, resulting in emits of metadataRead and samplesRead
     216              :  * respectively.
     217              :  */
     218           80 : bool DataLoggerService::fetchSamples()
     219           56 : {
     220              :     // Note, only the Settings::command member need be set, since the others are all ignored by the
     221              :     // Pokit device when the command is Refresh. However, we still explicitly initialise all other
     222              :     // members just to ensure we're never exposing uninitialised RAM to an external device.
     223          136 :     return setSettings({ DataLoggerService::Command::Refresh, 0, DataLoggerService::Mode::Idle, 0, 0, 0 });
     224           56 : }
     225              : 
     226              : /*!
     227              :  * Returns the most recent value of the `DataLogger` service's `Metadata` characteristic.
     228              :  *
     229              :  * The returned value, if any, is from the underlying Bluetooth stack's cache. If no such value is
     230              :  * currently available (ie the serviceDetailsDiscovered signal has not been emitted yet), then the
     231              :  * returned DataLoggerService::Metadata::scale member will be a quiet NaN, which can be checked like:
     232              :  *
     233              :  * ```
     234              :  * const DataLoggerService::Metadata metadata = multimeterService->metadata();
     235              :  * if (qIsNaN(metadata.scale)) {
     236              :  *     // Handle failure.
     237              :  * }
     238              :  * ```
     239              :  */
     240           80 : DataLoggerService::Metadata DataLoggerService::metadata() const
     241           56 : {
     242           56 :     Q_D(const DataLoggerService);
     243           56 :     const QLowEnergyCharacteristic characteristic =
     244          136 :         d->getCharacteristic(CharacteristicUuids::metadata);
     245          136 :     return (characteristic.isValid()) ? DataLoggerServicePrivate::parseMetadata(characteristic.value())
     246          216 :         : Metadata{ LoggerStatus::Error, std::numeric_limits<float>::quiet_NaN(), Mode::Idle, 0, 0, 0, 0 };
     247          136 : }
     248              : 
     249              : /*!
     250              :  * Enables client-side notifications of Data Logger metadata changes.
     251              :  *
     252              :  * This is an alternative to manually requesting individual reads via readMetadataCharacteristic().
     253              :  *
     254              :  * Returns `true` is the request was successfully submitted to the device queue, `false` otherwise.
     255              :  *
     256              :  * Successfully read values (if any) will be emitted via the metadataRead() signal.
     257              :  */
     258           80 : bool DataLoggerService::enableMetadataNotifications()
     259           56 : {
     260           56 :     Q_D(DataLoggerService);
     261          136 :     return d->enableCharacteristicNotificatons(CharacteristicUuids::metadata);
     262           56 : }
     263              : 
     264              : /*!
     265              :  * Disables client-side notifications of Data Logger metadata changes.
     266              :  *
     267              :  * Instantaneous reads can still be fetched by readMetadataCharacteristic().
     268              :  *
     269              :  * Returns `true` is the request was successfully submitted to the device queue, `false` otherwise.
     270              :  */
     271           80 : bool DataLoggerService::disableMetadataNotifications()
     272           56 : {
     273           56 :     Q_D(DataLoggerService);
     274          136 :     return d->disableCharacteristicNotificatons(CharacteristicUuids::metadata);
     275           56 : }
     276              : 
     277              : /*!
     278              :  * Enables client-side notifications of Data Logger readings.
     279              :  *
     280              :  * Returns `true` is the request was successfully submitted to the device queue, `false` otherwise.
     281              :  *
     282              :  * Successfully read samples (if any) will be emitted via the samplesRead() signal.
     283              :  */
     284           80 : bool DataLoggerService::enableReadingNotifications()
     285           56 : {
     286           56 :     Q_D(DataLoggerService);
     287          136 :     return d->enableCharacteristicNotificatons(CharacteristicUuids::reading);
     288           56 : }
     289              : 
     290              : /*!
     291              :  * Disables client-side notifications of Data Logger readings.
     292              :  *
     293              :  * Returns `true` is the request was successfully submitted to the device queue, `false` otherwise.
     294              :  */
     295           80 : bool DataLoggerService::disableReadingNotifications()
     296           56 : {
     297           56 :     Q_D(DataLoggerService);
     298          136 :     return d->disableCharacteristicNotificatons(CharacteristicUuids::reading);
     299           56 : }
     300              : 
     301              : /*!
     302              :  * \fn DataLoggerService::settingsWritten
     303              :  *
     304              :  * This signal is emitted when the `Settings` characteristic has been written successfully.
     305              :  *
     306              :  * \see setSettings
     307              :  */
     308              : 
     309              : /*!
     310              :  * \fn DataLoggerService::metadataRead
     311              :  *
     312              :  * This signal is emitted when the `Metadata` characteristic has been read successfully.
     313              :  *
     314              :  * \see readMetadataCharacteristic
     315              :  */
     316              : 
     317              : /*!
     318              :  * \fn DataLoggerService::samplesRead
     319              :  *
     320              :  * This signal is emitted when the `Reading` characteristic has been notified.
     321              :  *
     322              :  * \see beginSampling
     323              :  * \see stopSampling
     324              :  */
     325              : 
     326              : 
     327              : /*!
     328              :  * \cond internal
     329              :  * \class DataLoggerServicePrivate
     330              :  *
     331              :  * The DataLoggerServicePrivate class provides private implementation for DataLoggerService.
     332              :  */
     333              : 
     334              : /*!
     335              :  * \internal
     336              :  * Constructs a new DataLoggerServicePrivate object with public implementation \a q.
     337              :  */
     338         4635 : DataLoggerServicePrivate::DataLoggerServicePrivate(
     339         8240 :     QLowEnergyController * controller, DataLoggerService * const q)
     340        11160 :     : AbstractPokitServicePrivate(DataLoggerService::serviceUuid, controller, q)
     341         3368 : {
     342              : 
     343         8003 : }
     344              : 
     345              : /*!
     346              :  * Returns \a settings in the format Pokit devices expect. If \a updateIntervalIs32bit is \c true
     347              :  * then the `Update Interval` field will be encoded in 32-bit instead of 16.
     348              :  */
     349          560 : QByteArray DataLoggerServicePrivate::encodeSettings(const DataLoggerService::Settings &settings,
     350              :                                                     const bool updateIntervalIs32bit)
     351          392 : {
     352          392 :     static_assert(sizeof(settings.command)        == 1, "Expected to be 1 byte.");
     353          392 :     static_assert(sizeof(settings.arguments)      == 2, "Expected to be 2 bytes.");
     354          392 :     static_assert(sizeof(settings.mode)           == 1, "Expected to be 1 byte.");
     355          392 :     static_assert(sizeof(settings.range)          == 1, "Expected to be 1 byte.");
     356          392 :     static_assert(sizeof(settings.updateInterval) == 4, "Expected to be 4 bytes.");
     357          392 :     static_assert(sizeof(settings.timestamp)      == 4, "Expected to be 4 bytes.");
     358              : 
     359          693 :     QByteArray value;
     360          952 :     QDataStream stream(&value, QIODevice::WriteOnly);
     361          952 :     stream.setByteOrder(QDataStream::LittleEndian);
     362          952 :     stream.setFloatingPointPrecision(QDataStream::SinglePrecision); // 32-bit floats, not 64-bit.
     363          952 :     stream << (quint8)settings.command << settings.arguments << (quint8)settings.mode << settings.range;
     364              : 
     365              :     /*!
     366              :      * \pokitApi For Pokit Meter, `updateInterval` is `uint16` seconds (as per the Pokit API 1.00),
     367              :      * however for Pokit Pro it's `uint32` milliseconds, even though that's not officially
     368              :      * documented anywhere.
     369              :      */
     370              : 
     371          952 :     if (!updateIntervalIs32bit) {
     372          272 :         stream << (quint16)((settings.updateInterval+500)/1000) << settings.timestamp;
     373          112 :         Q_ASSERT(value.size() == 11); // According to Pokit API 1.00.
     374          280 :     } else {
     375          680 :         stream << settings.updateInterval << settings.timestamp;
     376          280 :         Q_ASSERT(value.size() == 13); // According to testing / experimentation.
     377          280 :     }
     378          952 :     return value;
     379          952 : }
     380              : 
     381              : /*!
     382              :  * Parses the `Metadata` \a value into a DataLoggerService::Metatdata struct.
     383              :  */
     384          400 : DataLoggerService::Metadata DataLoggerServicePrivate::parseMetadata(const QByteArray &value)
     385          280 : {
     386          680 :     DataLoggerService::Metadata metadata{
     387          280 :         DataLoggerService::LoggerStatus::Error, std::numeric_limits<float>::quiet_NaN(),
     388          280 :         DataLoggerService::Mode::Idle, 0, 0, 0, 0
     389          280 :     };
     390              : 
     391              :     // Pokit Meter: 15 bytes, Pokit Pro: 23 bytes.
     392          895 :     if (!checkSize(u"Metadata"_s, value, 15, 23)) {
     393          112 :         return metadata;
     394          112 :     }
     395              : 
     396          495 :     qCDebug(lc) << value.mid(7,12).toHex(',');
     397          408 :     metadata.status = static_cast<DataLoggerService::LoggerStatus>(value.at(0));
     398          537 :     metadata.scale  = qFromLittleEndian<float>(value.mid(1,4).constData());
     399          408 :     metadata.mode   = static_cast<DataLoggerService::Mode>(value.at(5));
     400          408 :     metadata.range  = static_cast<quint8>(value.at(6));
     401              : 
     402              :     /*!
     403              :      * \pokitApi For Pokit Meter, `updateInterval` is `uint16` (as per the Pokit API 1.00), however
     404              :      * for Pokit Pro it's `uint32`, even though that's not officially documented anywhere.
     405              :      * Also note, the doc claims 'microseconds' (ie 10^-6), but clearly the value is 'milliseconds'
     406              :      * (ie 10^-3) for Pokit Pro, and whole seconds for Pokit Meter.
     407              :      */
     408              : 
     409          408 :     if (value.size() == 15) {
     410          179 :         metadata.updateInterval  = qFromLittleEndian<quint16>(value.mid(7,2).constData())*1000;
     411          179 :         metadata.numberOfSamples = qFromLittleEndian<quint16>(value.mid(9,2).constData());
     412          179 :         metadata.timestamp       = qFromLittleEndian<quint32>(value.mid(11,4).constData());
     413          272 :     } else if (value.size() == 23) {
     414          179 :         metadata.updateInterval  = qFromLittleEndian<quint32>(value.mid(7,4).constData());
     415          179 :         metadata.numberOfSamples = qFromLittleEndian<quint16>(value.mid(11,2).constData());
     416          179 :         metadata.timestamp       = qFromLittleEndian<quint32>(value.mid(19,4).constData());
     417           56 :     } else {
     418          249 :         qCWarning(lc).noquote() << tr("Cannot decode metadata of %n byte/s: %1", nullptr, value.size())
     419          189 :             .arg(toHexString(value));
     420           56 :     }
     421          168 :     return metadata;
     422          280 : }
     423              : 
     424              : /*!
     425              :  * Parses the `Reading` \a value into a DataLoggerService::Samples vector.
     426              :  */
     427          320 : DataLoggerService::Samples DataLoggerServicePrivate::parseSamples(const QByteArray &value)
     428          224 : {
     429          396 :     DataLoggerService::Samples samples;
     430          544 :     if ((value.size()%2) != 0) {
     431          273 :         qCWarning(lc).noquote() << tr("Samples value has odd size %1 (should be even): %2")
     432          225 :             .arg(value.size()).arg(toHexString(value));
     433           65 :         return samples;
     434           56 :     }
     435         1632 :     while ((samples.size()*2) < value.size()) {
     436         1611 :         samples.append(qFromLittleEndian<qint16>(value.mid(samples.size()*2,2).constData()));
     437          504 :     }
     438          495 :     qCDebug(lc).noquote() << tr("Read %n sample/s from %1-bytes.", nullptr, samples.size()).arg(value.size());
     439          195 :     return samples;
     440          224 : }
     441              : 
     442              : /*!
     443              :  * Implements AbstractPokitServicePrivate::characteristicRead to parse \a value, then emit a
     444              :  * specialised signal, for each supported \a characteristic.
     445              :  */
     446           80 : void DataLoggerServicePrivate::characteristicRead(const QLowEnergyCharacteristic &characteristic,
     447              :                                               const QByteArray &value)
     448           56 : {
     449          136 :     AbstractPokitServicePrivate::characteristicRead(characteristic, value);
     450              : 
     451          136 :     if (characteristic.uuid() == DataLoggerService::CharacteristicUuids::settings) {
     452            0 :         qCWarning(lc).noquote() << tr("Settings characteristic is write-only, but somehow read")
     453            0 :             << serviceUuid << characteristic.name() << characteristic.uuid();
     454            0 :         return;
     455            0 :     }
     456              : 
     457           56 :     Q_Q(DataLoggerService);
     458          136 :     if (characteristic.uuid() == DataLoggerService::CharacteristicUuids::metadata) {
     459            0 :         Q_EMIT q->metadataRead(parseMetadata(value));
     460            0 :         return;
     461            0 :     }
     462              : 
     463          136 :     if (characteristic.uuid() == DataLoggerService::CharacteristicUuids::reading) {
     464            0 :         qCWarning(lc).noquote() << tr("Reading characteristic is notify-only")
     465            0 :             << serviceUuid << characteristic.name() << characteristic.uuid();
     466            0 :         return;
     467            0 :     }
     468              : 
     469          354 :     qCWarning(lc).noquote() << tr("Unknown characteristic read for Data Logger service")
     470          226 :         << serviceUuid << characteristic.name() << characteristic.uuid();
     471           56 : }
     472              : 
     473              : /*!
     474              :  * Implements AbstractPokitServicePrivate::characteristicWritten to parse \a newValue, then emit a
     475              :  * specialised signal, for each supported \a characteristic.
     476              :  */
     477           80 : void DataLoggerServicePrivate::characteristicWritten(const QLowEnergyCharacteristic &characteristic,
     478              :                                                      const QByteArray &newValue)
     479           56 : {
     480          136 :     AbstractPokitServicePrivate::characteristicWritten(characteristic, newValue);
     481              : 
     482           56 :     Q_Q(DataLoggerService);
     483          136 :     if (characteristic.uuid() == DataLoggerService::CharacteristicUuids::settings) {
     484            0 :         Q_EMIT q->settingsWritten();
     485            0 :         return;
     486            0 :     }
     487              : 
     488          136 :     if (characteristic.uuid() == DataLoggerService::CharacteristicUuids::metadata) {
     489            0 :         qCWarning(lc).noquote() << tr("Metadata characteristic is read/notify, but somehow written")
     490            0 :             << serviceUuid << characteristic.name() << characteristic.uuid();
     491            0 :         return;
     492            0 :     }
     493              : 
     494          136 :     if (characteristic.uuid() == DataLoggerService::CharacteristicUuids::reading) {
     495            0 :         qCWarning(lc).noquote() << tr("Reading characteristic is notify-only, but somehow written")
     496            0 :             << serviceUuid << characteristic.name() << characteristic.uuid();
     497            0 :         return;
     498            0 :     }
     499              : 
     500          354 :     qCWarning(lc).noquote() << tr("Unknown characteristic written for Data Logger service")
     501          226 :         << serviceUuid << characteristic.name() << characteristic.uuid();
     502           56 : }
     503              : 
     504              : /*!
     505              :  * Implements AbstractPokitServicePrivate::characteristicChanged to parse \a newValue, then emit a
     506              :  * specialised signal, for each supported \a characteristic.
     507              :  */
     508           80 : void DataLoggerServicePrivate::characteristicChanged(const QLowEnergyCharacteristic &characteristic,
     509              :                                                      const QByteArray &newValue)
     510           56 : {
     511          136 :     AbstractPokitServicePrivate::characteristicChanged(characteristic, newValue);
     512              : 
     513           56 :     Q_Q(DataLoggerService);
     514          136 :     if (characteristic.uuid() == DataLoggerService::CharacteristicUuids::settings) {
     515            0 :         qCWarning(lc).noquote() << tr("Settings characteristic is write-only, but somehow updated")
     516            0 :             << serviceUuid << characteristic.name() << characteristic.uuid();
     517            0 :         return;
     518            0 :     }
     519              : 
     520          136 :     if (characteristic.uuid() == DataLoggerService::CharacteristicUuids::metadata) {
     521            0 :         Q_EMIT q->metadataRead(parseMetadata(newValue));
     522            0 :         return;
     523            0 :     }
     524              : 
     525          136 :     if (characteristic.uuid() == DataLoggerService::CharacteristicUuids::reading) {
     526            0 :         Q_EMIT q->samplesRead(parseSamples(newValue));
     527            0 :         return;
     528            0 :     }
     529              : 
     530          354 :     qCWarning(lc).noquote() << tr("Unknown characteristic notified for Data Logger service")
     531          226 :         << serviceUuid << characteristic.name() << characteristic.uuid();
     532           56 : }
     533              : 
     534              : /// \endcond
     535              : 
     536              : QTPOKIT_END_NAMESPACE
        

Generated by: LCOV version 2.3.1-1