Dokit
Internal development documentation
Loading...
Searching...
No Matches
DsoCommand Class Reference

The DsoCommand class implements the dso CLI command. More...

Inheritance diagram for DsoCommand:
[legend]
Collaboration diagram for DsoCommand:
[legend]

Public Slots

QStringList processOptions (const QCommandLineParser &parser) override
 Processes the relevant options from the command line parser.
Public Slots inherited from DeviceCommand
bool start () override
 Begins scanning for the Pokit device.
Public Slots inherited from AbstractCommand
virtual QStringList processOptions (const QCommandLineParser &parser)
 Processes the relevant options from the command line parser.
virtual bool start ()=0
 Begins the functionality of this command, and returns true if begun successfully, false otherwise.

Public Member Functions

 DsoCommand (QObject *const parent=nullptr)
 Construct a new DsoCommand object with parent.
QStringList requiredOptions (const QCommandLineParser &parser) const override
 Returns a list of CLI option names required by this command.
QStringList supportedOptions (const QCommandLineParser &parser) const override
 Returns a list of CLI option names supported by this command.
Public Member Functions inherited from DeviceCommand
 DeviceCommand (QObject *const parent=nullptr)
 Construct a new DeviceCommand object with parent.
Public Member Functions inherited from AbstractCommand
 AbstractCommand (QObject *const parent=nullptr)
 Constructs a new command with parent.
Public Member Functions inherited from QObject
virtual const QMetaObjectmetaObject () const const
 QObject (QObject *parent)
virtual bool event (QEvent *e)
virtual bool eventFilter (QObject *watched, QEvent *event)
QString objectName () const const
void setObjectName (const QString &name)
bool isWidgetType () const const
bool isWindowType () const const
bool signalsBlocked () const const
bool blockSignals (bool block)
QThreadthread () const const
void moveToThread (QThread *targetThread)
int startTimer (int interval, Qt::TimerType timerType)
int startTimer (std::chrono::milliseconds time, Qt::TimerType timerType)
void killTimer (int id)
findChild (const QString &name, Qt::FindChildOptions options) const const
QList< T > findChildren (const QString &name, Qt::FindChildOptions options) const const
QList< T > findChildren (const QRegExp &regExp, Qt::FindChildOptions options) const const
QList< T > findChildren (const QRegularExpression &re, Qt::FindChildOptions options) const const
const QObjectListchildren () const const
void setParent (QObject *parent)
void installEventFilter (QObject *filterObj)
void removeEventFilter (QObject *obj)
QMetaObject::Connection connect (const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type) const const
bool disconnect (const char *signal, const QObject *receiver, const char *method) const const
bool disconnect (const QObject *receiver, const char *method) const const
void dumpObjectTree ()
void dumpObjectInfo ()
void dumpObjectTree () const const
void dumpObjectInfo () const const
bool setProperty (const char *name, const QVariant &value)
QVariant property (const char *name) const const
QList< QByteArraydynamicPropertyNames () const const
void destroyed (QObject *obj)
void objectNameChanged (const QString &objectName)
QObjectparent () const const
bool inherits (const char *className) const const
void deleteLater ()
 Q_DISABLE_COPY (Class)
 Q_DISABLE_MOVE (Class)
 Q_DISABLE_COPY_MOVE (Class)
qobject_cast (QObject *object)
qobject_cast (const QObject *object)
qFindChild (const QObject *obj, const QString &name)
QList< T > qFindChildren (const QObject *obj, const QString &name)
QList< T > qFindChildren (const QObject *obj, const QRegExp &regExp)
 Q_CLASSINFO (Name, Value)
 Q_INTERFACES (...)
 Q_PROPERTY (...)
 Q_ENUMS (...)
 Q_FLAGS (...)
 Q_ENUM (...)
 Q_FLAG (...)
 Q_ENUM_NS (...)
 Q_FLAG_NS (...)
 Q_OBJECT Q_OBJECT
 Q_GADGET Q_GADGET
 Q_NAMESPACE Q_NAMESPACE
 Q_NAMESPACE_EXPORT (EXPORT_MACRO)
 Q_SIGNALS Q_SIGNALS
 Q_SIGNAL Q_SIGNAL
 Q_SLOTS Q_SLOTS
 Q_SLOT Q_SLOT
 Q_EMIT Q_EMIT
 Q_INVOKABLE Q_INVOKABLE
 Q_REVISION Q_REVISION
 Q_SET_OBJECT_NAME (Object)
 QT_NO_NARROWING_CONVERSIONS_IN_CONNECT QT_NO_NARROWING_CONVERSIONS_IN_CONNECT

Protected Slots

void serviceDetailsDiscovered () override
 Handles service detail discovery events.
Protected Slots inherited from DeviceCommand
virtual void controllerError (const QLowEnergyController::Error error)
 Handles controller error events.
virtual void deviceDisconnected ()
 Handles device disconnection events.
virtual void serviceError (const QLowEnergyService::ServiceError error)
 Handles service error events.
virtual void serviceDetailsDiscovered ()
 Handles service detail discovery events.
Protected Slots inherited from AbstractCommand
virtual void deviceDiscovered (const QBluetoothDeviceInfo &info)=0
 Handles PokitDiscoveryAgent::pokitDeviceDiscovered signal.
virtual void deviceDiscoveryFinished ()=0
 Handles PokitDiscoveryAgent::deviceDiscoveryFinished signal.

Protected Member Functions

AbstractPokitServicegetService () override
 Returns a Pokit service object for the derived command class.
