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 : #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 : // Qt 6.5.0 added new QDateTime::fromSecsSinceEpoch() and fromMSecsSinceEpoch()
16 : // overloads, then Qt 6.6.0 deprecated some of of the older ones.
17 : #if (QT_VERSION < QT_VERSION_CHECK(6, 5, 0))
18 4704 : #define DOKIT_QT_UTC Qt::UTC
19 : #else
20 : #include <QTimeZone>
21 2688 : #define DOKIT_QT_UTC QTimeZone::UTC
22 : #endif
23 :
24 : DOKIT_USE_STRINGLITERALS
25 :
26 : /*!
27 : * \class LoggerFetchCommand
28 : *
29 : * The LoggerFetchCommand class implements the `logger` CLI command.
30 : */
31 :
32 : /*!
33 : * Construct a new LoggerFetchCommand object with \a parent.
34 : */
35 5189 : LoggerFetchCommand::LoggerFetchCommand(QObject * const parent) : DeviceCommand(parent)
36 1754 : {
37 :
38 5219 : }
39 :
40 : /*!
41 : * \copybrief DeviceCommand::getService
42 : *
43 : * This override returns a pointer to a DataLoggerService object.
44 : */
45 0 : AbstractPokitService * LoggerFetchCommand::getService()
46 0 : {
47 0 : Q_ASSERT(device);
48 0 : if (!service) {
49 0 : service = device->dataLogger();
50 0 : Q_ASSERT(service);
51 0 : connect(service, &DataLoggerService::metadataRead, this, &LoggerFetchCommand::metadataRead);
52 0 : connect(service, &DataLoggerService::samplesRead, this, &LoggerFetchCommand::outputSamples);
53 0 : }
54 0 : return service;
55 0 : }
56 :
57 : /*!
58 : * \copybrief DeviceCommand::serviceDetailsDiscovered
59 : *
60 : * This override fetches the current device's status, and outputs it in the selected format.
61 : */
62 0 : void LoggerFetchCommand::serviceDetailsDiscovered()
63 0 : {
64 0 : DeviceCommand::serviceDetailsDiscovered(); // Just logs consistently.
65 0 : qCInfo(lc).noquote() << tr("Fetching logger samples...");
66 0 : service->enableMetadataNotifications();
67 0 : service->enableReadingNotifications();
68 0 : service->fetchSamples();
69 0 : }
70 :
71 : /*!
72 : * Invoked when \a metadata has been received from the data logger.
73 : */
74 3420 : void LoggerFetchCommand::metadataRead(const DataLoggerService::Metadata &data)
75 1702 : {
76 6110 : qCDebug(lc) << "status:" << (int)(data.status);
77 6110 : qCDebug(lc) << "scale:" << data.scale;
78 6110 : qCDebug(lc) << "mode:" << DataLoggerService::toString(data.mode) << (quint8)data.mode;
79 6110 : qCDebug(lc) << "range:" << service->toString(data.range, data.mode) << data.range;
80 6110 : qCDebug(lc) << "updateInterval:" << (int)data.updateInterval;
81 6110 : qCDebug(lc) << "numberOfSamples:" << data.numberOfSamples;
82 6110 : qCDebug(lc) << "timestamp:" << data.timestamp << QDateTime::fromSecsSinceEpoch(data.timestamp, DOKIT_QT_UTC);
83 5122 : this->metadata = data;
84 5122 : this->samplesToGo = data.numberOfSamples;
85 5122 : this->timestamp = (quint64)data.timestamp * (quint64)1000;
86 9530 : qCInfo(lc).noquote() << tr("Fetching %Ln logger sample/s...", nullptr, data.numberOfSamples);
87 5122 : }
88 :
89 : /*!
90 : * Outputs logger \a samples in the selected output format.
91 : */
92 4050 : void LoggerFetchCommand::outputSamples(const DataLoggerService::Samples &samples)
93 1980 : {
94 3870 : QString unit;
95 6030 : switch (metadata.mode) {
96 1584 : case DataLoggerService::Mode::DcVoltage: unit = u"Vdc"_s; break;
97 1584 : case DataLoggerService::Mode::AcVoltage: unit = u"Vac"_s; break;
98 1584 : case DataLoggerService::Mode::DcCurrent: unit = u"Adc"_s; break;
99 1584 : case DataLoggerService::Mode::AcCurrent: unit = u"Aac"_s; break;
100 1584 : case DataLoggerService::Mode::Temperature: unit = QString::fromUtf8("°C"); break;
101 0 : default:
102 0 : qCDebug(lc).noquote() << tr(R"(No known unit for mode %1 "%2".)").arg((int)metadata.mode)
103 0 : .arg(DataLoggerService::toString(metadata.mode));
104 1980 : }
105 8190 : const QString range = service->toString(metadata.range, metadata.mode);
106 :
107 32190 : for (const qint16 &sample: samples) {
108 28140 : const QString timeString = (metadata.timestamp == 0) ? QString::number(timestamp)
109 28140 : : QDateTime::fromMSecsSinceEpoch(timestamp, DOKIT_QT_UTC).toString(Qt::ISODateWithMs);
110 28140 : const float value = sample * metadata.scale;
111 28140 : switch (format) {
112 3080 : case OutputFormat::Csv:
113 10720 : for (; showCsvHeader; showCsvHeader = false) {
114 1760 : std::cout << qUtf8Printable(tr("timestamp,value,unit,range\n"));
115 440 : }
116 18620 : std::cout << qUtf8Printable(QString::fromLatin1("%1,%2,%3,%4\n")
117 3080 : .arg(timeString).arg(value).arg(unit, range));
118 9380 : break;
119 9380 : case OutputFormat::Json: {
120 3080 : QJsonObject object{
121 12740 : { u"timestamp"_s, timeString },
122 12740 : { u"value"_s, value },
123 12740 : { u"unit"_s, unit },
124 15680 : { u"mode"_s, DataLoggerService::toString(metadata.mode) },
125 46760 : };
126 9380 : if (!range.isEmpty()) {
127 13888 : object.insert(u"range"_s, range);
128 2464 : }
129 15680 : std::cout << QJsonDocument(object).toJson().toStdString();
130 9380 : } break;
131 9380 : case OutputFormat::Text:
132 18200 : std::cout << qUtf8Printable(tr("%1 %2 %3\n").arg(timeString).arg(value).arg(unit));
133 9380 : break;
134 9240 : }
135 28140 : timestamp += metadata.updateInterval;
136 28140 : --samplesToGo;
137 19320 : }
138 6030 : if (samplesToGo <= 0) {
139 13410 : qCInfo(lc).noquote() << tr("Finished fetching %Ln sample/s (with %L1 remaining).",
140 10080 : nullptr, metadata.numberOfSamples).arg(samplesToGo);
141 6030 : if (device) disconnect(); // Will exit the application once disconnected.
142 1980 : }
143 36830 : }
|