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-2025 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         3240 : LoggerStartCommand::LoggerStartCommand(QObject * const parent) : DeviceCommand(parent)
      27         1512 : {
      28              : 
      29         3672 : }
      30              : 
      31         3600 : QStringList LoggerStartCommand::requiredOptions(const QCommandLineParser &parser) const
      32         2520 : {
      33        15390 :     return DeviceCommand::requiredOptions(parser) + QStringList{
      34         2520 :         u"mode"_s,
      35        12015 :     };
      36         2520 : }
      37              : 
      38         1760 : QStringList LoggerStartCommand::supportedOptions(const QCommandLineParser &parser) const
      39         1232 : {
      40        10780 :     return DeviceCommand::supportedOptions(parser) + QStringList{
      41         1232 :         u"interval"_s,
      42         1232 :         u"range"_s, // May still be required by processOptions(), depending on the --mode option's value.
      43         1232 :         u"timestamp"_s,
      44         9394 :     };
      45         1232 : }
      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         1680 : QStringList LoggerStartCommand::processOptions(const QCommandLineParser &parser)
      54         1176 : {
      55         2856 :     QStringList errors = DeviceCommand::processOptions(parser);
      56         2856 :     if (!errors.isEmpty()) {
      57          112 :         return errors;
      58          112 :     }
      59              : 
      60              :     // Parse the (required) mode option.
      61         4104 :     if (const QString mode = parser.value(u"mode"_s).trimmed().toLower();
      62         4921 :         mode.startsWith(u"ac v"_s) || mode.startsWith(u"vac"_s)) {
      63          136 :         settings.mode = DataLoggerService::Mode::AcVoltage;
      64          136 :         minRangeFunc = minVoltageRange;
      65         4662 :     } else if (mode.startsWith(u"dc v"_s) || mode.startsWith(u"vdc"_s)) {
      66         1768 :         settings.mode = DataLoggerService::Mode::DcVoltage;
      67         1768 :         minRangeFunc = minVoltageRange;
      68         1743 :     } else if (mode.startsWith(u"ac c"_s) || mode.startsWith(u"aac"_s)) {
      69          136 :         settings.mode = DataLoggerService::Mode::AcCurrent;
      70          136 :         minRangeFunc = minCurrentRange;
      71         1036 :     } else if (mode.startsWith(u"dc c"_s) || mode.startsWith(u"adc"_s)) {
      72          136 :         settings.mode = DataLoggerService::Mode::DcCurrent;
      73          136 :         minRangeFunc = minCurrentRange;
      74          537 :     } else if (mode.startsWith(u"temp"_s)) {
      75          272 :         settings.mode = DataLoggerService::Mode::Temperature;
      76          272 :         minRangeFunc = nullptr;
      77          112 :     } else {
      78          136 :         minRangeFunc = nullptr;
      79          229 :         errors.append(tr("Unknown logger mode: %1").arg(parser.value(u"mode"_s)));
      80           56 :         return errors;
      81          759 :     }
      82              : 
      83              :     // Parse the range option.
      84         2448 :     rangeOptionValue = 0;
      85         3222 :     if (parser.isSet(u"range"_s)) {
      86         2312 :         const QString value = parser.value(u"range"_s);
      87         2312 :         switch (settings.mode) {
      88         1712 :         case DataLoggerService::Mode::DcVoltage:
      89          728 :         case DataLoggerService::Mode::AcVoltage:
      90         1768 :             rangeOptionValue = parseNumber<std::milli>(value, u"V"_s, 50); // mV.
      91         1768 :             break;
      92          216 :         case DataLoggerService::Mode::DcCurrent:
      93          112 :         case DataLoggerService::Mode::AcCurrent:
      94          272 :             rangeOptionValue = parseNumber<std::milli>(value, u"A"_s, 5); // mA.
      95          272 :             break;
      96          272 :         default:
      97          576 :             qCInfo(lc).noquote() << tr("Ignoring range value: %1").arg(value);
      98          952 :         }
      99         2312 :         if ((minRangeFunc != nullptr) && (rangeOptionValue == 0)) {
     100          179 :             errors.append(tr("Invalid range value: %1").arg(value));
     101           56 :         }
     102         1661 :     } else if (settings.mode != DataLoggerService::Mode::Temperature) {
     103          229 :         errors.append(tr("Missing required option for logger mode '%1': range").arg(parser.value(u"mode"_s)));
     104           56 :     }
     105              : 
     106              :     // Parse the interval option.
     107         3222 :     if (parser.isSet(u"interval"_s)) {
     108          940 :         const QString value = parser.value(u"interval"_s);
     109          680 :         const quint32 interval = parseNumber<std::milli>(value, u"s"_s, 500);
     110          680 :         if (interval == 0) {
     111          358 :             errors.append(tr("Invalid interval value: %1").arg(value));
     112          168 :         } else {
     113          408 :             settings.updateInterval = interval;
     114          168 :         }
     115          465 :     }
     116              : 
     117              :     // Parse the timestamp option.
     118         2448 :     settings.timestamp = (quint32)QDateTime::currentSecsSinceEpoch(); // Note, subject to Y2038 epochalypse.
     119         3222 :     if (parser.isSet(u"timestamp"_s)) {
     120          680 :         const QString value = parser.value(u"timestamp"_s);
     121          680 :         QLocale locale; bool ok;
     122          280 :         static_assert(sizeof(uint) == sizeof(settings.timestamp), "QLocale has no toUint32().");
     123          465 :         const int timestamp = locale.toUInt(value, &ok);
     124          680 :         if (!ok) {
     125          358 :             errors.append(tr("Invalid timestamp value: %1").arg(value));
     126          168 :         } else {
     127          408 :             settings.timestamp = timestamp;
     128          168 :         }
     129          680 :     }
     130         1008 :     return errors;
     131         1008 : }
     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          240 : void LoggerStartCommand::settingsWritten()
     182          168 : {
     183          495 :     qCDebug(lc).noquote() << tr("Settings written; data logger has started.");
     184          408 :     switch (format) {
     185          136 :     case OutputFormat::Csv:
     186          148 :         std::cout << qUtf8Printable(tr("logger_start_result\nsuccess\n"));
     187          136 :         break;
     188          136 :     case OutputFormat::Json:
     189          148 :         std::cout << qUtf8Printable(u"true\n"_s);
     190          136 :         break;
     191          136 :     case OutputFormat::Text:
     192          148 :         std::cout << qUtf8Printable(tr("Done.\n"));
     193          136 :         break;
     194          168 :     }
     195          408 :     if (device) disconnect(); // Will exit the application once disconnected.
     196          408 : }
        

Generated by: LCOV version 2.3.1-1