Protected Member Functions inherited from DeviceCommand
void disconnect (int exitCode=EXIT_SUCCESS)
 Disconnects the underlying Pokit device, and sets exitCode to be return to the OS once the disconnection has taken place.
Protected Member Functions inherited from QObject
QObjectsender () const const
int senderSignalIndex () const const
int receivers (const char *signal) const const
bool isSignalConnected (const QMetaMethod &signal) const const
virtual void timerEvent (QTimerEvent *event)
virtual void childEvent (QChildEvent *event)
virtual void customEvent (QEvent *event)
virtual void connectNotify (const QMetaMethod &signal)
virtual void disconnectNotify (const QMetaMethod &signal)

Static Protected Member Functions

static quint16 maxWindowSize (const PokitProduct product)
 Returns the product's maximum sampling window size.
static bool configureWindow (const PokitProduct product, const quint32 sampleRate, DsoService::Settings &settings)
 Configures the settings.numberOfSamples and/or settings.samplingWindow, if not already set, according to the requested sampleRate.
Static Protected Member Functions inherited from DeviceCommand
template<typename T>
static T minRange (const quint32 maxValue)
static quint8 minCapacitanceRange (const PokitProduct product, const quint32 maxValue)
 Returns the product's lowest capacitance range that can measure at least up to maxValue (nF), or AutoRange if no such range is available.
static quint8 minCurrentRange (const PokitProduct product, const quint32 maxValue)
 Returns the product's lowest current range that can measure at least up to maxValue (μA), or AutoRange if no such range is available.
static quint8 minResistanceRange (const PokitProduct product, const quint32 maxValue)
 Returns the product's lowest resistance range that can measure at least up to maxValue (Ω), or AutoRange if no such range is available.
static quint8 minVoltageRange (const PokitProduct product, const quint32 maxValue)
 Returns the product's lowest voltage range that can measure at least up to maxValue (mV), or AutoRange if no such range is available.
Static Protected Member Functions inherited from AbstractCommand
static Q_LOGGING_CATEGORY (lc, "dokit.cli.command", QtInfoMsg)
 Logging category for UI commands.

Private Slots

void settingsWritten ()
 Invoked when the DSO settings have been written.
void metadataRead (const DsoService::Metadata &data)
 Invoked when metadata has been received from the DSO.
void outputSamples (const DsoService::Samples &samples)
 Outputs DSO samples in the selected output format.

Private Attributes

quint8(* minRangeFunc )(const PokitProduct product, const quint32 maxValue)
 Pointer to function for converting rangeOptionValue to a Pokit device's range enumerator.
quint32 rangeOptionValue { 0 }
 The parsed value of range option.
quint32 sampleRateValue { 0 }
 The parsed value of the sample-rate option.
quint32 samplesValue { 0 }
 The parsed value of the samples option.
DsoServiceservice { nullptr }
 Bluetooth service this command interacts with.
DsoService::Settings settings
 Settings for the Pokit device's DSO mode.
DsoService::Metadata metadata
 Most recent DSO metadata.
qint32 samplesToGo { 0 }
 Number of samples we're expecting in the current window.
bool showCsvHeader { true }
 Whether or not to show a header as the first line of CSV output.

Additional Inherited Members

Public Types inherited from AbstractCommand
enum class  OutputFormat { Csv , Json , Text }
 Supported output formats. More...
Static Public Member Functions inherited from AbstractCommand
static QString appendSiPrefix (const double value, int precision=6)
 Returns a human-readable version of value, with up-to precision decimal digits, and SI unit prefix when appropiate.
static QString escapeCsvField (const QString &field)
 Returns an RFC 4180 compliant version of field.
template<typename R, typename T = quint32>
static T parseNumber (const QString &value, const QString &unit, const T sensibleMinimum=0)
 Returns value as an integer multiple of the ratio R, as number of type T.
