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

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

Generated by: LCOV version 2.0-1