LCOV - code coverage report
Current view: top level - src/app - loggerstartcommand.cpp (source / functions) Hit Total Coverage
Project: QtPokit Lines: 85 104 81.7 %
Version: Functions: 9 11 81.8 %

          Line data    Source code
       1             : // SPDX-FileCopyrightText: 2022 Paul Colby <git@colby.id.au>
       2             : // SPDX-License-Identifier: LGPL-3.0-or-later
       3             : 
       4             : #include "loggerstartcommand.h"
       5             : 
       6             : #include <qtpokit/pokitdevice.h>
       7             : 
       8             : #include <QDateTime>
       9             : #include <QJsonDocument>
      10             : #include <QJsonObject>
      11             : 
      12             : #include <iostream>
      13             : 
      14             : /*!
      15             :  * \class LoggerStartCommand
      16             :  *
      17             :  * The LoggerStartCommand class implements the `logger` CLI command.
      18             :  */
      19             : 
      20             : /*!
      21             :  * Construct a new LoggerStartCommand object with \a parent.
      22             :  */
      23         425 : LoggerStartCommand::LoggerStartCommand(QObject * const parent) : DeviceCommand(parent),
      24         300 :     service(nullptr), settings{
      25             :         DataLoggerService::Command::Start, 0, DataLoggerService::Mode::DcVoltage,
      26         425 :         DataLoggerService::VoltageRange::_30V_to_60V, 60000, 0}
      27             : {
      28             : 
      29         425 : }
      30             : 
      31         697 : QStringList LoggerStartCommand::requiredOptions(const QCommandLineParser &parser) const
      32             : {
      33        2788 :     return DeviceCommand::requiredOptions(parser) + QStringList{
      34             :         QLatin1String("mode"),
      35             :         QLatin1String("range"),
      36        2542 :     };
      37             : }
      38             : 
      39         340 : QStringList LoggerStartCommand::supportedOptions(const QCommandLineParser &parser) const
      40             : {
      41        1360 :     return DeviceCommand::supportedOptions(parser) + QStringList{
      42             :         QLatin1String("interval"),
      43             :         QLatin1String("timestamp"),
      44        1240 :     };
      45             : }
      46             : 
      47             : /*!
      48             :  * \copybrief DeviceCommand::processOptions
      49             :  *
      50             :  * This implementation extends DeviceCommand::processOptions to process additional CLI options
      51             :  * supported (or required) by this command.
      52             :  */
      53         323 : QStringList LoggerStartCommand::processOptions(const QCommandLineParser &parser)
      54             : {
      55         323 :     QStringList errors = DeviceCommand::processOptions(parser);
      56         323 :     if (!errors.isEmpty()) {
      57             :         return errors;
      58             :     }
      59             : 
      60             :     // Parse the (required) mode option.
      61         672 :     const QString mode = parser.value(QLatin1String("mode")).trimmed().toLower();
      62         272 :     if (mode.startsWith(QLatin1String("ac v")) || mode.startsWith(QLatin1String("vac"))) {
      63          17 :         settings.mode = DataLoggerService::Mode::AcVoltage;
      64         255 :     } else if (mode.startsWith(QLatin1String("dc v")) || mode.startsWith(QLatin1String("vdc"))) {
      65         204 :         settings.mode = DataLoggerService::Mode::DcVoltage;
      66          51 :     } else if (mode.startsWith(QLatin1String("ac c")) || mode.startsWith(QLatin1String("aac"))) {
      67          17 :         settings.mode = DataLoggerService::Mode::AcCurrent;
      68          34 :     } else if (mode.startsWith(QLatin1String("dc c")) || mode.startsWith(QLatin1String("adc"))) {
      69          17 :         settings.mode = DataLoggerService::Mode::DcCurrent;
      70             :     } else {
      71          34 :         errors.append(tr("Unknown logger mode: %1").arg(parser.value(QLatin1String("mode"))));
      72          17 :         return errors;
      73             :     }
      74             : 
      75             :     // Parse the (required) range option.
      76             :     {
      77         585 :         const QString value = parser.value(QLatin1String("range"));
      78         165 :         QString unit; quint32 sensibleMinimum = 0;
      79         255 :         switch (settings.mode) {
      80             :         case DataLoggerService::Mode::Idle:
      81             :             Q_ASSERT(false); // Not possible, since the mode parsing above never allows Idle.
      82             :             break;
      83             :         case DataLoggerService::Mode::DcVoltage:
      84             :         case DataLoggerService::Mode::AcVoltage:
      85         221 :             unit = QLatin1String("V");
      86             :             sensibleMinimum = 50; // mV.
      87         221 :             break;
      88             :         case DataLoggerService::Mode::DcCurrent:
      89             :         case DataLoggerService::Mode::AcCurrent:
      90          34 :             unit = QLatin1String("A");
      91             :             sensibleMinimum = 5; // mA.
      92          34 :             break;
      93             :         }
      94             :         Q_ASSERT(!unit.isEmpty());
      95         255 :         const quint32 rangeMax = parseMilliValue(value, unit, sensibleMinimum);
      96         255 :         if (rangeMax == 0) {
      97          20 :             errors.append(tr("Invalid range value: %1").arg(value));
      98             :         } else {
      99         238 :             settings.range = lowestRange(settings.mode, rangeMax);
     100             :         }
     101          90 :     }
     102             : 
     103             :     // Parse the interval option.
     104         300 :     if (parser.isSet(QLatin1String("interval"))) {
     105         210 :         const QString value = parser.value(QLatin1String("interval"));
     106          85 :         const quint32 interval = parseMilliValue(value, QLatin1String("s"), 500);
     107          85 :         if (interval == 0) {
     108          40 :             errors.append(tr("Invalid interval value: %1").arg(value));
     109             :         } else {
     110          51 :             settings.updateInterval = interval;
     111             :         }
     112          30 :     }
     113             : 
     114             :     // Parse the timestamp option.
     115         255 :     settings.timestamp = QDateTime::currentSecsSinceEpoch(); // Note, subject to Y2038 epochalypse.
     116         300 :     if (parser.isSet(QLatin1String("timestamp"))) {
     117         125 :         const QString value = parser.value(QLatin1String("timestamp"));
     118         140 :         QLocale locale; bool ok;
     119             :         static_assert(sizeof(uint) == sizeof(settings.timestamp), "QLocale has no toUint32().");
     120          70 :         const int timestamp = locale.toUInt(value, &ok);
     121          85 :         if (!ok) {
     122          40 :             errors.append(tr("Invalid timestamp value: %1").arg(value));
     123             :         } else {
     124          51 :             settings.timestamp = timestamp;
     125             :         }
     126          30 :     }
     127             :     return errors;
     128          96 : }
     129             : 
     130             : /*!
     131             :  * \copybrief DeviceCommand::getService
     132             :  *
     133             :  * This override returns a pointer to a DataLoggerService object.
     134             :  */
     135           0 : AbstractPokitService * LoggerStartCommand::getService()
     136             : {
     137             :     Q_ASSERT(device);
     138           0 :     if (!service) {
     139           0 :         service = device->dataLogger();
     140             :         Q_ASSERT(service);
     141           0 :         connect(service, &DataLoggerService::settingsWritten,
     142             :                 this, &LoggerStartCommand::settingsWritten);
     143             :     }
     144           0 :     return service;
     145             : }
     146             : 
     147             : /*!
     148             :  * \copybrief DeviceCommand::serviceDetailsDiscovered
     149             :  *
     150             :  * This override fetches the current device's status, and outputs it in the selected format.
     151             :  */
     152           0 : void LoggerStartCommand::serviceDetailsDiscovered()
     153             : {
     154           0 :     DeviceCommand::serviceDetailsDiscovered(); // Just logs consistently.
     155           0 :     const QString range = DataLoggerService::toString(settings.range, settings.mode);
     156           0 :     qCInfo(lc).noquote() << tr("Logging %1, with range %2, every %L3ms.").arg(
     157           0 :         DataLoggerService::toString(settings.mode),
     158           0 :         (range.isNull()) ? QString::fromLatin1("N/A") : range).arg(settings.updateInterval);
     159           0 :     service->setSettings(settings);
     160           0 : }
     161             : 
     162             : /*!
     163             :  * Returns the lowest \a mode range that can measure at least up to \a desired max, or AutoRange
     164             :  * if no such range is available.
     165             :  */
     166        1292 : DataLoggerService::Range LoggerStartCommand::lowestRange(
     167             :     const DataLoggerService::Mode mode, const quint32 desiredMax)
     168             : {
     169        1292 :     switch (mode) {
     170           0 :     case DataLoggerService::Mode::Idle:
     171           0 :         qCWarning(lc).noquote() << tr("Idle has no defined ranges.");
     172             :         Q_ASSERT(false); // Should never have been called with this Idle mode.
     173           0 :         break;
     174         782 :     case DataLoggerService::Mode::DcVoltage:
     175             :     case DataLoggerService::Mode::AcVoltage:
     176         782 :         return lowestVoltageRange(desiredMax);
     177         510 :     case DataLoggerService::Mode::DcCurrent:
     178             :     case DataLoggerService::Mode::AcCurrent:
     179         510 :         return lowestCurrentRange(desiredMax);
     180           0 :     default:
     181           0 :         qCWarning(lc).noquote() << tr("No defined ranges for mode %1.").arg((quint8)mode);
     182             :         Q_ASSERT(false); // Should never have been called with this invalid mode.
     183             :     }
     184           0 :     return DataLoggerService::Range();
     185             : }
     186             : 
     187             : #define POKIT_APP_IF_LESS_THAN_RETURN(value, label) \
     188             : if (value <=  DataLoggerService::maxValue(DataLoggerService::label).toUInt()) { \
     189             :     return DataLoggerService::label; \
     190             : }
     191             : 
     192             : /*!
     193             :  * Returns the lowest current range that can measure at least up to \a desired max, or AutoRange
     194             :  * if no such range is available.
     195             :  */
     196         748 : DataLoggerService::CurrentRange LoggerStartCommand::lowestCurrentRange(const quint32 desiredMax)
     197             : {
     198         748 :     POKIT_APP_IF_LESS_THAN_RETURN(desiredMax, CurrentRange::_0_to_10mA)
     199         595 :     POKIT_APP_IF_LESS_THAN_RETURN(desiredMax, CurrentRange::_10mA_to_30mA)
     200         442 :     POKIT_APP_IF_LESS_THAN_RETURN(desiredMax, CurrentRange::_30mA_to_150mA)
     201         272 :     POKIT_APP_IF_LESS_THAN_RETURN(desiredMax, CurrentRange::_150mA_to_300mA)
     202         119 :     POKIT_APP_IF_LESS_THAN_RETURN(desiredMax, CurrentRange::_300mA_to_3A)
     203             :     return DataLoggerService::CurrentRange::_300mA_to_3A; // Out of range, so go with the biggest.
     204             : }
     205             : 
     206             : /*!
     207             :  * Returns the lowest voltage range that can measure at least up to \a desired max, or AutoRange
     208             :  * if no such range is available.
     209             :  */
     210        1071 : DataLoggerService::VoltageRange LoggerStartCommand::lowestVoltageRange(const quint32 desiredMax)
     211             : {
     212        1071 :     POKIT_APP_IF_LESS_THAN_RETURN(desiredMax, VoltageRange::_0_to_300mV)
     213         918 :     POKIT_APP_IF_LESS_THAN_RETURN(desiredMax, VoltageRange::_300mV_to_2V)
     214         578 :     POKIT_APP_IF_LESS_THAN_RETURN(desiredMax, VoltageRange::_2V_to_6V)
     215         408 :     POKIT_APP_IF_LESS_THAN_RETURN(desiredMax, VoltageRange::_6V_to_12V)
     216         255 :     POKIT_APP_IF_LESS_THAN_RETURN(desiredMax, VoltageRange::_12V_to_30V)
     217         102 :     POKIT_APP_IF_LESS_THAN_RETURN(desiredMax, VoltageRange::_30V_to_60V)
     218             :     return DataLoggerService::VoltageRange::_30V_to_60V; // Out of range, so go with the biggest.
     219             : }
     220             : 
     221             : #undef POKIT_APP_IF_LESS_THAN_RETURN
     222             : 
     223             : /*!
     224             :  * Invoked when the data logger settings have been written.
     225             :  */
     226          51 : void LoggerStartCommand::settingsWritten()
     227             : {
     228          51 :     qCDebug(lc).noquote() << tr("Settings written; data logger has started.");
     229          51 :     switch (format) {
     230           6 :     case OutputFormat::Csv:
     231          20 :         std::cout << qUtf8Printable(tr("logger_start_result\nsuccess\n"));
     232          17 :         break;
     233             :     case OutputFormat::Json:
     234          23 :         std::cout << qUtf8Printable(QLatin1String("true\n"));
     235          17 :         break;
     236           6 :     case OutputFormat::Text:
     237          20 :         std::cout << qUtf8Printable(tr("Done.\n"));
     238          17 :         break;
     239             :     }
     240          51 :     if (device) disconnect(); // Will exit the application once disconnected.
     241          51 : }

Generated by: LCOV version 1.14