LCOV - code coverage report
Current view: top level - src/lib - dataloggerservice.cpp (source / functions) Hit Total Coverage
Project: Dokit Lines: 123 156 78.8 %
Version: Functions: 27 31 87.1 %

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

Generated by: LCOV version 1.14