Static Public Member Functions inherited from QObject
QString tr (const char *sourceText, const char *disambiguation, int n)
QString trUtf8 (const char *sourceText, const char *disambiguation, int n)
QMetaObject::Connection connect (const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QMetaObject::Connection connect (const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method, Qt::ConnectionType type)
QMetaObject::Connection connect (const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type)
QMetaObject::Connection connect (const QObject *sender, PointerToMemberFunction signal, Functor functor)
QMetaObject::Connection connect (const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type)
bool disconnect (const QObject *sender, const char *signal, const QObject *receiver, const char *method)
bool disconnect (const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method)
bool disconnect (const QMetaObject::Connection &connection)
bool disconnect (const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method)
Public Attributes inherited from QObject
typedef QObjectList
Protected Attributes inherited from DeviceCommand
PokitDevicedevice { nullptr }
 Pokit Bluetooth device (if any) this command interacts with.
int exitCodeOnDisconnect { EXIT_FAILURE }
 Exit code to return on device disconnection.
Protected Attributes inherited from AbstractCommand
QString deviceToScanFor
 Device (if any) that were passed to processOptions().
PokitDiscoveryAgentdiscoveryAgent
 Agent for Pokit device discovery.
OutputFormat format { OutputFormat::Text }
 Selected output format.
Properties inherited from QObject
 objectName

Detailed Description

The DsoCommand class implements the dso CLI command.

Definition at line 10 of file dsocommand.h.

Constructor & Destructor Documentation

◆ DsoCommand()

DsoCommand::DsoCommand ( QObject *const parent = nullptr)
explicit

Construct a new DsoCommand object with parent.

Definition at line 25 of file dsocommand.cpp.

26{
27
28}
DeviceCommand(QObject *const parent=nullptr)
Construct a new DeviceCommand object with parent.
QObject * parent() const const

References DeviceCommand::DeviceCommand(), QObject::parent(), and QObject::QObject().

Here is the call graph for this function:

Member Function Documentation

◆ configureWindow()

bool DsoCommand::configureWindow ( const PokitProduct product,
const quint32 sampleRate,
DsoService::Settings & settings )
staticprotected

Configures the settings.numberOfSamples and/or settings.samplingWindow, if not already set, according to the requested sampleRate.

The chosen settings will be limited to product's capaibilities.

Returns true os settings were set (either by this function, or they were already set), or false if the settings could not be determined succesfully (eg, because sampleRate was too high for the product).

Definition at line 262 of file dsocommand.cpp.

263{
264 const quint32 maxSampleRate = 1'000'000; // Pokit Meter and Pokit Pro both sample up to 1MHz.
265 const quint32 maxWindowSize = DsoCommand::maxWindowSize(product);
266
267 if (sampleRate > maxSampleRate) {
268 qCWarning(lc).noquote() <<
269 tr("The requested sample rate (%1Hz) likely exceeds the connected device's limit (%2Hz)")
270 .arg(sampleRate).arg(maxSampleRate);
271 }
272
273 if (settings.numberOfSamples > maxWindowSize) {
274 qCWarning(lc).noquote() <<
275 tr("Requested window size (%1 samples) likely exceeds the connected device's limit (%2) samples")
276 .arg(settings.samplingWindow).arg(maxWindowSize);
277 }
278
279 if ((settings.numberOfSamples != 0) && (settings.samplingWindow != 0)) {
280 qCDebug(lc).noquote() << "Both numberOfSamples and samplingWindow are set, so no need to derive either";
281 if (sampleRate != 0) {
282 const quint32 derivedRate = settings.numberOfSamples * 1'000'000ull / settings.samplingWindow;
283 qCDebug(lc).noquote() << "derivedRate" << derivedRate << sampleRate;
284 if (sampleRate != derivedRate) {
285 qCWarning(lc).noquote() << tr("Ignoring sample-rate, as interval and window-size both provided");
286 }
287 }
288 return true; // Nothing more to do.
289 }
290 Q_ASSERT_X(sampleRate > 0, "DsoCommand::configureWindow", "processOptions should have rejected already");
291
292 // If both window parameters are unset, choose the best window size (we'll choose a window period later).
293 if ((settings.numberOfSamples == 0) && (settings.samplingWindow == 0)) {
294 qCDebug(lc).noquote() << tr("Choosing best number-of-samples for sample-rate %2Hz").arg(sampleRate);
295 double smallestDifference = std::numeric_limits<double>::quiet_NaN();
296 for (quint32 windowSize = maxWindowSize; windowSize > 0; --windowSize) {
297 const quint32 period = windowSize * 1'000'000ull / sampleRate;
298 const double effectiveRate = double(windowSize) * 1'000'000.0 / (double)period;
299 if (effectiveRate > maxSampleRate) continue; // Skip sizes that would exceed the device's max sample rate.
300 if (const quint32 effectivePeriod = windowSize * 1'000'000ull / sampleRate;
301 effectivePeriod > 1'000'000) continue; // Skip sizes that would take longer than 1s to fetch.
302 const double difference = qAbs(effectiveRate - sampleRate);
303 // qCDebug(lc).noquote() << tr("%1 samples, %2us, %3Hz, %4Hz, ±%5Hz").arg(windowSize).arg(period)
304 // .arg(effectiveRate, 0, 'f').arg(sampleRate).arg(difference, 0, 'f');
305 if ((settings.numberOfSamples == 0) || (difference < smallestDifference)) {
306 settings.numberOfSamples = windowSize;
307 smallestDifference = difference;
308 }
309 }
310 qCDebug(lc).noquote() << tr("Chose %Ln sample/s, with error ±%2Hz", nullptr,
311 settings.numberOfSamples).arg(smallestDifference, 0, 'f');
312 if (settings.numberOfSamples == 0) {
313 qCCritical(lc).noquote() << tr("Failed to select a compatible window size for sample rate %1Hz").arg(sampleRate);
314 return false;
315 }
316 }
317
318 if (settings.numberOfSamples == 0) {
319 qCDebug(lc).noquote() << tr("Calculating number-of-samples for %1us window at %2Hz")
320 .arg(settings.samplingWindow).arg(sampleRate);
321 Q_ASSERT(settings.samplingWindow != 0);
322 const auto numberOfSamples = sampleRate * settings.samplingWindow / 1'000'000ull;
323 qCDebug(lc).noquote() << tr("Calculated %Ln sample/s", nullptr, numberOfSamples);
324 if ((numberOfSamples == 0) || (numberOfSamples > maxWindowSize)) {
325 qCCritical(lc).noquote() << tr("Failed to calculate a valid number of samples for a %L1us period at %2Hz")
326 .arg(settings.samplingWindow).arg(sampleRate);
327 return false;
328 }
329 settings.numberOfSamples = numberOfSamples; // Note the implicit uint64 to uint16 conversion.
330 Q_ASSERT(settings.numberOfSamples * 1'000'000ull / settings.samplingWindow <= sampleRate); // Due to integer truncation.
331 }
332
333 if (settings.samplingWindow == 0) {
334 qCDebug(lc).noquote() << tr("Calculating sampling-window for %Ln sample/s at %1Hz", nullptr,
335 settings.numberOfSamples).arg(sampleRate);
336 Q_ASSERT(settings.numberOfSamples != 0);
337 settings.samplingWindow = settings.numberOfSamples * 1'000'000ull / sampleRate;
338 qCDebug(lc).noquote() << tr("Calculated %1us").arg(settings.samplingWindow);
339 if (settings.samplingWindow == 0) {
340 qCCritical(lc).noquote() << tr("Failed to calculate a valid sampling window for a %L1 samples at %1Hz")
341 .arg(settings.numberOfSamples).arg(sampleRate);
342 return false;
343 }
344 }
345 return true;
346}
DsoService::Settings settings
Settings for the Pokit device's DSO mode.
Definition dsocommand.h:37
static quint16 maxWindowSize(const PokitProduct product)
Returns the product's maximum sampling window size.
QString tr(const char *sourceText, const char *disambiguation, int n)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const

References QString::arg(), maxWindowSize(), settings, and QObject::tr().

Referenced by serviceDetailsDiscovered().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getService()

AbstractPokitService * DsoCommand::getService ( )
overrideprotectedvirtual

Returns a Pokit service object for the derived command class.

This override returns a pointer to a DsoService object.

Implements DeviceCommand.

Definition at line 224 of file dsocommand.cpp.

225{
226 Q_ASSERT(device);
227 if (!service) {
228 service = device->dso();
229 Q_ASSERT(service);
233 }
234 return service;
235}
PokitDevice * device
Pokit Bluetooth device (if any) this command interacts with.
void outputSamples(const DsoService::Samples &samples)
Outputs DSO samples in the selected output format.
DsoService * service
Bluetooth service this command interacts with.
Definition dsocommand.h:36
void settingsWritten()
Invoked when the DSO settings have been written.
void metadataRead(const DsoService::Metadata &data)
Invoked when metadata has been received from the DSO.
void metadataRead(const DsoService::Metadata &meta)
This signal is emitted when the Metadata characteristic has been read successfully.
void settingsWritten()
This signal is emitted when the Settings characteristic has been written successfully.
void samplesRead(const DsoService::Samples &samples)
This signal is emitted when the Reading characteristic has been notified.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)

