LCOV - code coverage report
Current view: top level - src/cli - loggerstartcommand.cpp (source / functions) Coverage Total Hit
Project: Dokit Lines: 84.6 % 123 104
Version: Functions: 71.4 % 7 5

            Line data    Source code
       1              : // SPDX-FileCopyrightText: 2022-2026 Paul Colby <git@colby.id.au>
       2              : // SPDX-License-Identifier: LGPL-3.0-or-later
       3              : 
       4              : #include "loggerstartcommand.h"
       5              : #include "../stringliterals_p.h"
       6              : 
       7              : #include <qtpokit/pokitdevice.h>
       8              : 
       9              : #include <QDateTime>
      10              : #include <QJsonDocument>
      11              : #include <QJsonObject>
      12              : 
      13              : #include <iostream>
      14              : 
      15              : DOKIT_USE_STRINGLITERALS
      16              : 
      17              : /*!
      18              :  * \class LoggerStartCommand
      19              :  *
      20              :  * The LoggerStartCommand class implements the `logger` CLI command.
      21              :  */
      22              : 
      23              : /*!
      24              :  * Construct a new LoggerStartCommand object with \a parent.
      25              :  */
      26         4428 : LoggerStartCommand::LoggerStartCommand(QObject * const parent) : DeviceCommand(parent)
      27         2997 : {
      28              : 
      29         4887 : }
      30              : 
      31         3150 : QStringList LoggerStartCommand::requiredOptions(const QCommandLineParser &parser) const
      32         4995 : {
      33        16470 :     return DeviceCommand::requiredOptions(parser) + QStringList{
      34         4995 :         u"mode"_s,
      35        12915 :     };
      36         4995 : }
      37              : 
      38         1540 : QStringList LoggerStartCommand::supportedOptions(const QCommandLineParser &parser) const
      39         2442 : {
      40        11044 :     return DeviceCommand::supportedOptions(parser) + QStringList{
      41         2442 :         u"interval"_s,
      42         2442 :         u"range"_s, // May still be required by processOptions(), depending on the --mode option's value.
      43         2442 :         u"timestamp"_s,
      44         9394 :     };
      45         2442 : }
      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         1470 : QStringList LoggerStartCommand::processOptions(const QCommandLineParser &parser)
      54         2331 : {
      55         3801 :     QStringList errors = DeviceCommand::processOptions(parser);
      56         3801 :     if (!errors.isEmpty()) {
      57          222 :         return errors;
      58          222 :     }
      59              : 
      60              :     // Parse the (required) mode option.
      61         4769 :     if (const QString mode = parser.value(u"mode"_s).trimmed().toLower();
      62         5643 :         mode.startsWith(u"ac v"_s) || mode.startsWith(u"vac"_s)) {
      63          181 :         settings.mode = DataLoggerService::Mode::AcVoltage;
      64          181 :         minRangeFunc = minVoltageRange;
      65         5346 :     } else if (mode.startsWith(u"dc v"_s) || mode.startsWith(u"vdc"_s)) {
      66         2353 :         settings.mode = DataLoggerService::Mode::DcVoltage;
      67         2353 :         minRangeFunc = minVoltageRange;
      68         2373 :     } else if (mode.startsWith(u"ac c"_s) || mode.startsWith(u"aac"_s)) {
      69          181 :         settings.mode = DataLoggerService::Mode::AcCurrent;
      70          181 :         minRangeFunc = minCurrentRange;
      71         1188 :     } else if (mode.startsWith(u"dc c"_s) || mode.startsWith(u"adc"_s)) {
      72          181 :         settings.mode = DataLoggerService::Mode::DcCurrent;
      73          181 :         minRangeFunc = minCurrentRange;
      74          681 :     } else if (mode.startsWith(u"temp"_s)) {
      75          362 :         settings.mode = DataLoggerService::Mode::Temperature;
      76          362 :         minRangeFunc = nullptr;
      77          222 :     } else {
      78          181 :         minRangeFunc = nullptr;
      79          269 :         errors.append(tr("Unknown logger mode: %1").arg(parser.value(u"mode"_s)));
      80          111 :         return errors;
      81          567 :     }
      82              : 
      83              :     // Parse the range option.
      84         3258 :     rangeOptionValue = 0;
      85         4086 :     if (parser.isSet(u"range"_s)) {
      86         3077 :         const QString value = parser.value(u"range"_s);
      87         3077 :         switch (settings.mode) {
      88         2242 :         case DataLoggerService::Mode::DcVoltage:
      89         1443 :         case DataLoggerService::Mode::AcVoltage:
      90         2353 :             rangeOptionValue = parseNumber<std::milli>(value, u"V"_s, 50); // mV.
      91         2353 :             break;
      92          251 :         case DataLoggerService::Mode::DcCurrent:
      93          222 :         case DataLoggerService::Mode::AcCurrent:
      94          362 :             rangeOptionValue = parseNumber<std::milli>(value, u"A"_s, 5); // mA.
      95          362 :             break;
      96          362 :         default:
      97          662 :             qCInfo(lc).noquote() << tr("Ignoring range value: %1").arg(value);
      98         1887 :         }
      99         3077 :         if ((minRangeFunc != nullptr) && (rangeOptionValue == 0)) {
     100          227 :             errors.append(tr("Invalid range value: %1").arg(value));
     101          111 :         }
     102         2365 :     } else if (settings.mode != DataLoggerService::Mode::Temperature) {
     103          269 :         errors.append(tr("Missing required option for logger mode '%1': range").arg(parser.value(u"mode"_s)));
     104          111 :     }
     105              : 
     106              :     // Parse the interval option.
     107         4086 :     if (parser.isSet(u"interval"_s)) {
     108         1180 :         const QString value = parser.value(u"interval"_s);
     109          905 :         const quint32 interval = parseNumber<std::milli>(value, u"s"_s, 500);
     110          905 :         if (interval == 0) {
     111          454 :             errors.append(tr("Invalid interval value: %1").arg(value));
     112          333 :         } else {
     113          543 :             settings.updateInterval = interval;
     114          333 :         }
     115          675 :     }
     116              : 
     117              :     // Parse the timestamp option.
     118         3258 :     settings.timestamp = (quint32)QDateTime::currentSecsSinceEpoch(); // Note, subject to Y2038 epochalypse.
     119         4086 :     if (parser.isSet(u"timestamp"_s)) {
     120          905 :         const QString value = parser.value(u"timestamp"_s);
     121          905 :         QLocale locale; bool ok;
     122          555 :         static_assert(sizeof(uint) == sizeof(settings.timestamp), "QLocale has no toUint32().");
     123          675 :         const int timestamp = locale.toUInt(value, &ok);
     124          905 :         if (!ok) {
     125          454 :             errors.append(tr("Invalid timestamp value: %1").arg(value));
     126          333 :         } else {
     127          543 :             settings.timestamp = timestamp;
     128          333 :         }
     129          905 :     }
     130         1998 :     return errors;
     131         1998 : }
     132              : 
     133              : /*!
     134              :  * \copybrief DeviceCommand::getService
     135              :  *
     136              :  * This override returns a pointer to a DataLoggerService object.
     137              :  */
     138            0 : AbstractPokitService * LoggerStartCommand::getService()
     139            0 : {
     140            0 :     Q_ASSERT(device);
     141            0 :     if (!service) {
     142            0 :         service = device->dataLogger();
     143            0 :         Q_ASSERT(service);
     144            0 :         connect(service, &DataLoggerService::settingsWritten, this, &LoggerStartCommand::settingsWritten);
     145            0 :     }
     146            0 :     return service;
     147            0 : }
     148              : 
     149              : /*!
     150              :  * \copybrief DeviceCommand::serviceDetailsDiscovered
     151              :  *
     152              :  * This override fetches the current device's status, and outputs it in the selected format.
     153              :  */
     154            0 : void LoggerStartCommand::serviceDetailsDiscovered()
     155            0 : {
     156            0 :     DeviceCommand::serviceDetailsDiscovered(); // Just logs consistently.
     157            0 :     settings.range = (minRangeFunc == nullptr) ? 0 : minRangeFunc(*service->pokitProduct(), rangeOptionValue);
     158            0 :     const QString range = service->toString(settings.range, settings.mode);
     159            0 :     qCInfo(lc).noquote() << tr("Logging %1, with range %2, every %L3ms.").arg(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 discovered,
     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          210 : void LoggerStartCommand::settingsWritten()
     182          333 : {
     183          645 :     qCDebug(lc).noquote() << tr("Settings written; data logger has started.");
     184          543 :     switch (format) {
     185          181 :     case OutputFormat::Csv:
     186          194 :         std::cout << qUtf8Printable(tr("logger_start_result\nsuccess\n"));
     187          181 :         break;
     188          181 :     case OutputFormat::Json:
     189          194 :         std::cout << qUtf8Printable(u"true\n"_s);
     190          181 :         break;
     191          181 :     case OutputFormat::Text:
     192          194 :         std::cout << qUtf8Printable(tr("Done.\n"));
     193          181 :         break;
     194          333 :     }
     195          543 :     if (device) disconnect(); // Will exit the application once disconnected.
     196          543 : }
        

Generated by: LCOV version 2.4-0