4#include "calibratecommand.h"
6#include "flashledcommand.h"
7#include "infocommand.h"
8#include "loggerfetchcommand.h"
9#include "loggerstartcommand.h"
10#include "loggerstopcommand.h"
11#include "metercommand.h"
12#include "scancommand.h"
13#include "setnamecommand.h"
14#include "settorchcommand.h"
15#include "statuscommand.h"
18#include <QCommandLineParser>
19#include <QCoreApplication>
21#include <QLoggingCategory>
28#elif defined(Q_OS_WIN)
34static Q_LOGGING_CATEGORY(lc,
"dokit.cli.main", QtInfoMsg);
38 Q_DECLARE_TR_FUNCTIONS(cli_main)
41inline bool haveConsole()
43 #if defined(Q_OS_UNIX)
44 return isatty(STDERR_FILENO);
45 #elif defined(Q_OS_WIN)
46 return GetConsoleWindow();
55 QString messagePattern = u
"%{if-category}%{category}: %{endif}%{message}"_s;
57 if (parser.
isSet(u
"debug"_s)) {
58 #ifdef QT_MESSAGELOGCONTEXT
60 messagePattern.
prepend(u
"%{function} "_s);
62 messagePattern.
prepend(u
"%{time process} %{threadid} %{type} "_s);
67 (color == u
"yes"_s) || (color == u
"auto"_s && haveConsole())) {
68 messagePattern.
prepend(QStringLiteral(
69 "%{if-debug}\x1b[37m%{endif}"
70 "%{if-info}\x1b[32m%{endif}"
71 "%{if-warning}\x1b[35m%{endif}"
72 "%{if-critical}\x1b[31m%{endif}"
73 "%{if-fatal}\x1b[31;1m%{endif}"));
74 messagePattern.
append(u
"\x1b[0m"_s);
77 qSetMessagePattern(messagePattern);
96void showCliError(
const QString &errorText)
100 fputs(qUtf8Printable(message), stderr);
103Command getCliCommand(
const QStringList &posArguments)
106 return Command::None;
108 if (posArguments.
size() > 1) {
109 showCliError(Private::tr(
"More than one command: %1").arg(posArguments.
join(u
", "_s)));
110 ::exit(EXIT_FAILURE);
114 { u
"info"_s, Command::Info },
115 { u
"status"_s, Command::Status },
116 { u
"meter"_s, Command::Meter },
117 { u
"dso"_s, Command::DSO },
118 { u
"logger-start"_s, Command::LoggerStart },
119 { u
"logger-stop"_s, Command::LoggerStop },
120 { u
"logger-fetch"_s, Command::LoggerFetch },
121 { u
"scan"_s, Command::Scan },
122 { u
"set-name"_s, Command::SetName },
123 { u
"set-torch"_s, Command::SetTorch },
124 { u
"flash-led"_s, Command::FlashLed },
125 { u
"calibrate"_s, Command::Calibrate },
127 const Command command = supportedCommands.
value(posArguments.
first().toLower(), Command::None);
128 if (command == Command::None) {
129 showCliError(Private::tr(
"Unknown command: %1").arg(posArguments.
first()));
130 ::exit(EXIT_FAILURE);
140 Private::tr(
"Colors the console output. Valid options are: yes, no and auto. The default is auto."),
141 u
"yes|no|auto"_s, u
"auto"_s},
143 Private::tr(
"Enable debug output.")},
144 {{u
"d"_s, u
"device"_s},
145 Private::tr(
"Set the name, hardware address or macOS UUID of Pokit device to use. If not specified, "
146 "the first discovered Pokit device will be used."),
147 Private::tr(
"device")},
152 Private::tr(
"Set the update interval for DSO, meter and "
153 "logger modes. Suffixes such as 's' and 'ms' (for seconds and milliseconds) may be used. "
154 "If no suffix is present, the units will be inferred from the magnitude of the given "
155 "interval. If the option itself is not specified, a sensible default will be chosen "
156 "according to the selected command."),
157 Private::tr(
"interval")},
159 Private::tr(
"Set the desired operation mode. For "
160 "meter, dso, and logger commands, the supported modes are: AC Voltage, DC Voltage, AC Current, "
161 "DC Current, Resistance, Diode, Continuity, and Temperature. All are case insensitive. "
162 "Only the first four options are available for dso and logger commands; the rest are "
163 "available in meter mode only. Temperature is also available for logger commands, but "
164 "requires firmware v1.5 or later for Pokit devices to support it. For the set-torch command "
165 "supported modes are On and Off."),
166 Private::tr(
"mode")},
168 Private::tr(
"Give the desired new name for the set-name command."), Private::tr(
"name")},
170 Private::tr(
"Set the format for output. Supported "
171 "formats are: CSV, JSON and Text. All are case insensitive. The default is Text."),
172 Private::tr(
"format"),
173 Private::tr(
"text")},
175 Private::tr(
"Set the desired measurement range. Pokit "
176 "devices support specific ranges, such as 0 to 300mV. Specify the desired upper limit, "
177 "and the best range will be selected, or use 'auto' to enable the Pokit device's auto-"
178 "range feature. The default is 'auto'."),
179 Private::tr(
"range"), u
"auto"_s},
180 {{u
"samples"_s}, Private::tr(
"Set the number of samples to acquire."), Private::tr(
"count")},
181 {{u
"sample-rate"_s}, Private::tr(
"Set the DSO sample rate. Suffixes such as 'k', 'M' and 'MHz' may be used. "
182 "If no suffix is present, the scale will be inferred from the magnitude of the given rate. Rate must be "
183 "between 1Hz and 1MHz."),
184 Private::tr(
"rate")},
186 Private::tr(
"Set the current ambient temperature for the calibration command."), Private::tr(
"degrees")},
188 Private::tr(
"Set the device discovery scan timeout. "
189 "Suffixes such as 's' and 'ms' (for seconds and milliseconds) may be used. "
190 "If no suffix is present, the units will be inferred from the magnitude of the given "
191 "interval. The default behaviour is no timeout."),
192 Private::tr(
"period")},
194 Private::tr(
"Set the optional starting timestamp for data logging. Default to 'now'."),
195 Private::tr(
"period")},
196 {{u
"trigger-level"_s}, Private::tr(
"Set the DSO trigger level."), Private::tr(
"level")},
197 {{u
"trigger-mode"_s},
198 Private::tr(
"Set the DSO trigger mode. Supported modes are: free, rising and falling. The default is free."),
199 Private::tr(
"mode"), u
"free"_s},
200 {{u
"window-size"_s}, Private::tr(
"Set the DSO window size. If not specified, a sensible value will be chosen"
201 "according to the connected Pokit device's capabilities. Note, the limit is Pokit device dependant."),
202 Private::tr(
"samples")},
209 parser.
addPositionalArgument(u
"meter"_s, Private::tr(
"Access Pokit device's multimeter mode"), u
" "_s);
211 parser.
addPositionalArgument(u
"logger-start"_s, Private::tr(
"Start Pokit device's data logger mode"), u
" "_s);
212 parser.
addPositionalArgument(u
"logger-stop"_s, Private::tr(
"Stop Pokit device's data logger mode"), u
" "_s);
213 parser.
addPositionalArgument(u
"logger-fetch"_s, Private::tr(
"Fetch Pokit device's data logger samples"), u
" "_s);
216 parser.
addPositionalArgument(u
"set-torch"_s, Private::tr(
"Set Pokit device's torch on or off"), u
" "_s);
217 parser.
addPositionalArgument(u
"flash-led"_s, Private::tr(
"Flash Pokit device's LED (Pokit Meter only)"), u
" "_s);
218 parser.
addPositionalArgument(u
"calibrate"_s, Private::tr(
"Calibrate Pokit device temperature"), u
" "_s);
221 parser.
parse(appArguments);
222 configureLogging(parser);
224 qCDebug(lc).noquote() <<
"Qt " QT_VERSION_STR
" compile-time";
225 qCDebug(lc).noquote() <<
"Qt" << qVersion() <<
"runtime";
229 if (command != Command::None) {
234 if (parser.
isSet(u
"help"_s)) {
235 const QString commandString = (command == Command::None) ? u
"<command>"_s
236 : parser.positionalArguments().constFirst();
237 std::cout << qUtf8Printable(parser.
helpText()
238 .
replace(u
"[options]"_s, commandString + u
" [options]"_s)
239 .
replace(u
"Arguments:"_s, u
"Command:"_s)
241 ::exit(EXIT_SUCCESS);
253 showCliError(Private::tr(
"Missing argument: <command>\nSee --help for usage information."));
256 case Command::DSO:
return new DsoCommand(parent);
258 case Command::Info:
return new InfoCommand(parent);
263 case Command::Scan:
return new ScanCommand(parent);
268 showCliError(Private::tr(
"Unknown command (%1)").arg((
int)command));
272int main(
int argc,
char *argv[])
278 #ifdef PROJECT_PRE_RELEASE
279 "-" PROJECT_PRE_RELEASE
281 #ifdef PROJECT_BUILD_ID
286#if defined(Q_OS_MACOS) || defined(Q_CC_MINGW)
291 const QLocale newLocale(localeName);
293 qCWarning(lc) <<
"Could not find locale" << localeName;
303 if (appTranslator.
load(locale, u
"cli"_s, u
"/"_s, u
":/i18n"_s)) {
306 if (libTranslator.
load(locale, u
"lib"_s, u
"/"_s, u
":/i18n"_s)) {
313 const Command commandType = parseCommandLine(appArguments, parser);
314 qCDebug(lc).noquote() <<
"Locale:" << locale << locale.
uiLanguages();
315#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
316 qCDebug(lc).noquote() <<
"App translations:" <<
317 (appTranslator.
filePath().isEmpty() ? u
"<none>"_s : appTranslator.
filePath());
318 qCDebug(lc).noquote() <<
"Library translations:" <<
319 (libTranslator.
filePath().isEmpty() ? u
"<none>"_s : libTranslator.
filePath());
321 qCDebug(lc).noquote() <<
"App translations:" << (!appTranslator.
isEmpty());
322 qCDebug(lc).noquote() <<
"Lib translations:" << (!libTranslator.
isEmpty());
327 if (command ==
nullptr) {
331 for (
const QString &error: cliErrors) {
The AbstractCommand class provides a consistent base for the classes that implement CLI commands.
virtual bool start()=0
Begins the functionality of this command, and returns true if begun successfully, false otherwise.
virtual QStringList processOptions(const QCommandLineParser &parser)
Processes the relevant options from the command line parser.
CalibrateCommand(QObject *const parent=nullptr)
Construct a new CalibrateCommand object with parent.
DsoCommand(QObject *const parent=nullptr)
Construct a new DsoCommand object with parent.
FlashLedCommand(QObject *const parent=nullptr)
Construct a new FlashLedCommand object with parent.
InfoCommand(QObject *const parent=nullptr)
Construct a new InfoCommand object with parent.
LoggerFetchCommand(QObject *const parent=nullptr)
Construct a new LoggerFetchCommand object with parent.
LoggerStartCommand(QObject *const parent=nullptr)
Construct a new LoggerStartCommand object with parent.
LoggerStopCommand(QObject *const parent=nullptr)
Construct a new LoggerStopCommand object with parent.
MeterCommand(QObject *const parent=nullptr)
Construct a new MeterCommand object with parent.
ScanCommand(QObject *const parent=nullptr)
Construct a new ScanCommand object with parent.
SetNameCommand(QObject *const parent=nullptr)
Construct a new SetNameCommand object with parent.
SetTorchCommand(QObject *const parent=nullptr)
Construct a new SetTorchCommand object with parent.
StatusCommand(QObject *const parent=nullptr)
Construct a new StatusCommand object with parent.
QCommandLineOption addHelpOption()
bool addOptions(const QList< QCommandLineOption > &options)
void addPositionalArgument(const QString &name, const QString &description, const QString &syntax)
QCommandLineOption addVersionOption()
void clearPositionalArguments()
QString helpText() const const
bool isSet(const QString &name) const const
bool parse(const QStringList &arguments)
QStringList positionalArguments() const const
void process(const QStringList &arguments)
QString value(const QString &optionName) const const
QString applicationVersion
bool installTranslator(QTranslator *translationFile)
const T & constFirst() const const
bool isEmpty() const const
void setDefault(const QLocale &locale)
QStringList uiLanguages() const const
void setFilterRules(const QString &rules)
const T value(const Key &key, const T &defaultValue) const const
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString & append(QChar ch)
QString fromLatin1(const char *str, int size)
QString fromLocal8Bit(const char *str, int size)
bool isEmpty() const const
QString & prepend(QChar ch)
QString & replace(int position, int n, QChar after)
QString join(const QString &separator) const const
QString filePath() const const
virtual bool isEmpty() const const
bool load(const QString &filename, const QString &directory, const QString &search_delimiters, const QString &suffix)
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 ...