References QObject::connect(), DeviceCommand::device, metadataRead(), DsoService::metadataRead(), outputSamples(), DsoService::samplesRead(), service, settingsWritten(), and DsoService::settingsWritten().

Here is the call graph for this function:

◆ maxWindowSize()

quint16 DsoCommand::maxWindowSize ( const PokitProduct product)
staticprotected

Returns the product's maximum sampling window size.

Pokit Bluetooth API errata
Pokit's official documentation claim the maximum is 8,192. However, my Pokit Meter fails for window size greater than 8,191, while my Pokit Pro supports up to 16,384 samples per window.

Definition at line 243 of file dsocommand.cpp.

244{
245 switch (product) {
247 return 8'191;
249 return 16'384;
250 }
251 Q_ASSERT_X(false, "DsoCommand::maxWindowSize", "Unknown PokitProduct enum value");
252 return 0;
253}
@ PokitPro
Pokit Pro.
@ PokitMeter
Pokit Meter.

References PokitMeter, and PokitPro.

Referenced by configureWindow(), and processOptions().

Here is the caller graph for this function:

◆ metadataRead

void DsoCommand::metadataRead ( const DsoService::Metadata & data)
privateslot

Invoked when metadata has been received from the DSO.

Definition at line 410 of file dsocommand.cpp.

411{
412 qCDebug(lc) << "status:" << (int)(data.status);
413 qCDebug(lc) << "scale:" << data.scale;
414 qCDebug(lc) << "mode:" << DsoService::toString(data.mode);
415 qCDebug(lc) << "range:" << service->toString(data.range, data.mode);
416 qCDebug(lc) << "samplingWindow:" << data.samplingWindow;
417 qCDebug(lc) << "numberOfSamples:" << data.numberOfSamples;
418 qCDebug(lc) << "samplingRate:" << data.samplingRate << "Hz";
419 this->metadata = data;
420 this->samplesToGo = data.numberOfSamples;
421}
qint32 samplesToGo
Number of samples we're expecting in the current window.
Definition dsocommand.h:42
DsoService::Metadata metadata
Most recent DSO metadata.
Definition dsocommand.h:41
static QString toString(const Command &command)
Returns command as a user-friendly string.
quint32 samplingRate
Sampling rate used during last acquisition (1 to 1MHz).
Definition dsoservice.h:92
DsoStatus status
Current DSO status.
Definition dsoservice.h:86
float scale
Scale to apply to read samples.
Definition dsoservice.h:87
quint16 numberOfSamples
Number of samples acquired (1 to 8,192|16,384 for Pokit Meter|Pro).
Definition dsoservice.h:91
quint8 range
Range used during last acquisition.
Definition dsoservice.h:89
Mode mode
Operation mode used during last acquisition.
Definition dsoservice.h:88
quint32 samplingWindow
Sampling window (microseconds) used during last acquisition.
Definition dsoservice.h:90

References metadata, DsoService::Metadata::mode, DsoService::Metadata::numberOfSamples, DsoService::Metadata::range, samplesToGo, DsoService::Metadata::samplingRate, DsoService::Metadata::samplingWindow, DsoService::Metadata::scale, service, DsoService::Metadata::status, and DsoService::toString().

Referenced by getService().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ outputSamples

void DsoCommand::outputSamples ( const DsoService::Samples & samples)
privateslot

Outputs DSO samples in the selected output format.

Definition at line 426 of file dsocommand.cpp.

