Dokit
Internal development documentation
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
loggerfetchcommand.cpp
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"
6
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 #define DOKIT_QT_UTC Qt::UTC
19#else
20 #include <QTimeZone>
21 #define DOKIT_QT_UTC QTimeZone::UTC
22#endif
23
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 */
39
40/*!
41 * \copybrief DeviceCommand::getService
42 *
43 * This override returns a pointer to a DataLoggerService object.
44 */
56
57/*!
58 * \copybrief DeviceCommand::serviceDetailsDiscovered
59 *
60 * This override fetches the current device's status, and outputs it in the selected format.
61 */
63{
64 DeviceCommand::serviceDetailsDiscovered(); // Just logs consistently.
65 qCInfo(lc).noquote() << tr("Fetching logger samples...");
66 service->enableMetadataNotifications();
67 service->enableReadingNotifications();
68 service->fetchSamples();
69}
70
71/*!
72 * Invoked when \a metadata has been received from the data logger.
73 */
75{
76 qCDebug(lc) << "status:" << (int)(data.status);
77 qCDebug(lc) << "scale:" << data.scale;
78 qCDebug(lc) << "mode:" << DataLoggerService::toString(data.mode) << (quint8)data.mode;
79 qCDebug(lc) << "range:" << service->toString(data.range, data.mode) << data.range;
80 qCDebug(lc) << "updateInterval:" << (int)data.updateInterval;
81 qCDebug(lc) << "numberOfSamples:" << data.numberOfSamples;
82 qCDebug(lc) << "timestamp:" << data.timestamp << QDateTime::fromSecsSinceEpoch(data.timestamp, DOKIT_QT_UTC);
83 this->metadata = data;
84 this->samplesToGo = data.numberOfSamples;
85 this->timestamp = (quint64)data.timestamp * (quint64)1000;
86 qCInfo(lc).noquote() << tr("Fetching %Ln logger sample/s...", nullptr, data.numberOfSamples);
87}
88
89/*!
90 * Outputs logger \a samples in the selected output format.
91 */
93{
94 QString unit;
95 switch (metadata.mode) {
96 case DataLoggerService::Mode::DcVoltage: unit = u"Vdc"_s; break;
97 case DataLoggerService::Mode::AcVoltage: unit = u"Vac"_s; break;
98 case DataLoggerService::Mode::DcCurrent: unit = u"Adc"_s; break;
99 case DataLoggerService::Mode::AcCurrent: unit = u"Aac"_s; break;
101 default:
102 qCDebug(lc).noquote() << tr(R"(No known unit for mode %1 "%2".)").arg((int)metadata.mode)
104 }
105 const QString range = service->toString(metadata.range, metadata.mode);
106
107 for (const qint16 &sample: samples) {
108 const QString timeString = (metadata.timestamp == 0) ? QString::number(timestamp)
110 const float value = sample * metadata.scale;
111 switch (format) {
113 for (; showCsvHeader; showCsvHeader = false) {
114 std::cout << qUtf8Printable(tr("timestamp,value,unit,range\n"));
115 }
116 std::cout << qUtf8Printable(QString::fromLatin1("%1,%2,%3,%4\n")
117 .arg(timeString).arg(value).arg(unit, range));
118 break;
119 case OutputFormat::Json: {
120 QJsonObject object{
121 { u"timestamp"_s, timeString },
122 { u"value"_s, value },
123 { u"unit"_s, unit },
124 { u"mode"_s, DataLoggerService::toString(metadata.mode) },
125 };
126 if (!range.isEmpty()) {
127 object.insert(u"range"_s, range);
128 }
129 std::cout << QJsonDocument(object).toJson().toStdString();
130 } break;
132 std::cout << qUtf8Printable(tr("%1 %2 %3\n").arg(timeString).arg(value).arg(unit));
133 break;
134 }
135 timestamp += metadata.updateInterval;
136 --samplesToGo;
137 }
138 if (samplesToGo <= 0) {
139 qCInfo(lc).noquote() << tr("Finished fetching %Ln sample/s (with %L1 remaining).",
140 nullptr, metadata.numberOfSamples).arg(samplesToGo);
141 if (device) disconnect(); // Will exit the application once disconnected.
142 }
143}
OutputFormat format
Selected output format.
@ Text
Plain unstructured text.
@ Csv
RFC 4180 compliant CSV text.
@ Json
RFC 8259 compliant JSON text.
The AbstractPokitService class provides a common base for Pokit services classes.
QVector< qint16 > Samples
Raw samples from the Reading characteristic.
static QString toString(const Mode &mode)
Returns mode as a user-friendly string.
void samplesRead(const DataLoggerService::Samples &samples)
This signal is emitted when the Reading characteristic has been notified.
void metadataRead(const DataLoggerService::Metadata &meta)
This signal is emitted when the Metadata characteristic has been read successfully.
@ DcVoltage
Measure DC voltage.
@ AcCurrent
Measure AC current.
@ AcVoltage
Measure AC voltage.
@ Temperature
Measure temperature.
@ DcCurrent
Measure DC current.
PokitDevice * device
Pokit Bluetooth device (if any) this command interacts with.
DeviceCommand(QObject *const parent=nullptr)
Construct a new DeviceCommand object with parent.
virtual void serviceDetailsDiscovered()
Handles service detail discovery events.
void disconnect(int exitCode=EXIT_SUCCESS)
Disconnects the underlying Pokit device, and sets exitCode to be return to the OS once the disconnect...
qint32 samplesToGo
Number of samples we're still expecting to receive.
DataLoggerService::Metadata metadata
Most recent data logging metadata.
DataLoggerService * service
Bluetooth service this command interacts with.
bool showCsvHeader
Whether or not to show a header as the first line of CSV output.
void outputSamples(const DataLoggerService::Samples &samples)
Outputs logger samples in the selected output format.
AbstractPokitService * getService() override
Returns a Pokit service object for the derived command class.
LoggerFetchCommand(QObject *const parent=nullptr)
Construct a new LoggerFetchCommand object with parent.
void serviceDetailsDiscovered() override
Handles service detail discovery events.
quint64 timestamp
Current sample's epoch milliseconds timestamp.
void metadataRead(const DataLoggerService::Metadata &data)
Invoked when metadata has been received from the data logger.
Declares the PokitDevice class.
std::string toStdString() const const
QDateTime fromMSecsSinceEpoch(qint64 msecs)
QDateTime fromSecsSinceEpoch(qint64 secs, Qt::TimeSpec spec, int offsetSeconds)
QString toString(Qt::DateFormat format) const const
QByteArray toJson() const const
QObject(QObject *parent)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString fromLatin1(const char *str, int size)
QString fromUtf8(const char *str, int size)
bool isEmpty() const const
QString number(int n, int base)
ISODateWithMs
Declares the DOKIT_USE_STRINGLITERALS macro, and related functions.
#define DOKIT_USE_STRINGLITERALS
Internal macro for using either official Qt string literals (added in Qt 6.4), or our own equivalent ...
Attributes included in the Metadata characteristic.
quint16 numberOfSamples
Number of samples acquired (1 to 6192).
quint32 timestamp
Timestamp stored at the beginning of the logging session.
float scale
Scale to apply to read samples.
LoggerStatus status
Current data logger status.
quint32 updateInterval
Current logging interval in milliseconds.
Mode mode
Current operation mode.