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"
17#include <QCommandLineParser>
18#include <QCoreApplication>
20#include <QLoggingCategory>
27#elif defined(Q_OS_WIN)
31static Q_LOGGING_CATEGORY(lc,
"dokit.cli.main", QtInfoMsg);
35 Q_DECLARE_TR_FUNCTIONS(cli_main)
38inline bool haveConsole()
40 #if defined(Q_OS_UNIX)
41 return isatty(STDERR_FILENO);
42 #elif defined(Q_OS_WIN)
43 return GetConsoleWindow();
52 QString messagePattern = QStringLiteral(
"%{if-category}%{category}: %{endif}%{message}");
54 if (parser.
isSet(QStringLiteral(
"debug"))) {
55 #ifdef QT_MESSAGELOGCONTEXT
57 messagePattern.
prepend(QStringLiteral(
"%{function} "));
59 messagePattern.
prepend(QStringLiteral(
"%{time process} %{threadid} %{type} "));
63 if (
const QString color = parser.
value(QStringLiteral(
"color"));
64 (color == QStringLiteral(
"yes")) || (color == QStringLiteral(
"auto") && haveConsole())) {
65 messagePattern.
prepend(QStringLiteral(
66 "%{if-debug}\x1b[37m%{endif}"
67 "%{if-info}\x1b[32m%{endif}"
68 "%{if-warning}\x1b[35m%{endif}"
69 "%{if-critical}\x1b[31m%{endif}"
70 "%{if-fatal}\x1b[31;1m%{endif}"));
71 messagePattern.
append(QStringLiteral(
"\x1b[0m"));
74 qSetMessagePattern(messagePattern);
93void showCliError(
const QString &errorText)
98 std::cerr << qUtf8Printable(message);
101Command getCliCommand(
const QStringList &posArguments)
104 return Command::None;
106 if (posArguments.
size() > 1) {
107 showCliError(Private::tr(
"More than one command: %1").arg(posArguments.
join(QStringLiteral(
", "))));
108 ::exit(EXIT_FAILURE);
112 { QStringLiteral(
"info"), Command::Info },
113 { QStringLiteral(
"status"), Command::Status },
114 { QStringLiteral(
"meter"), Command::Meter },
115 { QStringLiteral(
"dso"), Command::DSO },
116 { QStringLiteral(
"logger-start"), Command::LoggerStart },
117 { QStringLiteral(
"logger-stop"), Command::LoggerStop },
118 { QStringLiteral(
"logger-fetch"), Command::LoggerFetch },
119 { QStringLiteral(
"scan"), Command::Scan },
120 { QStringLiteral(
"set-name"), Command::SetName },
121 { QStringLiteral(
"set-torch"), Command::SetTorch },
122 { QStringLiteral(
"flash-led"), Command::FlashLed },
123 { QStringLiteral(
"calibrate"), Command::Calibrate },
125 const Command command = supportedCommands.
value(posArguments.
first().toLower(), Command::None);
126 if (command == Command::None) {
127 showCliError(Private::tr(
"Unknown command: %1").arg(posArguments.
first()));
128 ::exit(EXIT_FAILURE);
137 { QStringLiteral(
"color"),
138 Private::tr(
"Colors the console output. Valid options are: yes, no and auto. The default is auto."),
139 QStringLiteral(
"yes|no|auto"), QStringLiteral(
"auto")},
140 {{QStringLiteral(
"debug")},
141 Private::tr(
"Enable debug output.")},
142 {{QStringLiteral(
"d"), QStringLiteral(
"device")},
143 Private::tr(
"Set the name, hardware address or macOS UUID of Pokit device to use. If not specified, "
144 "the first discovered Pokit device will be used."),
145 Private::tr(
"device")},
149 {{QStringLiteral(
"interval")},
150 Private::tr(
"Set the update interval for DOS, meter and "
151 "logger modes. Suffixes such as 's' and 'ms' (for seconds and milliseconds) may be used. "
152 "If no suffix is present, the units will be inferred from the magnitide of the given "
153 "interval. If the option itself is not specified, a sensible default will be chosen "
154 "according to the selected command."),
155 Private::tr(
"interval")},
156 {{QStringLiteral(
"mode")},
157 Private::tr(
"Set the desired operation mode. For "
158 "meter, dso, and logger commands, the supported modes are: AC Voltage, DC Voltage, AC Current, "
159 "DC Current, Resistance, Diode, Continuity, and Temperature. All are case insensitive. "
160 "Only the first four options are available for dso and logger commands; the rest are "
161 "available in meter mode only. Temperature is also available for logger commands, but "
162 "requires firmware v1.5 or later for Pokit devices to support it. For the set-torch command "
163 "supported modes are On and Off."),
164 Private::tr(
"mode")},
165 {{QStringLiteral(
"new-name")},
166 Private::tr(
"Give the desired new name for the set-name command."), Private::tr(
"name")},
167 {{QStringLiteral(
"output")},
168 Private::tr(
"Set the format for output. Supported "
169 "formats are: CSV, JSON and Text. All are case insenstitve. The default is Text."),
170 Private::tr(
"format"),
171 Private::tr(
"text")},
172 {{QStringLiteral(
"range")},
173 Private::tr(
"Set the desired measurement range. Pokit "
174 "devices support specific ranges, such as 0 to 300mV. Specify the desired upper limit, "
175 "and the best range will be selected, or use 'auto' to enable the Pokit device's auto-"
176 "range feature. The default is 'auto'."),
177 Private::tr(
"range"), QStringLiteral(
"auto")},
178 {{QStringLiteral(
"samples")}, Private::tr(
"Set the number of samples to acquire."), Private::tr(
"count")},
179 {{QStringLiteral(
"temperature")},
180 Private::tr(
"Set the current ambient temperature for the calibration command."), Private::tr(
"degrees")},
181 {{QStringLiteral(
"timeout")},
182 Private::tr(
"Set the device discovery scan timeout. "
183 "Suffixes such as 's' and 'ms' (for seconds and milliseconds) may be used. "
184 "If no suffix is present, the units will be inferred from the magnitide of the given "
185 "interval. The default behaviour is no timeout."),
186 Private::tr(
"period")},
187 {{QStringLiteral(
"timestamp")},
188 Private::tr(
"Set the optional starting timestamp for data logging. Default to 'now'."),
189 Private::tr(
"period")},
190 {{QStringLiteral(
"trigger-level")}, Private::tr(
"Set the DSO trigger level."), Private::tr(
"level")},
191 {{QStringLiteral(
"trigger-mode")},
192 Private::tr(
"Set the DSO trigger mode. Supported modes are: free, rising and falling. The default is free."),
193 Private::tr(
"mode"), QStringLiteral(
"free")},
199 Private::tr(
"Get Pokit device information"), QStringLiteral(
" "));
201 Private::tr(
"Get Pokit device status"), QStringLiteral(
" "));
203 Private::tr(
"Access Pokit device's multimeter mode"), QStringLiteral(
" "));
205 Private::tr(
"Access Pokit device's DSO mode"), QStringLiteral(
" "));
207 Private::tr(
"Start Pokit device's data logger mode"), QStringLiteral(
" "));
209 Private::tr(
"Stop Pokit device's data logger mode"), QStringLiteral(
" "));
211 Private::tr(
"Fetch Pokit device's data logger samples"), QStringLiteral(
" "));
213 Private::tr(
"Scan Bluetooth for Pokit devices"), QStringLiteral(
" "));
215 Private::tr(
"Set Pokit device's name"), QStringLiteral(
" "));
217 Private::tr(
"Set Pokit device's torch on or off"), QStringLiteral(
" "));
219 Private::tr(
"Flash Pokit device's LED (Pokit Meter only)"), QStringLiteral(
" "));
221 Private::tr(
"Calibrate Pokit device temperature"), QStringLiteral(
" "));
224 parser.
parse(appArguments);
225 configureLogging(parser);
227 qCDebug(lc).noquote() <<
"Qt " QT_VERSION_STR
" compile-time";
228 qCDebug(lc).noquote() <<
"Qt" << qVersion() <<
"runtime";
232 if (command != Command::None) {
237 if (parser.
isSet(QStringLiteral(
"help"))) {
238 const QString commandString = (command == Command::None) ? QStringLiteral(
"<command>")
240 std::cout << qUtf8Printable(parser.
helpText()
241 .
replace(QStringLiteral(
"[options]"), commandString + QStringLiteral(
" [options]"))
242 .replace(QStringLiteral(
"Arguments:"), QStringLiteral(
"Command:"))
244 ::exit(EXIT_SUCCESS);
256 showCliError(Private::tr(
"Missing argument: <command>\nSee --help for usage information."));
259 case Command::DSO:
return new DsoCommand(parent);
261 case Command::Info:
return new InfoCommand(parent);
266 case Command::Scan:
return new ScanCommand(parent);
271 showCliError(Private::tr(
"Unknown command (%1)").arg((
int)command));
275int main(
int argc,
char *argv[])
281 #ifdef PROJECT_PRE_RELEASE
282 "-" PROJECT_PRE_RELEASE
284 #ifdef PROJECT_BUILD_ID
291 if (appTranslator.
load(
QLocale(), QStringLiteral(
"cli"), QStringLiteral(
"/"), QStringLiteral(
":/i18n"))) {
294 if (libTranslator.
load(
QLocale(), QStringLiteral(
"lib"), QStringLiteral(
"/"), QStringLiteral(
":/i18n"))) {
301 const Command commandType = parseCommandLine(appArguments, parser);
302#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
303 qCDebug(lc).noquote() <<
"App translations:" <<
304 (appTranslator.
filePath().
isEmpty() ? QStringLiteral(
"<none>") : appTranslator.filePath());
305 qCDebug(lc).noquote() <<
"Library translations:" <<
306 (libTranslator.
filePath().
isEmpty() ? QStringLiteral(
"<none>") : libTranslator.filePath());
308 qCDebug(lc).noquote() <<
"App translations:" << (!appTranslator.
isEmpty());
309 qCDebug(lc).noquote() <<
"Lib translations:" << (!libTranslator.
isEmpty());
314 if (command ==
nullptr) {
318 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.
The CalibrateCommand class implements the calibrate CLI command.
The DsoCommand class implements the dso CLI command.
The FlashLedCommand class implements the flash-led CLI command.
The InfoCommand class implements the info CLI command.
The LoggerFetchCommand class implements the logger CLI command.
The LoggerStartCommand class implements the logger CLI command.
The LoggerStopCommand class implements the logger stop CLI command.
The MeterCommand class implements the meter CLI command.
The ScanCommand class implements the scan CLI command, by scanning for nearby Pokit Bluetooth devices...
The SetNameCommand class implements the set-name CLI command.
The SetTorchCommand class implements the set-torch CLI command.
The StatusCommand class implements the status CLI command.
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
bool installTranslator(QTranslator *translationFile)
const T & constFirst() const const
bool isEmpty() const const
void setFilterRules(const QString &rules)
const T value(const Key &key, const T &defaultValue) const const
QString & append(QChar ch)
QString fromLatin1(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)