forked from commaai/openpilot
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
replay: replace QCommandLineParser with getopt for command-line argum…
…ent parsing. (commaai#33812) * use getopt * fix -h * show help if no args are provided
- Loading branch information
Showing
1 changed file
with
125 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,83 +1,153 @@ | ||
#include <getopt.h> | ||
|
||
#include <QApplication> | ||
#include <QCommandLineParser> | ||
#include <iostream> | ||
#include <map> | ||
#include <string> | ||
|
||
#include "common/prefix.h" | ||
#include "tools/replay/consoleui.h" | ||
#include "tools/replay/replay.h" | ||
|
||
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 | ||
const std::string helpText = | ||
R"(Usage: replay [options] | ||
Options: | ||
-a, --allow Whitelist of services to send | ||
-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[] = { | ||
{"dcam", REPLAY_FLAG_DCAM, "load driver camera"}, | ||
{"ecam", REPLAY_FLAG_ECAM, "load wide road camera"}, | ||
{"no-loop", REPLAY_FLAG_NO_LOOP, "stop at the end of the route"}, | ||
{"no-cache", REPLAY_FLAG_NO_FILE_CACHE, "turn off local cache"}, | ||
{"qcam", REPLAY_FLAG_QCAMERA, "load qcamera"}, | ||
{"no-hw-decoder", REPLAY_FLAG_NO_HW_DECODER, "disable HW video decoding"}, | ||
{"no-vipc", REPLAY_FLAG_NO_VIPC, "do not output video"}, | ||
{"all", REPLAY_FLAG_ALL_SERVICES, "do output all messages including uiDebug, userFlag" | ||
". this may causes issues when used along with UI"} | ||
const std::map<std::string, REPLAY_FLAGS> flag_map = { | ||
{"dcam", REPLAY_FLAG_DCAM}, | ||
{"ecam", REPLAY_FLAG_ECAM}, | ||
{"no-loop", REPLAY_FLAG_NO_LOOP}, | ||
{"no-cache", REPLAY_FLAG_NO_FILE_CACHE}, | ||
{"qcam", REPLAY_FLAG_QCAMERA}, | ||
{"no-hw-decoder", REPLAY_FLAG_NO_HW_DECODER}, | ||
{"no-vipc", REPLAY_FLAG_NO_VIPC}, | ||
{"all", REPLAY_FLAG_ALL_SERVICES}, | ||
}; | ||
|
||
QCommandLineParser parser; | ||
parser.setApplicationDescription("Mock openpilot components by publishing logged messages."); | ||
parser.addHelpOption(); | ||
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}); | ||
if (argc == 1) { | ||
std::cout << helpText; | ||
return false; | ||
} | ||
|
||
parser.process(app); | ||
const QStringList args = parser.positionalArguments(); | ||
if (args.empty() && !parser.isSet("demo")) { | ||
parser.showHelp(); | ||
int opt, option_index = 0; | ||
while ((opt = getopt_long(argc, argv, "a:b:c:s:x:d:p:h", cli_options, &option_index)) != -1) { | ||
switch (opt) { | ||
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(); | ||
QStringList allow = parser.value("allow").isEmpty() ? QStringList{} : parser.value("allow").split(","); | ||
QStringList block = parser.value("block").isEmpty() ? QStringList{} : parser.value("block").split(","); | ||
// Check for a route name (first positional argument) | ||
if (config.route.isEmpty() && optind < argc) { | ||
config.route = argv[optind]; | ||
} | ||
|
||
uint32_t replay_flags = REPLAY_FLAG_NONE; | ||
for (const auto &[name, flag, _] : flags) { | ||
if (parser.isSet(name)) { | ||
replay_flags |= flag; | ||
} | ||
if (config.route.isEmpty()) { | ||
std::cerr << "No route provided. Use --help for usage information.\n"; | ||
return false; | ||
} | ||
|
||
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; | ||
auto prefix = parser.value("prefix"); | ||
if (!prefix.isEmpty()) { | ||
op_prefix.reset(new OpenpilotPrefix(prefix.toStdString())); | ||
if (!config.prefix.empty()) { | ||
op_prefix = std::make_unique<OpenpilotPrefix>(config.prefix); | ||
} | ||
|
||
Replay *replay = new Replay(route, allow, block, nullptr, replay_flags, parser.value("data_dir"), &app); | ||
if (!parser.value("c").isEmpty()) { | ||
replay->setSegmentCacheLimit(parser.value("c").toInt()); | ||
Replay *replay = new Replay(config.route, config.allow, config.block, nullptr, config.flags, config.data_dir, &app); | ||
if (config.cache_segments > 0) { | ||
replay->setSegmentCacheLimit(config.cache_segments); | ||
} | ||
if (!parser.value("x").isEmpty()) { | ||
replay->setSpeed(std::clamp(parser.value("x").toFloat(), | ||
ConsoleUI::speed_array.front(), ConsoleUI::speed_array.back())); | ||
if (config.playback_speed > 0) { | ||
replay->setSpeed(std::clamp(config.playback_speed, ConsoleUI::speed_array.front(), ConsoleUI::speed_array.back())); | ||
} | ||
if (!replay->load()) { | ||
return 0; | ||
return 1; | ||
} | ||
|
||
ConsoleUI console_ui(replay); | ||
replay->start(parser.value("start").toInt()); | ||
replay->start(config.start_seconds); | ||
return app.exec(); | ||
} |