LCOV - code coverage report
Current view: top level - src/cli - loggerstartcommand.cpp (source / functions) Hit Total Coverage
Project: Dokit Lines: 75 91 82.4 %
Version: Functions: 6 15 40.0 %

          Line data    Source code
       1             : // SPDX-FileCopyrightText: 2022-2024 Paul Colby <git@colby.id.au>
       2             : // SPDX-License-Identifier: LGPL-3.0-or-later
       3             : 
       4             : #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         540 : LoggerStartCommand::LoggerStartCommand(QObject * const parent) : DeviceCommand(parent)
      24             : {
      25             : 
      26         540 : }
      27             : 
      28         900 : QStringList LoggerStartCommand::requiredOptions(const QCommandLineParser &parser) const
      29             : {
      30        2970 :     return DeviceCommand::requiredOptions(parser) + QStringList{
      31             :         QLatin1String("mode"),
      32        2610 :     };
      33             : }
      34             : 
      35         440 : QStringList LoggerStartCommand::supportedOptions(const QCommandLineParser &parser) const
      36             : {
      37        2332 :     return DeviceCommand::supportedOptions(parser) + QStringList{
      38             :         QLatin1String("interval"),
      39             :         QLatin1String("range"), // May still be required by processOptions(), depending on the --mode option's value.
      40             :         QLatin1String("timestamp"),
      41        2156 :     };
      42           0 : }
      43             : 
      44             : /*!
      45             :  * \copybrief DeviceCommand::processOptions
      46             :  *
      47             :  * This implementation extends DeviceCommand::processOptions to process additional CLI options
      48             :  * supported (or required) by this command.
      49             :  */
      50         420 : QStringList LoggerStartCommand::processOptions(const QCommandLineParser &parser)
      51             : {
      52         420 :     QStringList errors = DeviceCommand::processOptions(parser);
      53         420 :     if (!errors.isEmpty()) {
      54             :         return errors;
      55             :     }
      56             : 
      57             :     // Parse the (required) mode option.
      58         760 :     const QString mode = parser.value(QLatin1String("mode")).trimmed().toLower();
      59         380 :     if (mode.startsWith(QLatin1String("ac v")) || mode.startsWith(QLatin1String("vac"))) {
      60          20 :         settings.mode = DataLoggerService::Mode::AcVoltage;
      61          20 :         minRangeFunc = minVoltageRange;
      62         360 :     } else if (mode.startsWith(QLatin1String("dc v")) || mode.startsWith(QLatin1String("vdc"))) {
      63         260 :         settings.mode = DataLoggerService::Mode::DcVoltage;
      64         260 :         minRangeFunc = minVoltageRange;
      65         100 :     } else if (mode.startsWith(QLatin1String("ac c")) || mode.startsWith(QLatin1String("aac"))) {
      66          20 :         settings.mode = DataLoggerService::Mode::AcCurrent;
      67          20 :         minRangeFunc = minCurrentRange;
      68          80 :     } else if (mode.startsWith(QLatin1String("dc c")) || mode.startsWith(QLatin1String("adc"))) {
      69          20 :         settings.mode = DataLoggerService::Mode::DcCurrent;
      70          20 :         minRangeFunc = minCurrentRange;
      71          60 :     } else if (mode.startsWith(QLatin1String("temp"))) {
      72          40 :         settings.mode = DataLoggerService::Mode::Temperature;
      73          40 :         minRangeFunc = nullptr;
      74             :     } else {
      75          20 :         minRangeFunc = nullptr;
      76          40 :         errors.append(tr("Unknown logger mode: %1").arg(parser.value(QLatin1String("mode"))));
      77          20 :         return errors;
      78             :     }
      79             : 
      80             :     // Parse the range option.
      81         360 :     rangeOptionValue = 0;
      82         468 :     if (parser.isSet(QLatin1String("range"))) {
      83         340 :         const QString value = parser.value(QLatin1String("range"));
      84         340 :         switch (settings.mode) {
      85             :         case DataLoggerService::Mode::DcVoltage:
      86             :         case DataLoggerService::Mode::AcVoltage:
      87         260 :             rangeOptionValue = parseNumber<std::milli>(value, QLatin1String("V"), 50); // mV.
      88         260 :             break;
      89             :         case DataLoggerService::Mode::DcCurrent:
      90             :         case DataLoggerService::Mode::AcCurrent:
      91          40 :             rangeOptionValue = parseNumber<std::milli>(value, QLatin1String("A"), 5); // mA.
      92          40 :             break;
      93          40 :         default:
      94          96 :             qCInfo(lc).noquote() << tr("Ignoring range value: %1").arg(value);
      95             :         }
      96         340 :         if ((minRangeFunc != nullptr) && (rangeOptionValue == 0)) {
      97          26 :             errors.append(tr("Invalid range value: %1").arg(value));
      98             :         }
      99         258 :     } else if (settings.mode != DataLoggerService::Mode::Temperature) {
     100          42 :         errors.append(tr("Missing required option for logger mode '%1': range")
     101          40 :             .arg(parser.value(QLatin1String("mode"))));
     102             :     }
     103             : 
     104             :     // Parse the interval option.
     105         468 :     if (parser.isSet(QLatin1String("interval"))) {
     106         170 :         const QString value = parser.value(QLatin1String("interval"));
     107         100 :         const quint32 interval = parseNumber<std::milli>(value, QLatin1String("s"), 500);
     108         100 :         if (interval == 0) {
     109          52 :             errors.append(tr("Invalid interval value: %1").arg(value));
     110             :         } else {
     111          60 :             settings.updateInterval = interval;
     112             :         }
     113          70 :     }
     114             : 
     115             :     // Parse the timestamp option.
     116         360 :     settings.timestamp = (quint32)QDateTime::currentSecsSinceEpoch(); // Note, subject to Y2038 epochalypse.
     117         468 :     if (parser.isSet(QLatin1String("timestamp"))) {
     118         100 :         const QString value = parser.value(QLatin1String("timestamp"));
     119         100 :         QLocale locale; bool ok;
     120             :         static_assert(sizeof(uint) == sizeof(settings.timestamp), "QLocale has no toUint32().");
     121          70 :         const int timestamp = locale.toUInt(value, &ok);
     122         100 :         if (!ok) {
     123          52 :             errors.append(tr("Invalid timestamp value: %1").arg(value));
     124             :         } else {
     125          60 :             settings.timestamp = timestamp;
     126             :         }
     127         100 :     }
     128             :     return errors;
     129         266 : }
     130             : 
     131             : /*!
     132             :  * \copybrief DeviceCommand::getService
     133             :  *
     134             :  * This override returns a pointer to a DataLoggerService object.
     135             :  */
     136           0 : AbstractPokitService * LoggerStartCommand::getService()
     137             : {
     138             :     Q_ASSERT(device);
     139           0 :     if (!service) {
     140           0 :         service = device->dataLogger();
     141             :         Q_ASSERT(service);
     142           0 :         connect(service, &DataLoggerService::settingsWritten,
     143           0 :                 this, &LoggerStartCommand::settingsWritten);
     144             :     }
     145           0 :     return service;
     146             : }
     147             : 
     148             : /*!
     149             :  * \copybrief DeviceCommand::serviceDetailsDiscovered
     150             :  *
     151             :  * This override fetches the current device's status, and outputs it in the selected format.
     152             :  */
     153           0 : void LoggerStartCommand::serviceDetailsDiscovered()
     154             : {
     155           0 :     DeviceCommand::serviceDetailsDiscovered(); // Just logs consistently.
     156           0 :     settings.range = (minRangeFunc == nullptr) ? 0 : minRangeFunc(*service->pokitProduct(), rangeOptionValue);
     157           0 :     const QString range = service->toString(settings.range, settings.mode);
     158           0 :     qCInfo(lc).noquote() << tr("Logging %1, with range %2, every %L3ms.").arg(
     159           0 :         DataLoggerService::toString(settings.mode),
     160           0 :         (range.isNull()) ? QString::fromLatin1("N/A") : range).arg(settings.updateInterval);
     161           0 :     service->setSettings(settings);
     162           0 : }
     163             : 
     164             : /*!
     165             :  * \var LoggerStartCommand::minRangeFunc
     166             :  *
     167             :  * Pointer to function for converting #rangeOptionValue to a Pokit device's range enumerator. This function pointer
     168             :  * is assigned during the command line parsing, but is not invoked until after the device's services are discovere,
     169             :  * because prior to that discovery, we don't know which product (Meter vs Pro vs Clamp, etc) we're talking to and thus
     170             :  * which enumerator list to be using.
     171             :  *
     172             :  * If the current mode does not support ranges (eg diode, and continuity modes), then this member will be \c nullptr.
     173             :  *
     174             :  * \see processOptions
     175             :  * \see serviceDetailsDiscovered
     176             :  */
     177             : 
     178             : /*!
     179             :  * Invoked when the data logger settings have been written.
     180             :  */
     181          60 : void LoggerStartCommand::settingsWritten()
     182             : {
     183          66 :     qCDebug(lc).noquote() << tr("Settings written; data logger has started.");
     184          60 :     switch (format) {
     185          20 :     case OutputFormat::Csv:
     186          26 :         std::cout << qUtf8Printable(tr("logger_start_result\nsuccess\n"));
     187          20 :         break;
     188             :     case OutputFormat::Json:
     189          32 :         std::cout << qUtf8Printable(QLatin1String("true\n"));
     190          20 :         break;
     191          20 :     case OutputFormat::Text:
     192          26 :         std::cout << qUtf8Printable(tr("Done.\n"));
     193          20 :         break;
     194             :     }
     195          60 :     if (device) disconnect(); // Will exit the application once disconnected.
     196          60 : }

Generated by: LCOV version 1.14