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         2214 : LoggerStartCommand::LoggerStartCommand(QObject * const parent) : DeviceCommand(parent)
      27         1404 : {
      28              : 
      29         2619 : }
      30              : 
      31         2025 : QStringList LoggerStartCommand::requiredOptions(const QCommandLineParser &parser) const
      32         2340 : {
      33         9945 :     return DeviceCommand::requiredOptions(parser) + QStringList{
      34         2340 :         u"mode"_s,
      35         7830 :     };
      36         2340 : }
      37              : 
      38          990 : QStringList LoggerStartCommand::supportedOptions(const QCommandLineParser &parser) const
      39         1144 : {
      40         6666 :     return DeviceCommand::supportedOptions(parser) + QStringList{
      41         1144 :         u"interval"_s,
      42         1144 :         u"range"_s, // May still be required by processOptions(), depending on the --mode option's value.
      43         1144 :         u"timestamp"_s,
      44         5808 :     };
      45         1144 : }
      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          945 : QStringList LoggerStartCommand::processOptions(const QCommandLineParser &parser)
      54         1092 : {
      55         2037 :     QStringList errors = DeviceCommand::processOptions(parser);
      56         2037 :     if (!errors.isEmpty()) {
      57          104 :         return errors;
      58          104 :     }
      59              : 
      60              :     // Parse the (required) mode option.
      61         2698 :     if (const QString mode = parser.value(u"mode"_s).trimmed().toLower();
      62         3097 :         mode.startsWith(u"ac v"_s) || mode.startsWith(u"vac"_s)) {
      63           97 :         settings.mode = DataLoggerService::Mode::AcVoltage;
      64           97 :         minRangeFunc = minVoltageRange;
      65         2934 :     } else if (mode.startsWith(u"dc v"_s) || mode.startsWith(u"vdc"_s)) {
      66         1261 :         settings.mode = DataLoggerService::Mode::DcVoltage;
      67         1261 :         minRangeFunc = minVoltageRange;
      68         1231 :     } else if (mode.startsWith(u"ac c"_s) || mode.startsWith(u"aac"_s)) {
      69           97 :         settings.mode = DataLoggerService::Mode::AcCurrent;
      70           97 :         minRangeFunc = minCurrentRange;
      71          652 :     } else if (mode.startsWith(u"dc c"_s) || mode.startsWith(u"adc"_s)) {
      72           97 :         settings.mode = DataLoggerService::Mode::DcCurrent;
      73           97 :         minRangeFunc = minCurrentRange;
      74          354 :     } else if (mode.startsWith(u"temp"_s)) {
      75          194 :         settings.mode = DataLoggerService::Mode::Temperature;
      76          194 :         minRangeFunc = nullptr;
      77          104 :     } else {
      78           97 :         minRangeFunc = nullptr;
      79          148 :         errors.append(tr("Unknown logger mode: %1").arg(parser.value(u"mode"_s)));
      80           52 :         return errors;
      81          508 :     }
      82              : 
      83              :     // Parse the range option.
      84         1746 :     rangeOptionValue = 0;
      85         2124 :     if (parser.isSet(u"range"_s)) {
      86         1649 :         const QString value = parser.value(u"range"_s);
      87         1649 :         switch (settings.mode) {
      88         1209 :         case DataLoggerService::Mode::DcVoltage:
      89          676 :         case DataLoggerService::Mode::AcVoltage:
      90         1261 :             rangeOptionValue = parseNumber<std::milli>(value, u"V"_s, 50); // mV.
      91         1261 :             break;
      92          142 :         case DataLoggerService::Mode::DcCurrent:
      93          104 :         case DataLoggerService::Mode::AcCurrent:
      94          194 :             rangeOptionValue = parseNumber<std::milli>(value, u"A"_s, 5); // mA.
      95          194 :             break;
      96          194 :         default:
      97          352 :             qCInfo(lc).noquote() << tr("Ignoring range value: %1").arg(value);
      98          884 :         }
      99         1649 :         if ((minRangeFunc != nullptr) && (rangeOptionValue == 0)) {
     100          118 :             errors.append(tr("Invalid range value: %1").arg(value));
     101           52 :         }
     102         1337 :     } else if (settings.mode != DataLoggerService::Mode::Temperature) {
     103          148 :         errors.append(tr("Missing required option for logger mode '%1': range").arg(parser.value(u"mode"_s)));
     104           52 :     }
     105              : 
     106              :     // Parse the interval option.
     107         2124 :     if (parser.isSet(u"interval"_s)) {
     108          590 :         const QString value = parser.value(u"interval"_s);
     109          485 :         const quint32 interval = parseNumber<std::milli>(value, u"s"_s, 500);
     110          485 :         if (interval == 0) {
     111          236 :             errors.append(tr("Invalid interval value: %1").arg(value));
     112          156 :         } else {
     113          291 :             settings.updateInterval = interval;
     114          156 :         }
     115          380 :     }
     116              : 
     117              :     // Parse the timestamp option.
     118         1746 :     settings.timestamp = (quint32)QDateTime::currentSecsSinceEpoch(); // Note, subject to Y2038 epochalypse.
     119         2124 :     if (parser.isSet(u"timestamp"_s)) {
     120          485 :         const QString value = parser.value(u"timestamp"_s);
     121          485 :         QLocale locale; bool ok;
     122          260 :         static_assert(sizeof(uint) == sizeof(settings.timestamp), "QLocale has no toUint32().");
     123          380 :         const int timestamp = locale.toUInt(value, &ok);
     124          485 :         if (!ok) {
     125          236 :             errors.append(tr("Invalid timestamp value: %1").arg(value));
     126          156 :         } else {
     127          291 :             settings.timestamp = timestamp;
     128          156 :         }
     129          485 :     }
     130          936 :     return errors;
     131          936 : }
     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          135 : void LoggerStartCommand::settingsWritten()
     182          156 : {
     183          330 :     qCDebug(lc).noquote() << tr("Settings written; data logger has started.");
     184          291 :     switch (format) {
     185           97 :     case OutputFormat::Csv:
     186           97 :         std::cout << qUtf8Printable(tr("logger_start_result\nsuccess\n"));
     187           97 :         break;
     188           97 :     case OutputFormat::Json:
     189           97 :         std::cout << qUtf8Printable(u"true\n"_s);
     190           97 :         break;
     191           97 :     case OutputFormat::Text:
     192           97 :         std::cout << qUtf8Printable(tr("Done.\n"));
     193           97 :         break;
     194          156 :     }
     195          291 :     if (device) disconnect(); // Will exit the application once disconnected.
     196          291 : }
        

Generated by: LCOV version 2.3.1-1