replay: replace QCommandLineParser with getopt for command-line argument parsing. (#33812)
* use getopt * fix -h * show help if no args are providedpull/33823/head
parent
c9f7e39d37
commit
debd71ebec
1 changed files with 125 additions and 55 deletions
@ -1,83 +1,153 @@ |
|||||||
|
#include <getopt.h> |
||||||
|
|
||||||
#include <QApplication> |
#include <QApplication> |
||||||
#include <QCommandLineParser> |
#include <iostream> |
||||||
|
#include <map> |
||||||
|
#include <string> |
||||||
|
|
||||||
#include "common/prefix.h" |
#include "common/prefix.h" |
||||||
#include "tools/replay/consoleui.h" |
#include "tools/replay/consoleui.h" |
||||||
#include "tools/replay/replay.h" |
#include "tools/replay/replay.h" |
||||||
|
|
||||||
int main(int argc, char *argv[]) { |
const std::string helpText = |
||||||
#ifdef __APPLE__ |
R"(Usage: replay [options] |
||||||
// With all sockets opened, we might hit the default limit of 256 on macOS
|
Options: |
||||||
util::set_file_descriptor_limit(1024); |
-a, --allow Whitelist of services to send |
||||||
#endif |
-b, --block Blacklist of services to send |
||||||
|
-c, --cache Cache <n> segments in memory. Default is 5 |
||||||
|
-s, --start Start from <seconds> |
||||||
|
-x, --playback Playback <speed> |
||||||
|
--demo Use a demo route instead of providing your own |
||||||
|
-d, --data_dir Local directory with routes |
||||||
|
-p, --prefix Set OPENPILOT_PREFIX |
||||||
|
--dcam Load driver camera |
||||||
|
--ecam Load wide road camera |
||||||
|
--no-loop Stop at the end of the route |
||||||
|
--no-cache Turn off local cache |
||||||
|
--qcam Load qcamera |
||||||
|
--no-hw-decoder Disable HW video decoding |
||||||
|
--no-vipc Do not output video |
||||||
|
--all Output all messages including uiDebug, userFlag |
||||||
|
-h, --help Show this help message |
||||||
|
)"; |
||||||
|
|
||||||
QCoreApplication app(argc, argv); |
struct ReplayConfig { |
||||||
|
QString route; |
||||||
|
QStringList allow; |
||||||
|
QStringList block; |
||||||
|
QString data_dir; |
||||||
|
std::string prefix; |
||||||
|
uint32_t flags = REPLAY_FLAG_NONE; |
||||||
|
int start_seconds = 0; |
||||||
|
int cache_segments = -1; |
||||||
|
float playback_speed = -1; |
||||||
|
}; |
||||||
|
|
||||||
|
bool parseArgs(int argc, char *argv[], ReplayConfig &config) { |
||||||
|
const struct option cli_options[] = { |
||||||
|
{"allow", required_argument, nullptr, 'a'}, |
||||||
|
{"block", required_argument, nullptr, 'b'}, |
||||||
|
{"cache", required_argument, nullptr, 'c'}, |
||||||
|
{"start", required_argument, nullptr, 's'}, |
||||||
|
{"playback", required_argument, nullptr, 'x'}, |
||||||
|
{"demo", no_argument, nullptr, 0}, |
||||||
|
{"data_dir", required_argument, nullptr, 'd'}, |
||||||
|
{"prefix", required_argument, nullptr, 'p'}, |
||||||
|
{"dcam", no_argument, nullptr, 0}, |
||||||
|
{"ecam", no_argument, nullptr, 0}, |
||||||
|
{"no-loop", no_argument, nullptr, 0}, |
||||||
|
{"no-cache", no_argument, nullptr, 0}, |
||||||
|
{"qcam", no_argument, nullptr, 0}, |
||||||
|
{"no-hw-decoder", no_argument, nullptr, 0}, |
||||||
|
{"no-vipc", no_argument, nullptr, 0}, |
||||||
|
{"all", no_argument, nullptr, 0}, |
||||||
|
{"help", no_argument, nullptr, 'h'}, |
||||||
|
{nullptr, 0, nullptr, 0}, // Terminating entry
|
||||||
|
}; |
||||||
|
|
||||||
const std::tuple<QString, REPLAY_FLAGS, QString> flags[] = { |
const std::map<std::string, REPLAY_FLAGS> flag_map = { |
||||||
{"dcam", REPLAY_FLAG_DCAM, "load driver camera"}, |
{"dcam", REPLAY_FLAG_DCAM}, |
||||||
{"ecam", REPLAY_FLAG_ECAM, "load wide road camera"}, |
{"ecam", REPLAY_FLAG_ECAM}, |
||||||
{"no-loop", REPLAY_FLAG_NO_LOOP, "stop at the end of the route"}, |
{"no-loop", REPLAY_FLAG_NO_LOOP}, |
||||||
{"no-cache", REPLAY_FLAG_NO_FILE_CACHE, "turn off local cache"}, |
{"no-cache", REPLAY_FLAG_NO_FILE_CACHE}, |
||||||
{"qcam", REPLAY_FLAG_QCAMERA, "load qcamera"}, |
{"qcam", REPLAY_FLAG_QCAMERA}, |
||||||
{"no-hw-decoder", REPLAY_FLAG_NO_HW_DECODER, "disable HW video decoding"}, |
{"no-hw-decoder", REPLAY_FLAG_NO_HW_DECODER}, |
||||||
{"no-vipc", REPLAY_FLAG_NO_VIPC, "do not output video"}, |
{"no-vipc", REPLAY_FLAG_NO_VIPC}, |
||||||
{"all", REPLAY_FLAG_ALL_SERVICES, "do output all messages including uiDebug, userFlag" |
{"all", REPLAY_FLAG_ALL_SERVICES}, |
||||||
". this may causes issues when used along with UI"} |
|
||||||
}; |
}; |
||||||
|
|
||||||
QCommandLineParser parser; |
if (argc == 1) { |
||||||
parser.setApplicationDescription("Mock openpilot components by publishing logged messages."); |
std::cout << helpText; |
||||||
parser.addHelpOption(); |
return false; |
||||||
parser.addPositionalArgument("route", "the drive to replay. find your drives at connect.comma.ai"); |
|
||||||
parser.addOption({{"a", "allow"}, "whitelist of services to send", "allow"}); |
|
||||||
parser.addOption({{"b", "block"}, "blacklist of services to send", "block"}); |
|
||||||
parser.addOption({{"c", "cache"}, "cache <n> segments in memory. default is 5", "n"}); |
|
||||||
parser.addOption({{"s", "start"}, "start from <seconds>", "seconds"}); |
|
||||||
parser.addOption({"x", QString("playback <speed>. between %1 - %2") |
|
||||||
.arg(ConsoleUI::speed_array.front()).arg(ConsoleUI::speed_array.back()), "speed"}); |
|
||||||
parser.addOption({"demo", "use a demo route instead of providing your own"}); |
|
||||||
parser.addOption({"data_dir", "local directory with routes", "data_dir"}); |
|
||||||
parser.addOption({"prefix", "set OPENPILOT_PREFIX", "prefix"}); |
|
||||||
for (auto &[name, _, desc] : flags) { |
|
||||||
parser.addOption({name, desc}); |
|
||||||
} |
} |
||||||
|
|
||||||
parser.process(app); |
int opt, option_index = 0; |
||||||
const QStringList args = parser.positionalArguments(); |
while ((opt = getopt_long(argc, argv, "a:b:c:s:x:d:p:h", cli_options, &option_index)) != -1) { |
||||||
if (args.empty() && !parser.isSet("demo")) { |
switch (opt) { |
||||||
parser.showHelp(); |
case 'a': config.allow = QString(optarg).split(","); break; |
||||||
|
case 'b': config.block = QString(optarg).split(","); break; |
||||||
|
case 'c': config.cache_segments = std::atoi(optarg); break; |
||||||
|
case 's': config.start_seconds = std::atoi(optarg); break; |
||||||
|
case 'x': config.playback_speed = std::atof(optarg); break; |
||||||
|
case 'd': config.data_dir = optarg; break; |
||||||
|
case 'p': config.prefix = optarg; break; |
||||||
|
case 0: { |
||||||
|
std::string name = cli_options[option_index].name; |
||||||
|
if (name == "demo") { |
||||||
|
config.route = DEMO_ROUTE; |
||||||
|
} else { |
||||||
|
config.flags |= flag_map.at(name); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
case 'h': std::cout << helpText; return false; |
||||||
|
default: return false; |
||||||
|
} |
||||||
} |
} |
||||||
|
|
||||||
const QString route = args.empty() ? DEMO_ROUTE : args.first(); |
// Check for a route name (first positional argument)
|
||||||
QStringList allow = parser.value("allow").isEmpty() ? QStringList{} : parser.value("allow").split(","); |
if (config.route.isEmpty() && optind < argc) { |
||||||
QStringList block = parser.value("block").isEmpty() ? QStringList{} : parser.value("block").split(","); |
config.route = argv[optind]; |
||||||
|
} |
||||||
|
|
||||||
uint32_t replay_flags = REPLAY_FLAG_NONE; |
if (config.route.isEmpty()) { |
||||||
for (const auto &[name, flag, _] : flags) { |
std::cerr << "No route provided. Use --help for usage information.\n"; |
||||||
if (parser.isSet(name)) { |
return false; |
||||||
replay_flags |= flag; |
} |
||||||
} |
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, char *argv[]) { |
||||||
|
#ifdef __APPLE__ |
||||||
|
// With all sockets opened, we might hit the default limit of 256 on macOS
|
||||||
|
util::set_file_descriptor_limit(1024); |
||||||
|
#endif |
||||||
|
|
||||||
|
QCoreApplication app(argc, argv); |
||||||
|
ReplayConfig config; |
||||||
|
|
||||||
|
if (!parseArgs(argc, argv, config)) { |
||||||
|
return 1; |
||||||
} |
} |
||||||
|
|
||||||
std::unique_ptr<OpenpilotPrefix> op_prefix; |
std::unique_ptr<OpenpilotPrefix> op_prefix; |
||||||
auto prefix = parser.value("prefix"); |
if (!config.prefix.empty()) { |
||||||
if (!prefix.isEmpty()) { |
op_prefix = std::make_unique<OpenpilotPrefix>(config.prefix); |
||||||
op_prefix.reset(new OpenpilotPrefix(prefix.toStdString())); |
|
||||||
} |
} |
||||||
|
|
||||||
Replay *replay = new Replay(route, allow, block, nullptr, replay_flags, parser.value("data_dir"), &app); |
Replay *replay = new Replay(config.route, config.allow, config.block, nullptr, config.flags, config.data_dir, &app); |
||||||
if (!parser.value("c").isEmpty()) { |
if (config.cache_segments > 0) { |
||||||
replay->setSegmentCacheLimit(parser.value("c").toInt()); |
replay->setSegmentCacheLimit(config.cache_segments); |
||||||
} |
} |
||||||
if (!parser.value("x").isEmpty()) { |
if (config.playback_speed > 0) { |
||||||
replay->setSpeed(std::clamp(parser.value("x").toFloat(), |
replay->setSpeed(std::clamp(config.playback_speed, ConsoleUI::speed_array.front(), ConsoleUI::speed_array.back())); |
||||||
ConsoleUI::speed_array.front(), ConsoleUI::speed_array.back())); |
|
||||||
} |
} |
||||||
if (!replay->load()) { |
if (!replay->load()) { |
||||||
return 0; |
return 1; |
||||||
} |
} |
||||||
|
|
||||||
ConsoleUI console_ui(replay); |
ConsoleUI console_ui(replay); |
||||||
replay->start(parser.value("start").toInt()); |
replay->start(config.start_seconds); |
||||||
return app.exec(); |
return app.exec(); |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue