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 "loggerfetchcommand.h"
5 :
6 : #include <qtpokit/pokitdevice.h>
7 :
8 : #include <QDateTime>
9 : #include <QJsonDocument>
10 : #include <QJsonObject>
11 :
12 : #include <iostream>
13 :
14 : // Qt 6.5.0 added new QDateTime::fromSecsSinceEpoch() and fromMSecsSinceEpoch()
15 : // overloads, then Qt 6.6.0 deprectated some of of the older ones.
16 : #if (QT_VERSION < QT_VERSION_CHECK(6, 5, 0))
17 4032 : #define DOKIT_QT_UTC Qt::UTC
18 : #else
19 : #include <QTimeZone>
20 1680 : #define DOKIT_QT_UTC QTimeZone::UTC
21 : #endif
22 :
23 : /*!
24 : * \class LoggerFetchCommand
25 : *
26 : * The LoggerFetchCommand class implements the `logger` CLI command.
27 : */
28 :
29 : /*!
30 : * Construct a new LoggerFetchCommand object with \a parent.
31 : */
32 4389 : LoggerFetchCommand::LoggerFetchCommand(QObject * const parent) : DeviceCommand(parent)
33 1369 : {
34 :
35 4449 : }
36 :
37 : /*!
38 : * \copybrief DeviceCommand::getService
39 : *
40 : * This override returns a pointer to a DataLoggerService object.
41 : */
42 0 : AbstractPokitService * LoggerFetchCommand::getService()
43 0 : {
44 0 : Q_ASSERT(device);
45 0 : if (!service) {
46 0 : service = device->dataLogger();
47 0 : Q_ASSERT(service);
48 0 : connect(service, &DataLoggerService::metadataRead, this, &LoggerFetchCommand::metadataRead);
49 0 : connect(service, &DataLoggerService::samplesRead, this, &LoggerFetchCommand::outputSamples);
50 0 : }
51 0 : return service;
52 0 : }
53 :
54 : /*!
55 : * \copybrief DeviceCommand::serviceDetailsDiscovered
56 : *
57 : * This override fetches the current device's status, and outputs it in the selected format.
58 : */
59 0 : void LoggerFetchCommand::serviceDetailsDiscovered()
60 0 : {
61 0 : DeviceCommand::serviceDetailsDiscovered(); // Just logs consistently.
62 0 : qCInfo(lc).noquote() << tr("Fetching logger samples...");
63 0 : service->enableMetadataNotifications();
64 0 : service->enableReadingNotifications();
65 0 : service->fetchSamples();
66 0 : }
67 :
68 : /*!
69 : * Invoked when \a metadata has been received from the data logger.
70 : */
71 3040 : void LoggerFetchCommand::metadataRead(const DataLoggerService::Metadata &data)
72 1322 : {
73 5122 : qCDebug(lc) << "status:" << (int)(data.status);
74 5122 : qCDebug(lc) << "scale:" << data.scale;
75 5122 : qCDebug(lc) << "mode:" << DataLoggerService::toString(data.mode) << (quint8)data.mode;
76 5122 : qCDebug(lc) << "range:" << service->toString(data.range, data.mode) << data.range;
77 5122 : qCDebug(lc) << "updateInterval:" << (int)data.updateInterval;
78 5122 : qCDebug(lc) << "numberOfSamples:" << data.numberOfSamples;
79 5122 : qCDebug(lc) << "timestamp:" << data.timestamp << QDateTime::fromSecsSinceEpoch(data.timestamp, DOKIT_QT_UTC);
80 4362 : this->metadata = data;
81 4362 : this->samplesToGo = data.numberOfSamples;
82 4362 : this->timestamp = (quint64)data.timestamp * (quint64)1000;
83 8162 : qCInfo(lc).noquote() << tr("Fetching %Ln logger sample/s...", nullptr, data.numberOfSamples);
84 4362 : }
85 :
86 : /*!
87 : * Outputs logger \a samples in the selected ouput format.
88 : */
89 3600 : void LoggerFetchCommand::outputSamples(const DataLoggerService::Samples &samples)
90 1530 : {
91 3060 : QString unit;
92 5130 : switch (metadata.mode) {
93 1026 : case DataLoggerService::Mode::DcVoltage: unit = QLatin1String("Vdc"); break;
94 1026 : case DataLoggerService::Mode::AcVoltage: unit = QLatin1String("Vac"); break;
95 1026 : case DataLoggerService::Mode::DcCurrent: unit = QLatin1String("Adc"); break;
96 1026 : case DataLoggerService::Mode::AcCurrent: unit = QLatin1String("Aac"); break;
97 1332 : case DataLoggerService::Mode::Temperature: unit = QString::fromUtf8("°C"); break;
98 0 : default:
99 0 : qCDebug(lc).noquote() << tr(R"(No known unit for mode %1 "%2".)").arg((int)metadata.mode)
100 0 : .arg(DataLoggerService::toString(metadata.mode));
101 1530 : }
102 7200 : const QString range = service->toString(metadata.range, metadata.mode);
103 :
104 27540 : for (const qint16 &sample: samples) {
105 23940 : const QString timeString = (metadata.timestamp == 0) ? QString::number(timestamp)
106 23940 : : QDateTime::fromMSecsSinceEpoch(timestamp, DOKIT_QT_UTC).toString(Qt::ISODateWithMs);
107 23940 : const float value = sample * metadata.scale;
108 23940 : switch (format) {
109 2380 : case OutputFormat::Csv:
110 9120 : for (; showCsvHeader; showCsvHeader = false) {
111 1480 : std::cout << qUtf8Printable(tr("timestamp,value,unit,range\n"));
112 340 : }
113 15960 : std::cout << qUtf8Printable(QString::fromLatin1("%1,%2,%3,%4\n")
114 2380 : .arg(timeString).arg(value).arg(unit, range));
115 7980 : break;
116 7980 : case OutputFormat::Json: {
117 2380 : QJsonObject object{
118 4760 : { QLatin1String("timestamp"), timeString },
119 4760 : { QLatin1String("value"), value },
120 4760 : { QLatin1String("unit"), unit },
121 11200 : { QLatin1String("mode"), DataLoggerService::toString(metadata.mode) },
122 39060 : };
123 7980 : if (!range.isEmpty()) {
124 8624 : object.insert(QLatin1String("range"), range);
125 1904 : }
126 13580 : std::cout << QJsonDocument(object).toJson().toStdString();
127 7980 : } break;
128 7980 : case OutputFormat::Text:
129 15680 : std::cout << qUtf8Printable(tr("%1 %2 %3\n").arg(timeString).arg(value).arg(unit));
130 7980 : break;
131 7140 : }
132 23940 : timestamp += metadata.updateInterval;
133 23940 : --samplesToGo;
134 16800 : }
135 5130 : if (samplesToGo <= 0) {
136 11700 : qCInfo(lc).noquote() << tr("Finished fetching %Ln sample/s (with %L1 remaining).",
137 8730 : nullptr, metadata.numberOfSamples).arg(samplesToGo);
138 5130 : if (device) disconnect(); // Will exit the application once disconnected.
139 1530 : }
140 29770 : }
|