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

Generated by: LCOV version 2.3-1