LCOV - code coverage report
Current view: top level - src/lib - multimeterservice.cpp (source / functions) Hit Total Coverage
Project: Dokit Lines: 189 212 89.2 %
Version: Functions: 41 45 91.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 MultimeterService and MultimeterServicePrivate classes.
       7             :  */
       8             : 
       9             : #include <qtpokit/multimeterservice.h>
      10             : #include "multimeterservice_p.h"
      11             : 
      12             : #include <QDataStream>
      13             : #include <QIODevice>
      14             : #include <QtEndian>
      15             : 
      16             : /*!
      17             :  * \class MultimeterService
      18             :  *
      19             :  * The MultimeterService class accesses the `Multimeter` service of Pokit devices.
      20             :  */
      21             : 
      22             : /// UUID of the "Multimeter" service.
      23             : const QBluetoothUuid MultimeterService::
      24             :     serviceUuid(QLatin1String("e7481d2f-5781-442e-bb9a-fd4e3441dadc"));
      25             : 
      26             : /// \struct MultimeterService::CharacteristicUuids
      27             : /// \brief Characteristics available via the `Multimeter` service.
      28             : 
      29             : /// UUID of the `Multimeter` service's `Settings` characterstic.
      30             : const QBluetoothUuid MultimeterService::CharacteristicUuids::
      31             :     settings(QLatin1String("53dc9a7a-bc19-4280-b76b-002d0e23b078"));
      32             : 
      33             : /// UUID of the `Multimeter` service's `Reading` characterstic.
      34             : const QBluetoothUuid MultimeterService::CharacteristicUuids::
      35             :     reading(QLatin1String("047d3559-8bee-423a-b229-4417fa603b90"));
      36             : 
      37             : /// \enum MultimeterService::Mode
      38             : /// \brief Values supported by the `Mode` attribute of the `Settings` and `Reading` characteristics.
      39             : 
      40             : /// Returns \a mode as a user-friendly string.
      41        1638 : QString MultimeterService::toString(const Mode &mode)
      42             : {
      43        1638 :     switch (mode) {
      44         324 :     case Mode::Idle:        return tr("Idle");
      45         144 :     case Mode::DcVoltage:   return tr("DC voltage");
      46         144 :     case Mode::AcVoltage:   return tr("AC voltage");
      47         144 :     case Mode::DcCurrent:   return tr("DC current");
      48         144 :     case Mode::AcCurrent:   return tr("AC current");
      49         144 :     case Mode::Resistance:  return tr("Resistance");
      50         144 :     case Mode::Diode:       return tr("Diode");
      51         270 :     case Mode::Continuity:  return tr("Continuity");
      52         144 :     case Mode::Temperature: return tr("Temperature");
      53             :     default:                return QString();
      54             :     }
      55             : }
      56             : 
      57             : /// \enum MultimeterService::VoltageRange
      58             : /// \brief Values supported by the `Range` attribute of the `Settings` and `Reading` characteristics,
      59             : /// when `Mode` is AC or DC voltage.
      60             : 
      61             : /// Returns \a range as a user-friendly string.
      62         450 : QString MultimeterService::toString(const VoltageRange &range)
      63             : {
      64         450 :     switch (range) {
      65          54 :     case VoltageRange::_0_to_300mV:  return tr("0 to 300mV");
      66         234 :     case VoltageRange::_300mV_to_2V: return tr("300mV to 2V");
      67          18 :     case VoltageRange::_2V_to_6V:    return tr("2V to 6V");
      68          18 :     case VoltageRange::_6V_to_12V:   return tr("6V to 12V");
      69          18 :     case VoltageRange::_12V_to_30V:  return tr("12V to 30V");
      70          54 :     case VoltageRange::_30V_to_60V:  return tr("30V to 60V");
      71          18 :     case VoltageRange::AutoRange:    return tr("Auto-range");
      72             :     default:                         return QString();
      73             :     }
      74             : }
      75             : 
      76             : /*!
      77             :  *  Returns the minimum value for \a range in (integer) millivolts, or the string "Auto".
      78             :  *  If \a range is not known valid value, then an null QVariant is returned.
      79             :  *
      80             :  *  Note, this is an *absolute* minimum. That is, the true range for DC measurements is from
      81             :  *  `-maxValue(range)` to `+maxValue(range)`. In this sense, `minValue(range)` indicates the
      82             :  *  magnitude (ignore signs) that can be measured accurately for the given \a range. As AC voltage
      83             :  *  can never be negative, this is relevant for DC voltage only.
      84             :  */
      85         378 : QVariant MultimeterService::minValue(const VoltageRange &range)
      86             : {
      87         378 :     switch (range) {
      88          18 :     case VoltageRange::_0_to_300mV:  return     0;
      89         234 :     case VoltageRange::_300mV_to_2V: return   300;
      90          18 :     case VoltageRange::_2V_to_6V:    return  2000;
      91          18 :     case VoltageRange::_6V_to_12V:   return  6000;
      92          18 :     case VoltageRange::_12V_to_30V:  return 12000;
      93          18 :     case VoltageRange::_30V_to_60V:  return 30000;
      94          22 :     case VoltageRange::AutoRange:    return tr("Auto");
      95             :     default:                         return QVariant();
      96             :     }
      97             : }
      98             : 
      99             : /*!
     100             :  *  Returns the maximum value for \a range in (integer) millivolts, or the string "Auto".
     101             :  *  If \a range is not known valid value, then an null QVariant is returned.
     102             :  */
     103        4518 : QVariant MultimeterService::maxValue(const VoltageRange &range)
     104             : {
     105        4518 :     switch (range) {
     106        1134 :     case VoltageRange::_0_to_300mV:  return   300;
     107        1188 :     case VoltageRange::_300mV_to_2V: return  2000;
     108         792 :     case VoltageRange::_2V_to_6V:    return  6000;
     109         612 :     case VoltageRange::_6V_to_12V:   return 12000;
     110         450 :     case VoltageRange::_12V_to_30V:  return 30000;
     111         288 :     case VoltageRange::_30V_to_60V:  return 60000;
     112          22 :     case VoltageRange::AutoRange:    return tr("Auto");
     113             :     default:                         return QVariant();
     114             :     }
     115             : }
     116             : 
     117             : /// \enum MultimeterService::CurrentRange
     118             : /// \brief Values supported by the `Range` attribute of the `Settings` and `Reading` characteristics,
     119             : /// when `Mode` is AC or DC current.
     120             : 
     121             : /// Returns \a range as a user-friendly string.
     122         432 : QString MultimeterService::toString(const CurrentRange &range)
     123             : {
     124         432 :     switch (range) {
     125          54 :     case CurrentRange::_0_to_10mA:      return tr("0 to 10mA");
     126          18 :     case CurrentRange::_10mA_to_30mA:   return tr("10mA to 30mA");
     127         126 :     case CurrentRange::_30mA_to_150mA:  return tr("30mA to 150mA");
     128          18 :     case CurrentRange::_150mA_to_300mA: return tr("150mA to 300mA");
     129         162 :     case CurrentRange::_300mA_to_3A:    return tr("300mA to 3A");
     130          18 :     case CurrentRange::AutoRange:       return tr("Auto-range");
     131             :     default:                            return QString();
     132             :     }
     133             : }
     134             : 
     135             : /*!
     136             :  *  Returns the minimum value for \a range in (integer) milliamps, or the string "Auto".
     137             :  *  If \a range is not known valid value, then an null QVariant is returned.
     138             :  *
     139             :  *  Note, this is an *absolute* minimum. That is, the true range for DC measurements is from
     140             :  *  `-maxValue(range)` to `+maxValue(range)`. In this sense, `minValue(range)` indicates the
     141             :  *  magnitude (ignore signs) that can be measured accurately for the given \a range. As AC current
     142             :  *  can never be negative, this is relevant for DC current only.
     143             :  */
     144         360 : QVariant MultimeterService::minValue(const CurrentRange &range)
     145             : {
     146         360 :     switch (range) {
     147          18 :     case CurrentRange::_0_to_10mA:      return   0;
     148          18 :     case CurrentRange::_10mA_to_30mA:   return  10;
     149         126 :     case CurrentRange::_30mA_to_150mA:  return  30;
     150          18 :     case CurrentRange::_150mA_to_300mA: return 150;
     151         126 :     case CurrentRange::_300mA_to_3A:    return 300;
     152          22 :     case CurrentRange::AutoRange:       return tr("Auto");
     153             :     default:                            return QVariant();
     154             :     }
     155             : }
     156             : 
     157             : /*!
     158             :  *  Returns the maximum value for \a range in (integer) milliamps, or the string "Auto".
     159             :  *  If \a range is not known valid value, then an null QVariant is returned.
     160             :  */
     161        3474 : QVariant MultimeterService::maxValue(const CurrentRange &range)
     162             : {
     163        3474 :     switch (range) {
     164         972 :     case CurrentRange::_0_to_10mA:      return   10;
     165         810 :     case CurrentRange::_10mA_to_30mA:   return   30;
     166         756 :     case CurrentRange::_30mA_to_150mA:  return  150;
     167         468 :     case CurrentRange::_150mA_to_300mA: return  300;
     168         414 :     case CurrentRange::_300mA_to_3A:    return 3000;
     169          22 :     case CurrentRange::AutoRange:       return tr("Auto");
     170             :     default:                            return QVariant();
     171             :     }
     172             : }
     173             : 
     174             : /// \enum MultimeterService::ResistanceRange
     175             : /// \brief Values supported by the `Range` attribute of the `Settings` and `Reading` characteristics,
     176             : /// when `Mode` is resistance.
     177             : 
     178             : /// Returns \a range as a user-friendly string.
     179         306 : QString MultimeterService::toString(const ResistanceRange &range)
     180             : {
     181         306 :     switch (range) {
     182          18 :     case ResistanceRange::_0_to_160:     return tr("0 to 160 ohms");
     183          18 :     case ResistanceRange::_160_to_330:   return tr("160 to 330 ohms");
     184          18 :     case ResistanceRange::_330_to_890:   return tr("330 to 890 ohms");
     185          18 :     case ResistanceRange::_890_to_1K5:   return tr("890 to 1.5K ohms");
     186         126 :     case ResistanceRange::_1K5_to_10K:   return tr("1.5K to 10K ohms");
     187          18 :     case ResistanceRange::_10K_to_100K:  return tr("10K to 100K ohms");
     188          18 :     case ResistanceRange::_100K_to_470K: return tr("100K to 470K ohms");
     189          18 :     case ResistanceRange::_470K_to_1M:   return tr("470K to 1M ohms");
     190          18 :     case ResistanceRange::AutoRange:     return tr("Auto-range");
     191             :     default:                             return QString();
     192             :     }
     193             : }
     194             : 
     195             : /*!
     196             :  *  Returns the minimum value for \a range in (integer) ohms, or the string "Auto".
     197             :  *  If \a range is not known valid value, then an null QVariant is returned.
     198             :  */
     199         306 : QVariant MultimeterService::minValue(const ResistanceRange &range)
     200             : {
     201         306 :     switch (range) {
     202          18 :     case ResistanceRange::_0_to_160:     return      0;
     203          18 :     case ResistanceRange::_160_to_330:   return    160;
     204          18 :     case ResistanceRange::_330_to_890:   return    330;
     205          18 :     case ResistanceRange::_890_to_1K5:   return    890;
     206         126 :     case ResistanceRange::_1K5_to_10K:   return   1500;
     207          18 :     case ResistanceRange::_10K_to_100K:  return  10000;
     208          18 :     case ResistanceRange::_100K_to_470K: return 100000;
     209          18 :     case ResistanceRange::_470K_to_1M:   return 470000;
     210          22 :     case ResistanceRange::AutoRange:     return tr("Auto");
     211             :     default:                             return QVariant();
     212             :     }
     213             : }
     214             : 
     215             : /*!
     216             :  *  Returns the maximum value for \a range in (integer) ohms, or the string "Auto".
     217             :  *  If \a range is not known valid value, then an null QVariant is returned.
     218             :  */
     219        4770 : QVariant MultimeterService::maxValue(const ResistanceRange &range)
     220             : {
     221        4770 :     switch (range) {
     222         954 :     case ResistanceRange::_0_to_160:     return     160;
     223         846 :     case ResistanceRange::_160_to_330:   return     330;
     224         738 :     case ResistanceRange::_330_to_890:   return     890;
     225         630 :     case ResistanceRange::_890_to_1K5:   return    1500;
     226         630 :     case ResistanceRange::_1K5_to_10K:   return   10000;
     227         414 :     case ResistanceRange::_10K_to_100K:  return  100000;
     228         306 :     case ResistanceRange::_100K_to_470K: return  470000;
     229         198 :     case ResistanceRange::_470K_to_1M:   return 1000000;
     230          22 :     case ResistanceRange::AutoRange:     return tr("Auto");
     231             :     default:                             return QVariant();
     232             :     }
     233             : }
     234             : 
     235             : /// \union MultimeterService::Range
     236             : /// \brief Values supported by the `Range` attribute of the `Settings` characteristic.
     237             : 
     238             : static_assert(std::is_same<std::underlying_type_t<MultimeterService::VoltageRange>,
     239             :                            std::underlying_type_t<MultimeterService::CurrentRange>>::value,
     240             :               "MultimeterService::Range members must all have the same underlying type.");
     241             : 
     242             : /// Constructs a new MultimeterService::Range instance with 0. This should be considered
     243        2034 : MultimeterService::Range::Range() : voltageRange(static_cast<MultimeterService::VoltageRange>(0))
     244             : {
     245             : 
     246        2034 : }
     247             : 
     248             : /// Constructs a new MultimeterService::Range instance with \a range.
     249        3012 : MultimeterService::Range::Range(const MultimeterService::VoltageRange range) : voltageRange(range)
     250             : {
     251             : 
     252        3006 : }
     253             : 
     254             : /// Constructs a new MultimeterService::Range instance with \a range.
     255         882 : MultimeterService::Range::Range(const MultimeterService::CurrentRange range) : currentRange(range)
     256             : {
     257             : 
     258         882 : }
     259             : 
     260             : /// Constructs a new MultimeterService::Range instance with \a range.
     261         504 : MultimeterService::Range::Range(const MultimeterService::ResistanceRange range) : resistanceRange(range)
     262             : {
     263             : 
     264         504 : }
     265             : 
     266             : /// Returns \a range as a user-friendly string, or a null QString if \a mode has no ranges.
     267         162 : QString MultimeterService::toString(const Range &range, const Mode &mode)
     268             : {
     269         162 :     switch (mode) {
     270          72 :     case Mode::DcVoltage:
     271             :     case Mode::AcVoltage:
     272          72 :         return toString(range.voltageRange);
     273          72 :     case Mode::DcCurrent:
     274             :     case Mode::AcCurrent:
     275          72 :         return toString(range.currentRange);
     276             :     default:
     277             :         return QString();
     278             :     }
     279             : }
     280             : 
     281             : /// Returns \c true if \a lhs is numerically equal to \a rhs, \c false otherwise.
     282        2304 : bool operator==(const MultimeterService::Range &lhs, const MultimeterService::Range &rhs)
     283             : {
     284        2304 :     return static_cast<std::underlying_type_t<MultimeterService::VoltageRange>>(lhs.voltageRange)
     285        2304 :         == static_cast<std::underlying_type_t<MultimeterService::VoltageRange>>(rhs.voltageRange);
     286             : }
     287             : 
     288             : /// Returns \c true if \a lhs is numerically not-equal to \a rhs, \c false otherwise.
     289          18 : bool operator!=(const MultimeterService::Range &lhs, const MultimeterService::Range &rhs)
     290             : {
     291          18 :     return static_cast<std::underlying_type_t<MultimeterService::VoltageRange>>(lhs.voltageRange)
     292          18 :         != static_cast<std::underlying_type_t<MultimeterService::VoltageRange>>(rhs.voltageRange);
     293             : }
     294             : 
     295             : /// Returns \c true if \a lhs is numerically less than \a rhs, \c false otherwise.
     296          18 : bool operator< (const MultimeterService::Range &lhs, const MultimeterService::Range &rhs)
     297             : {
     298          18 :     return static_cast<std::underlying_type_t<MultimeterService::VoltageRange>>(lhs.voltageRange)
     299          18 :          < static_cast<std::underlying_type_t<MultimeterService::VoltageRange>>(rhs.voltageRange);
     300             : }
     301             : 
     302             : /// Returns \c true if \a lhs is numerically greater than \a rhs, \c false otherwise.
     303          18 : bool operator> (const MultimeterService::Range &lhs, const MultimeterService::Range &rhs)
     304             : {
     305          18 :     return static_cast<std::underlying_type_t<MultimeterService::VoltageRange>>(lhs.voltageRange)
     306          18 :          > static_cast<std::underlying_type_t<MultimeterService::VoltageRange>>(rhs.voltageRange);
     307             : }
     308             : 
     309             : /// Returns \c true if \a lhs is numerically less than or equal to \a rhs, \c false otherwise.
     310          36 : bool operator<=(const MultimeterService::Range &lhs, const MultimeterService::Range &rhs)
     311             : {
     312          36 :     return static_cast<std::underlying_type_t<MultimeterService::VoltageRange>>(lhs.voltageRange)
     313          36 :         <= static_cast<std::underlying_type_t<MultimeterService::VoltageRange>>(rhs.voltageRange);
     314             : }
     315             : 
     316             : /// Returns \c true if \a lhs is numerically greater than or equal to \a rhs, \c false otherwise.
     317          36 : bool operator>=(const MultimeterService::Range &lhs, const MultimeterService::Range &rhs)
     318             : {
     319          36 :     return static_cast<std::underlying_type_t<MultimeterService::VoltageRange>>(lhs.voltageRange)
     320          36 :         >= static_cast<std::underlying_type_t<MultimeterService::VoltageRange>>(rhs.voltageRange);
     321             : }
     322             : 
     323             : /// \struct MultimeterService::Settings
     324             : /// \brief Attributes included in the `Settings` characterstic.
     325             : 
     326             : /// \enum MultimeterService::MeterStatus
     327             : /// \brief Values supported by the `Status` attribute of the `Settings` characteristic.
     328             : 
     329             : /// \struct MultimeterService::Reading
     330             : /// \brief Attributes included in the `Reading` characterstic.
     331             : 
     332             : /*!
     333             :  * Constructs a new Pokit service with \a parent.
     334             :  */
     335         198 : MultimeterService::MultimeterService(QLowEnergyController * const controller, QObject * parent)
     336         198 :     : AbstractPokitService(new MultimeterServicePrivate(controller, this), parent)
     337             : {
     338             : 
     339         198 : }
     340             : 
     341             : /*!
     342             :  * \cond internal
     343             :  * Constructs a new Pokit service with \a parent, and private implementation \a d.
     344             :  */
     345           0 : MultimeterService::MultimeterService(
     346           0 :     MultimeterServicePrivate * const d, QObject * const parent)
     347           0 :     : AbstractPokitService(d, parent)
     348             : {
     349             : 
     350           0 : }
     351             : /// \endcond
     352             : 
     353             : /*!
     354             :  * Destroys this MultimeterService object.
     355             :  */
     356         180 : MultimeterService::~MultimeterService()
     357             : {
     358             : 
     359         180 : }
     360             : 
     361          18 : bool MultimeterService::readCharacteristics()
     362             : {
     363          18 :     return readReadingCharacteristic();
     364             : }
     365             : 
     366             : /*!
     367             :  * Read the `Multimeter` service's `Reading` characteristic.
     368             :  *
     369             :  * Returns `true` is the read request is succesfully queued, `false` otherwise (ie if the
     370             :  * underlying controller it not yet connected to the Pokit device, or the device's services have
     371             :  * not yet been discovered).
     372             :  *
     373             :  * Emits readingRead() if/when the characteristic has been read successfully.
     374             :  */
     375          30 : bool MultimeterService::readReadingCharacteristic()
     376             : {
     377             :     Q_D(MultimeterService);
     378          36 :     return d->readCharacteristic(CharacteristicUuids::reading);
     379             : }
     380             : 
     381             : /*!
     382             :  * Configures the Pokit device's multimeter mode.
     383             :  *
     384             :  * Returns `true` if the write request was successfully queued, `false` otherwise.
     385             :  *
     386             :  * Emits settingsWritten() if/when the \a settings have been writtem successfully.
     387             :  */
     388          18 : bool MultimeterService::setSettings(const Settings &settings)
     389             : {
     390             :     Q_D(const MultimeterService);
     391             :     const QLowEnergyCharacteristic characteristic =
     392          18 :         d->getCharacteristic(CharacteristicUuids::settings);
     393          18 :     if (!characteristic.isValid()) {
     394             :         return false;
     395             :     }
     396             : 
     397           0 :     const QByteArray value = MultimeterServicePrivate::encodeSettings(settings);
     398           0 :     if (value.isNull()) {
     399             :         return false;
     400             :     }
     401             : 
     402           0 :     d->service->writeCharacteristic(characteristic, value);
     403           0 :     return (d->service->error() != QLowEnergyService::ServiceError::CharacteristicWriteError);
     404          18 : }
     405             : 
     406             : /*!
     407             :  * Returns the most recent value of the `Multimeter` service's `Reading` characteristic.
     408             :  *
     409             :  * The returned value, if any, is from the underlying Bluetooth stack's cache. If no such value is
     410             :  * currently available (ie the serviceDetailsDiscovered signal has not been emitted yet), then the
     411             :  * returned MultimeterService::Reading::value member will be a quiet NaN, which can be checked like:
     412             :  *
     413             :  * ```
     414             :  * const MultimeterService::Reading reading = multimeterService->reading();
     415             :  * if (qIsNaN(reading.value)) {
     416             :  *     // Handle failure.
     417             :  * }
     418             :  * ```
     419             :  */
     420          18 : MultimeterService::Reading MultimeterService::reading() const
     421             : {
     422             :     Q_D(const MultimeterService);
     423             :     const QLowEnergyCharacteristic characteristic =
     424          18 :         d->getCharacteristic(CharacteristicUuids::reading);
     425          18 :     return (characteristic.isValid()) ? MultimeterServicePrivate::parseReading(characteristic.value())
     426             :         : Reading{ MeterStatus::Error, std::numeric_limits<float>::quiet_NaN(),
     427          36 :                    Mode::Idle, VoltageRange::AutoRange };
     428          18 : }
     429             : 
     430             : /*!
     431             :  * Enables client-side notifications of meter readings.
     432             :  *
     433             :  * This is an alternative to manually requesting individual reads via readReadingCharacteristic().
     434             :  *
     435             :  * Returns `true` is the request was successfully submited to the device queue, `false` otherwise.
     436             :  *
     437             :  * Successfully read values (if any) will be emitted via the readingRead() signal.
     438             :  */
     439          18 : bool MultimeterService::enableReadingNotifications()
     440             : {
     441             :     Q_D(MultimeterService);
     442          18 :     return d->enableCharacteristicNotificatons(CharacteristicUuids::reading);
     443             : }
     444             : 
     445             : /*!
     446             :  * Disables client-side notifications of meter readings.
     447             :  *
     448             :  * Instantaneous reads can still be fetched by readReadingCharacteristic().
     449             :  *
     450             :  * Returns `true` is the request was successfully submited to the device queue, `false` otherwise.
     451             :  */
     452          18 : bool MultimeterService::disableReadingNotifications()
     453             : {
     454             :     Q_D(MultimeterService);
     455          18 :     return d->disableCharacteristicNotificatons(CharacteristicUuids::reading);
     456             : }
     457             : 
     458             : /*!
     459             :  * \fn MultimeterService::readingRead
     460             :  *
     461             :  * This signal is emitted when the `Reading` characteristic has been read successfully.
     462             :  *
     463             :  * \see readReadingCharacteristic
     464             :  */
     465             : 
     466             : /*!
     467             :  * \fn MultimeterService::settingsWritten
     468             :  *
     469             :  * This signal is emitted when the `Settings` characteristic has been written successfully.
     470             :  *
     471             :  * \see setSettings
     472             :  */
     473             : 
     474             : /*!
     475             :  * \cond internal
     476             :  * \class MultimeterServicePrivate
     477             :  *
     478             :  * The MultimeterServicePrivate class provides private implementation for MultimeterService.
     479             :  */
     480             : 
     481             : /*!
     482             :  * \internal
     483             :  * Constructs a new MultimeterServicePrivate object with public implementation \a q.
     484             :  */
     485         132 : MultimeterServicePrivate::MultimeterServicePrivate(
     486         198 :     QLowEnergyController * controller, MultimeterService * const q)
     487         198 :     : AbstractPokitServicePrivate(MultimeterService::serviceUuid, controller, q)
     488             : {
     489             : 
     490         132 : }
     491             : 
     492             : /*!
     493             :  * Returns \a settings in the format Pokit devices expect.
     494             :  */
     495          72 : QByteArray MultimeterServicePrivate::encodeSettings(const MultimeterService::Settings &settings)
     496             : {
     497             :     static_assert(sizeof(settings.mode)           == 1, "Expected to be 1 byte.");
     498             :     static_assert(sizeof(settings.range)          == 1, "Expected to be 1 byte.");
     499             :     static_assert(sizeof(settings.updateInterval) == 4, "Expected to be 4 bytes.");
     500             : 
     501          16 :     QByteArray value;
     502          72 :     QDataStream stream(&value, QIODevice::WriteOnly);
     503          72 :     stream.setByteOrder(QDataStream::LittleEndian);
     504          72 :     stream.setFloatingPointPrecision(QDataStream::SinglePrecision); // 32-bit floats, not 64-bit.
     505          72 :     stream << (quint8)settings.mode << (quint8)settings.range.voltageRange << settings.updateInterval;
     506             : 
     507             :     Q_ASSERT(value.size() == 6);
     508          72 :     return value;
     509          72 : }
     510             : 
     511             : /*!
     512             :  * Parses the `Reading` \a value into a MultimeterService::Reading struct.
     513             :  */
     514          90 : MultimeterService::Reading MultimeterServicePrivate::parseReading(const QByteArray &value)
     515             : {
     516          90 :     MultimeterService::Reading reading{
     517             :         MultimeterService::MeterStatus::Error,
     518             :         std::numeric_limits<float>::quiet_NaN(),
     519             :         MultimeterService::Mode::Idle,
     520             :         MultimeterService::VoltageRange::AutoRange
     521          60 :     };
     522             : 
     523         110 :     if (!checkSize(QLatin1String("Reading"), value, 7, 7)) {
     524          36 :         return reading;
     525             :     }
     526             : 
     527          54 :     reading.status = MultimeterService::MeterStatus(value.at(0));
     528          66 :     reading.value = qFromLittleEndian<float>(value.mid(1,4));
     529          54 :     reading.mode = static_cast<MultimeterService::Mode>(value.at(5));
     530          54 :     reading.range.voltageRange = static_cast<MultimeterService::VoltageRange>(value.at(6));
     531          54 :     return reading;
     532             : }
     533             : 
     534             : /*!
     535             :  * Implements AbstractPokitServicePrivate::characteristicRead to parse \a value, then emit a
     536             :  * specialised signal, for each supported \a characteristic.
     537             :  */
     538          18 : void MultimeterServicePrivate::characteristicRead(const QLowEnergyCharacteristic &characteristic,
     539             :                                               const QByteArray &value)
     540             : {
     541          18 :     AbstractPokitServicePrivate::characteristicRead(characteristic, value);
     542             : 
     543             :     Q_Q(MultimeterService);
     544          18 :     if (characteristic.uuid() == MultimeterService::CharacteristicUuids::reading) {
     545           0 :         emit q->readingRead(parseReading(value));
     546           0 :         return;
     547             :     }
     548             : 
     549          18 :     if (characteristic.uuid() == MultimeterService::CharacteristicUuids::settings) {
     550           0 :         qCWarning(lc).noquote() << tr("Settings characteristic is write-only, but somehow read")
     551           0 :             << serviceUuid << characteristic.name() << characteristic.uuid();
     552           0 :         return;
     553             :     }
     554             : 
     555          54 :     qCWarning(lc).noquote() << tr("Unknown characteristic read for Multimeter service")
     556          24 :         << serviceUuid << characteristic.name() << characteristic.uuid();
     557             : }
     558             : 
     559             : /*!
     560             :  * Implements AbstractPokitServicePrivate::characteristicWritten to parse \a newValue, then emit a
     561             :  * specialised signal, for each supported \a characteristic.
     562             :  */
     563          18 : void MultimeterServicePrivate::characteristicWritten(const QLowEnergyCharacteristic &characteristic,
     564             :                                                      const QByteArray &newValue)
     565             : {
     566          18 :     AbstractPokitServicePrivate::characteristicWritten(characteristic, newValue);
     567             : 
     568             :     Q_Q(MultimeterService);
     569          18 :     if (characteristic.uuid() == MultimeterService::CharacteristicUuids::settings) {
     570           0 :         emit q->settingsWritten();
     571           0 :         return;
     572             :     }
     573             : 
     574          18 :     if (characteristic.uuid() == MultimeterService::CharacteristicUuids::reading) {
     575           0 :         qCWarning(lc).noquote() << tr("Reading characteristic is read/notify, but somehow written")
     576           0 :             << serviceUuid << characteristic.name() << characteristic.uuid();
     577           0 :         return;
     578             :     }
     579             : 
     580          54 :     qCWarning(lc).noquote() << tr("Unknown characteristic written for Multimeter service")
     581          24 :         << serviceUuid << characteristic.name() << characteristic.uuid();
     582             : }
     583             : 
     584             : /*!
     585             :  * Implements AbstractPokitServicePrivate::characteristicChanged to parse \a newValue, then emit a
     586             :  * specialised signal, for each supported \a characteristic.
     587             :  */
     588          18 : void MultimeterServicePrivate::characteristicChanged(const QLowEnergyCharacteristic &characteristic,
     589             :                                                      const QByteArray &newValue)
     590             : {
     591          18 :     AbstractPokitServicePrivate::characteristicChanged(characteristic, newValue);
     592             : 
     593             :     Q_Q(MultimeterService);
     594          18 :     if (characteristic.uuid() == MultimeterService::CharacteristicUuids::settings) {
     595           0 :         qCWarning(lc).noquote() << tr("Settings characteristic is write-only, but somehow updated")
     596           0 :             << serviceUuid << characteristic.name() << characteristic.uuid();
     597           0 :         return;
     598             :     }
     599             : 
     600          18 :     if (characteristic.uuid() == MultimeterService::CharacteristicUuids::reading) {
     601           0 :         emit q->readingRead(parseReading(newValue));
     602           0 :         return;
     603             :     }
     604             : 
     605          54 :     qCWarning(lc).noquote() << tr("Unknown characteristic notified for Multimeter service")
     606          24 :         << serviceUuid << characteristic.name() << characteristic.uuid();
     607             : }
     608             : 
     609             : /// \endcond

Generated by: LCOV version 1.14