Line data Source code
1 : // SPDX-FileCopyrightText: 2022-2023 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 486 : LoggerStartCommand::LoggerStartCommand(QObject * const parent) : DeviceCommand(parent) 24 : { 25 : 26 486 : } 27 : 28 810 : QStringList LoggerStartCommand::requiredOptions(const QCommandLineParser &parser) const 29 : { 30 2700 : return DeviceCommand::requiredOptions(parser) + QStringList{ 31 : QLatin1String("mode"), 32 2385 : }; 33 : } 34 : 35 396 : QStringList LoggerStartCommand::supportedOptions(const QCommandLineParser &parser) const 36 : { 37 2112 : 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 1958 : }; 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 378 : QStringList LoggerStartCommand::processOptions(const QCommandLineParser &parser) 51 : { 52 378 : QStringList errors = DeviceCommand::processOptions(parser); 53 378 : if (!errors.isEmpty()) { 54 : return errors; 55 : } 56 : 57 : // Parse the (required) mode option. 58 684 : const QString mode = parser.value(QLatin1String("mode")).trimmed().toLower(); 59 342 : if (mode.startsWith(QLatin1String("ac v")) || mode.startsWith(QLatin1String("vac"))) { 60 18 : settings.mode = DataLoggerService::Mode::AcVoltage; 61 18 : minRangeFunc = minVoltageRange; 62 324 : } else if (mode.startsWith(QLatin1String("dc v")) || mode.startsWith(QLatin1String("vdc"))) { 63 234 : settings.mode = DataLoggerService::Mode::DcVoltage; 64 234 : minRangeFunc = minVoltageRange; 65 90 : } else if (mode.startsWith(QLatin1String("ac c")) || mode.startsWith(QLatin1String("aac"))) { 66 18 : settings.mode = DataLoggerService::Mode::AcCurrent; 67 18 : minRangeFunc = minCurrentRange; 68 72 : } else if (mode.startsWith(QLatin1String("dc c")) || mode.startsWith(QLatin1String("adc"))) { 69 18 : settings.mode = DataLoggerService::Mode::DcCurrent; 70 18 : minRangeFunc = minCurrentRange; 71 54 : } else if (mode.startsWith(QLatin1String("temp"))) { 72 36 : settings.mode = DataLoggerService::Mode::Temperature; 73 36 : minRangeFunc = nullptr; 74 : } else { 75 18 : minRangeFunc = nullptr; 76 36 : errors.append(tr("Unknown logger mode: %1").arg(parser.value(QLatin1String("mode")))); 77 18 : return errors; 78 : } 79 : 80 : // Parse the range option. 81 324 : rangeOptionValue = 0; 82 396 : if (parser.isSet(QLatin1String("range"))) { 83 306 : const QString value = parser.value(QLatin1String("range")); 84 306 : switch (settings.mode) { 85 : case DataLoggerService::Mode::DcVoltage: 86 : case DataLoggerService::Mode::AcVoltage: 87 234 : rangeOptionValue = parseNumber<std::milli>(value, QLatin1String("V"), 50); // mV. 88 234 : break; 89 : case DataLoggerService::Mode::DcCurrent: 90 : case DataLoggerService::Mode::AcCurrent: 91 36 : rangeOptionValue = parseNumber<std::milli>(value, QLatin1String("A"), 5); // mA. 92 36 : break; 93 36 : default: 94 80 : qCInfo(lc).noquote() << tr("Ignoring range value: %1").arg(value); 95 : } 96 306 : if ((minRangeFunc != nullptr) && (rangeOptionValue == 0)) { 97 22 : errors.append(tr("Invalid range value: %1").arg(value)); 98 : } 99 256 : } else if (settings.mode != DataLoggerService::Mode::Temperature) { 100 40 : errors.append(tr("Missing required option for logger mode '%1': range") 101 36 : .arg(parser.value(QLatin1String("mode")))); 102 : } 103 : 104 : // Parse the interval option. 105 396 : if (parser.isSet(QLatin1String("interval"))) { 106 150 : const QString value = parser.value(QLatin1String("interval")); 107 90 : const quint32 interval = parseNumber<std::milli>(value, QLatin1String("s"), 500); 108 90 : if (interval == 0) { 109 44 : errors.append(tr("Invalid interval value: %1").arg(value)); 110 : } else { 111 54 : settings.updateInterval = interval; 112 : } 113 70 : } 114 : 115 : // Parse the timestamp option. 116 324 : settings.timestamp = (quint32)QDateTime::currentSecsSinceEpoch(); // Note, subject to Y2038 epochalypse. 117 396 : if (parser.isSet(QLatin1String("timestamp"))) { 118 90 : const QString value = parser.value(QLatin1String("timestamp")); 119 90 : 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 90 : if (!ok) { 123 44 : errors.append(tr("Invalid timestamp value: %1").arg(value)); 124 : } else { 125 54 : settings.timestamp = timestamp; 126 : } 127 90 : } 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 : 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 54 : void LoggerStartCommand::settingsWritten() 182 : { 183 54 : qCDebug(lc).noquote() << tr("Settings written; data logger has started."); 184 54 : switch (format) { 185 18 : case OutputFormat::Csv: 186 22 : std::cout << qUtf8Printable(tr("logger_start_result\nsuccess\n")); 187 18 : break; 188 : case OutputFormat::Json: 189 26 : std::cout << qUtf8Printable(QLatin1String("true\n")); 190 18 : break; 191 18 : case OutputFormat::Text: 192 22 : std::cout << qUtf8Printable(tr("Done.\n")); 193 18 : break; 194 : } 195 54 : if (device) disconnect(); // Will exit the application once disconnected. 196 54 : }