427{
428 QString unit;
429 switch (metadata.mode) {
430 case DsoService::Mode::DcVoltage: unit = u"Vdc"_s; break;
431 case DsoService::Mode::AcVoltage: unit = u"Vac"_s; break;
432 case DsoService::Mode::DcCurrent: unit = u"Adc"_s; break;
433 case DsoService::Mode::AcCurrent: unit = u"Aac"_s; break;
434 default:
435 qCDebug(lc).noquote() << tr(R"(No known unit for mode %1 "%2".)").arg((int)metadata.mode)
437 }
438 const QString range = service->toString(metadata.range, metadata.mode);
439
440 for (const qint16 &sample: samples) {
441 static quint32 sampleNumber = 0; ++sampleNumber;
442 const float value = sample * metadata.scale;
443 switch (format) {
445 for (; showCsvHeader; showCsvHeader = false) {
446 std::cout << qUtf8Printable(tr("sample_number,value,unit,range\n"));
447 }
448 std::cout << qUtf8Printable(QString::fromLatin1("%1,%2,%3,%4\n")
449 .arg(sampleNumber).arg(value).arg(unit, range));
450 break;
452 std::cout << QJsonDocument(QJsonObject{
453 { u"value"_s, value },
454 { u"unit"_s, unit },
455 { u"range"_s, range },
456 { u"mode"_s, DsoService::toString(metadata.mode) },
457 }).toJson().toStdString();
458 break;
460 std::cout << qUtf8Printable(tr("%1 %2 %3\n").arg(sampleNumber).arg(value).arg(unit));
461 break;
462 }
463 --samplesToGo;
464
465 if ((sampleNumber > samplesValue) && (samplesValue != 0)) {
466 qCInfo(lc).noquote() << tr("Finished fetching %Ln sample/s. Disconnecting.", nullptr, sampleNumber);
467 if (device) disconnect(); // Will exit the application once disconnected.
468 return;
469 }
470 }
471
472 // If we've received all the data for the current window, begin fetching another.
473 if (samplesToGo <= 0) {
474 qCDebug(lc).noquote() << tr("Finished fetching %Ln window sample/s (with %L2 to remaining).",
475 nullptr, metadata.numberOfSamples).arg(samplesToGo);
477 }
478}
OutputFormat format
Selected output format.
@ Text
Plain unstructured text.
@ Csv
RFC 4180 compliant CSV text.
@ Json
RFC 8259 compliant JSON text.
void disconnect(int exitCode=EXIT_SUCCESS)
Disconnects the underlying Pokit device, and sets exitCode to be return to the OS once the disconnect...
bool showCsvHeader
Whether or not to show a header as the first line of CSV output.
Definition dsocommand.h:43
quint32 samplesValue
The parsed value of the samples option.
Definition dsocommand.h:35
@ DcVoltage
Measure DC voltage.
Definition dsoservice.h:55
@ AcCurrent
Measure AC current.
Definition dsoservice.h:58
@ AcVoltage
Measure AC voltage.
Definition dsoservice.h:56
@ DcCurrent
Measure DC current.
Definition dsoservice.h:57
QString fromLatin1(const char *str, int size)

References DsoService::AcCurrent, DsoService::AcVoltage, QString::arg(), PokitMeter::AutoRange, AbstractCommand::Csv, DsoService::DcCurrent, DsoService::DcVoltage, DeviceCommand::device, DeviceCommand::disconnect(), AbstractCommand::format, QString::fromLatin1(), AbstractCommand::Json, metadata, samplesToGo, samplesValue, service, settings, showCsvHeader, AbstractCommand::Text, DsoService::toString(), and QObject::tr().

Referenced by getService().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ processOptions

QStringList DsoCommand::processOptions ( const QCommandLineParser & parser)
overrideslot

Processes the relevant options from the command line parser.

This implementation extends DeviceCommand::processOptions to process additional CLI options supported (or required) by this command.

Definition at line 56 of file dsocommand.cpp.

57{
58 QStringList errors = DeviceCommand::processOptions(parser);
59 if (!errors.isEmpty()) {
60 return errors;
61 }
62
63 // Parse the (required) mode option.
64 if (const QString mode = parser.value(u"mode"_s).trimmed().toLower();
65 mode.startsWith(u"ac v"_s) || mode.startsWith(u"vac"_s)) {
67 } else if (mode.startsWith(u"dc v"_s) || mode.startsWith(u"vdc"_s)) {
69 } else if (mode.startsWith(u"ac c"_s) || mode.startsWith(u"aac"_s)) {
71 } else if (mode.startsWith(u"dc c"_s) || mode.startsWith(u"adc"_s)) {
73 } else {
74 errors.append(tr("Unknown DSO mode: %1").arg(parser.value(u"mode"_s)));
75 return errors;
76 }
77
78 // Parse the (required) range option.
79 QString unit;
80 {
81 const QString value = parser.value(u"range"_s);
82 quint32 sensibleMinimum = 0;
83 switch (settings.mode) {
85 Q_ASSERT(false); // Not possible, since the mode parsing above never allows Idle.
86 break;
90 unit = u"V"_s;
91 sensibleMinimum = 50; // mV.
92 break;
96 unit = u"A"_s;
97 sensibleMinimum = 5; // mA.
98 break;
99 }
100 Q_ASSERT(!unit.isEmpty());
101 rangeOptionValue = parseNumber<std::milli>(value, unit, sensibleMinimum);
102 if (rangeOptionValue == 0) {
103 errors.append(tr("Invalid range value: %1").arg(value));
104 }
105 }
106
107 // Parse the trigger-level option.
108 if (parser.isSet(u"trigger-level"_s)) {
109 float sign = 1.0;
110 const QString rawValue = parser.value(u"trigger-level"_s);
111 QString absValue = rawValue;
112 DOKIT_STRING_INDEX_TYPE nonSpacePos;
113 for (nonSpacePos = 0; (nonSpacePos < rawValue.length()) && (rawValue.at(nonSpacePos) == u' '); ++nonSpacePos);
114 if ((nonSpacePos < rawValue.length()) && (rawValue.at(nonSpacePos) == u'-')) {
115 absValue = rawValue.mid(nonSpacePos+1);
116 sign = -1.0;
117 }
118 const float level = parseNumber<std::ratio<1>,float>(absValue, unit, 0.f);
119 qCDebug(lc) << "Trigger level" << rawValue << absValue << nonSpacePos << sign << level;
120 if (qIsNaN(level)) {
121 errors.append(tr("Invalid trigger-level value: %1").arg(rawValue));
122 } else {
123 settings.triggerLevel = sign * level;
124 qCDebug(lc) << "Trigger level" << settings.triggerLevel;
125 // Check the trigger level is within the Votage / Current range.
126 if ((rangeOptionValue != 0) && (qAbs(settings.triggerLevel) > (rangeOptionValue/1000.0))) {
127 errors.append(tr("Trigger-level %1%2 is outside range ±%3%2").arg(
128 appendSiPrefix(settings.triggerLevel), unit, appendSiPrefix(rangeOptionValue / 1000.0)));
129 }
130 }
131 }
132
133 // Parse the trigger-mode option.
134 if (parser.isSet(u"trigger-mode"_s)) {
135 const QString triggerMode = parser.value(u"trigger-mode"_s).trimmed().toLower();
136 if (triggerMode.startsWith(u"free"_s)) {
138 } else if (triggerMode.startsWith(u"ris"_s)) {
140 } else if (triggerMode.startsWith(u"fall"_s)) {
142 } else {
143 errors.append(tr("Unknown trigger mode: %1").arg(parser.value(u"trigger-mode"_s)));
144 }
145 }
146
147 // Ensure that if either trigger option is present, then both are.
148 if (parser.isSet(u"trigger-level"_s) != parser.isSet(u"trigger-mode"_s)) {
149 errors.append(tr("If either option is provided, then both must be: trigger-level, trigger-mode"));
150 }
151
152 // Parse the sample-rate option.
153 if (parser.isSet(u"sample-rate"_s)) {
154 const QString value = parser.value(u"sample-rate"_s);
155 sampleRateValue = parseNumber<std::ratio<1,1>>(value, u"Hz"_s, (quint32)50'000);
156 if (sampleRateValue == 0) {
157 errors.append(tr("Invalid sample-rate value: %1").arg(value));
158 } else if (sampleRateValue > 1'000'000) {
159 qCWarning(lc).noquote() << tr("Pokit devices do not officially support sample rates greater than 1Mhz");
160 }
161 }
162
163 // Parse the interval option.
164 if (parser.isSet(u"interval"_s)) {
165 const QString value = parser.value(u"interval"_s);
166 const quint32 interval = parseNumber<std::micro>(value, u"s"_s, (quint32)500'000);
167 if (interval == 0) {
168 errors.append(tr("Invalid interval value: %1").arg(value));
169 } else {
170 settings.samplingWindow = interval;
171 }
172 }
173
174 // Parse the window-size option.
175 if (parser.isSet(u"window-size"_s)) {
176 const QString value = parser.value(u"window-size"_s);
177 const quint32 samples = parseNumber<std::ratio<1>>(value, u"S"_s);
178 if (samples == 0) {
179 errors.append(tr("Invalid window-size value: %1").arg(value));
180 } else if (samples > std::numeric_limits<quint16>::max()) {
181 errors.append(tr("Window size value (%1) must be no greater than %2")
182 .arg(value).arg(std::numeric_limits<quint16>::max()));
183 } else {
184 settings.numberOfSamples = (quint16)samples;
185 if (const auto maxSamples = maxWindowSize(PokitProduct::PokitPro); settings.numberOfSamples > maxSamples) {
186 qCWarning(lc).noquote() <<
187 tr("No Pokit device officially supports windows greater than %L1 samples").arg(maxSamples);
188 }
189 }
190 }
191
192 // Ensure that we have at least: sample-rate, or both interval and window-size.
193 if ((!parser.isSet(u"sample-rate"_s)) && !(parser.isSet(u"interval"_s) && parser.isSet(u"window-size"_s))) {
194 errors.append(tr("Missing required option/s: either sample-rate, or both interval and window-size"));
195 }
196
197 // If we have all three sample-rate related options, ensure they agree.
198 if ((sampleRateValue != 0) && (settings.numberOfSamples != 0) && (settings.samplingWindow != 0)) {
199 const quint32 sampleRate = settings.numberOfSamples * 1'000'000ull / settings.samplingWindow;
200 if (sampleRate != sampleRateValue) {
201 errors.append(tr("Windows size (%1 samples) and interval (%2ns) yield a sample rate of %3Hz, which does "
202 "not match the supplied sample-rate (%4Hz). Tip: leave one option unset to have dokit calculate the "
203 "remaining option.").arg(settings.numberOfSamples).arg(settings.samplingWindow).arg(sampleRate)
205 }
206 }
207
208 // Parse the samples option.
209 if (parser.isSet(u"samples"_s)) {
210 const QString value = parser.value(u"samples"_s);
211 samplesValue = parseNumber<std::ratio<1>>(value, u"S"_s);
212 if (samplesValue == 0) {
213 errors.append(tr("Invalid samples value: %1").arg(value));
214 }
215 }
216 return errors;
217}
static QString appendSiPrefix(const double value, int precision=6)
Returns a human-readable version of value, with up-to precision decimal digits, and SI unit prefix wh...
static T parseNumber(const QString &value, const QString &unit, const T sensibleMinimum=0)
Returns value as an integer multiple of the ratio R, as number of type T.
virtual QStringList processOptions(const QCommandLineParser &parser)
Processes the relevant options from the command line parser.
static quint8 minVoltageRange(const PokitProduct product, const quint32 maxValue)
Returns the product's lowest voltage range that can measure at least up to maxValue (mV),...
static quint8 minCurrentRange(const PokitProduct product, const quint32 maxValue)
Returns the product's lowest current range that can measure at least up to maxValue (μA),...
quint8(* minRangeFunc)(const PokitProduct product, const quint32 maxValue)
Pointer to function for converting rangeOptionValue to a Pokit device's range enumerator.
Definition dsocommand.h:32
quint32 rangeOptionValue
The parsed value of range option.
Definition dsocommand.h:33
quint32 sampleRateValue
The parsed value of the sample-rate option.
Definition dsocommand.h:34
@ Idle
Make device idle.
Definition dsoservice.h:54
@ FreeRunning
Run free, without waiting for edge triggers.
Definition dsoservice.h:45
@ RisingEdgeTrigger
Trigger on a rising edge.
Definition dsoservice.h:46
@ FallingEdgeTrigger
Trigger on a falling edge.
Definition dsoservice.h:47
bool isSet(const QString &name) const const
QString value(const QString &optionName) const const
void append(const T &value)
bool isEmpty() const const
const QChar at(int position) const const
bool isEmpty() const const
int length() const const
QString mid(int position, int n) const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString toLower() const const
QString trimmed() const const
#define DOKIT_STRING_INDEX_TYPE
Internal macro for matching the index type used by QString methods.

References DsoService::AcCurrent, DsoService::AcVoltage, QList::append(), AbstractCommand::appendSiPrefix(), QString::arg(), QString::at(), DsoService::DcCurrent, DsoService::DcVoltage, DOKIT_STRING_INDEX_TYPE, DsoService::FallingEdgeTrigger, DsoService::FreeRunning, DsoService::Idle, QList::isEmpty(), QString::isEmpty(), QCommandLineParser::isSet(), QString::length(), maxWindowSize(), QString::mid(), DeviceCommand::minCurrentRange(), minRangeFunc, DeviceCommand::minVoltageRange(), AbstractCommand::parseNumber(), PokitPro, AbstractCommand::processOptions(), rangeOptionValue, DsoService::RisingEdgeTrigger, sampleRateValue, samplesValue, settings, QString::startsWith(), QString::toLower(), QObject::tr(), QString::trimmed(), and QCommandLineParser::value().

Here is the call graph for this function:

◆ requiredOptions()

QStringList DsoCommand::requiredOptions ( const QCommandLineParser & parser) const
overridevirtual

Returns a list of CLI option names required by this command.

The main console appication may use this list to output an error (and exit) if any of the returned names are not found in the parsed CLI options.

The (already parsed) parser may be used adjust the returned required options depending on the value of other options. For example, the logger command only requires the --mode option if the --command option is start.

This base implementation simply returns an empty list. Derived classes should override this function to include any required options.

Reimplemented from AbstractCommand.

Definition at line 30 of file dsocommand.cpp.

31{
32 return DeviceCommand::requiredOptions(parser) + QStringList{
33 u"mode"_s,
34 u"range"_s,
35 };
36}
virtual QStringList requiredOptions(const QCommandLineParser &parser) const
Returns a list of CLI option names required by this command.

References AbstractCommand::requiredOptions().

Here is the call graph for this function:

◆ serviceDetailsDiscovered

void DsoCommand::serviceDetailsDiscovered ( )
overrideprotectedslot

Handles service detail discovery events.

This override fetches the current device's status, and outputs it in the selected format.

Definition at line 353 of file dsocommand.cpp.

354{
355 DeviceCommand::serviceDetailsDiscovered(); // Just logs consistently.
357 if (!configureWindow(*service->pokitProduct(), sampleRateValue, settings)) {
358 disconnect(EXIT_FAILURE);
359 return;
360 }
361 if (samplesValue == 0) samplesValue = settings.numberOfSamples;
362 const QString range = service->toString(settings.range, settings.mode);
363 const QString triggerInfo = (settings.command == DsoService::Command::FreeRunning) ? QString() :
364 tr(", and a %1 at %2%3%4 (%5Hz)").arg(DsoService::toString(settings.command).toLower(),
365 (settings.triggerLevel < 0.) ? u"-"_s : u""_s, appendSiPrefix(qAbs(settings.triggerLevel)),
366 range.at(range.size()-1));
367 qCInfo(lc).noquote() << tr("Sampling %1, with range %2, at %L3Hz (%Ln sample/s over %L4us)%5", nullptr, settings.numberOfSamples)
368 .arg(DsoService::toString(settings.mode), (range.isNull()) ? QString::fromLatin1("N/A") : range)
369 .arg(settings.numberOfSamples * 1'000'000ull / settings.samplingWindow).arg(settings.samplingWindow)
370 .arg(triggerInfo);
371 if (!service->enableMetadataNotifications()) {
372 qCCritical(lc).noquote() << tr("Failed to enable metadata notifications");
373 disconnect(EXIT_FAILURE);
374 return;
375 }
376 if (!service->enableReadingNotifications()) {
377 qCCritical(lc).noquote() << tr("Failed to enable reading notifications");
378 disconnect(EXIT_FAILURE);
379 return;
380 }
381 service->setSettings(settings);
382}
virtual void serviceDetailsDiscovered()
Handles service detail discovery events.
static bool configureWindow(const PokitProduct product, const quint32 sampleRate, DsoService::Settings &settings)
Configures the settings.numberOfSamples and/or settings.samplingWindow, if not already set,...
QTPOKIT_EXPORT PokitProduct pokitProduct(const QBluetoothDeviceInfo &info)
Returns the PokitProduct corresponding the Bluetooth device info.

References AbstractCommand::appendSiPrefix(), QString::arg(), QString::at(), configureWindow(), DeviceCommand::disconnect(), DsoService::FreeRunning, QString::fromLatin1(), QString::isNull(), minRangeFunc, rangeOptionValue, sampleRateValue, samplesValue, service, DeviceCommand::serviceDetailsDiscovered(), settings, QString::size(), DsoService::toString(), and QObject::tr().

Here is the call graph for this function:

◆ settingsWritten

void DsoCommand::settingsWritten ( )
privateslot

Invoked when the DSO settings have been written.

Definition at line 401 of file dsocommand.cpp.

402{
403 Q_ASSERT(service);
404 qCDebug(lc).noquote() << tr("Settings written; DSO has started.");
405}

References service, and QObject::tr().

Referenced by getService().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ supportedOptions()

QStringList DsoCommand::supportedOptions ( const QCommandLineParser & parser) const
overridevirtual

Returns a list of CLI option names supported by this command.

The main console appication may use this list to output a warning for any parsed CLI options not included in the returned list.

The (already parsed) parser may be used adjust the returned supported options depending on the value of other options. For example, the logger command only supported the --timestamp option if the --command option is start.

This base implementation simply returns requiredOptions(). Derived classes should override this function to include optional options, such as:

QStringList Derived::supportedOptions(const QCommandLineParser &parser) const
{
list.sort();
list.removeDuplicates(); // Optional, recommended.
return list;
}
virtual QStringList supportedOptions(const QCommandLineParser &parser) const
Returns a list of CLI option names supported by this command.
int removeDuplicates()
void sort(Qt::CaseSensitivity cs)

Reimplemented from AbstractCommand.

Definition at line 38 of file dsocommand.cpp.

39{
40 return DeviceCommand::supportedOptions(parser) + QStringList{
41 u"interval"_s,
42 u"samples"_s,
43 u"sample-rate"_s,
44 u"trigger-level"_s,
45 u"trigger-mode"_s,
46 u"window-size"_s,
47 };
48}

References AbstractCommand::supportedOptions().

Here is the call graph for this function:

Member Data Documentation

◆ metadata

DsoService::Metadata DsoCommand::metadata
private

Most recent DSO metadata.

Definition at line 41 of file dsocommand.h.

Referenced by metadataRead(), and outputSamples().

◆ minRangeFunc

quint8(* DsoCommand::minRangeFunc) (const PokitProduct product, const quint32 maxValue)
inlineprivate

Pointer to function for converting rangeOptionValue to a Pokit device's range enumerator.

This function pointer is assigned during the command line parsing, but is not invoked until after the device's services are discovered, because prior to that discovery, we don't know which product (Meter vs Pro vs Clamp, etc) we're talking to and thus which enumerator list to be using.

If the current mode does not support ranges (eg diode, and continuity modes), then this member will be nullptr.

See also
processOptions
serviceDetailsDiscovered

Definition at line 32 of file dsocommand.h.

32{ nullptr };

Referenced by processOptions(), and serviceDetailsDiscovered().

◆ rangeOptionValue

quint32 DsoCommand::rangeOptionValue { 0 }
private

The parsed value of range option.

Definition at line 33 of file dsocommand.h.

33{ 0 }; ///< The parsed value of range option.

Referenced by processOptions(), and serviceDetailsDiscovered().

◆ sampleRateValue

quint32 DsoCommand::sampleRateValue { 0 }
private

The parsed value of the sample-rate option.

Definition at line 34 of file dsocommand.h.

34{ 0 }; ///< The parsed value of the sample-rate option.

Referenced by processOptions(), and serviceDetailsDiscovered().

◆ samplesToGo

qint32 DsoCommand::samplesToGo { 0 }
private

Number of samples we're expecting in the current window.

Definition at line 42 of file dsocommand.h.

42{ 0 }; ///< Number of samples we're expecting in the current window.

Referenced by metadataRead(), and outputSamples().

◆ samplesValue

quint32 DsoCommand::samplesValue { 0 }
private

The parsed value of the samples option.

Definition at line 35 of file dsocommand.h.

35{ 0 }; ///< The parsed value of the samples option.

Referenced by outputSamples(), processOptions(), and serviceDetailsDiscovered().

◆ service

DsoService* DsoCommand::service { nullptr }
private

Bluetooth service this command interacts with.

Definition at line 36 of file dsocommand.h.

36{ nullptr }; ///< Bluetooth service this command interacts with.

Referenced by getService(), metadataRead(), outputSamples(), serviceDetailsDiscovered(), and settingsWritten().

◆ settings

DsoService::Settings DsoCommand::settings
private
Initial value:

Settings for the Pokit device's DSO mode.

Definition at line 37 of file dsocommand.h.

37 { ///< Settings for the Pokit device's DSO mode.
40 };

Referenced by configureWindow(), outputSamples(), processOptions(), and serviceDetailsDiscovered().

◆ showCsvHeader

bool DsoCommand::showCsvHeader { true }
private

Whether or not to show a header as the first line of CSV output.

Definition at line 43 of file dsocommand.h.

43{ true }; ///< Whether or not to show a header as the first line of CSV output.

Referenced by outputSamples().


The documentation for this class was generated from the following files: