mirror of https://github.com/commaai/openpilot.git
replay: replace QCommandLineParser with getopt for command-line argument parsing. (#33812)
* use getopt * fix -h * show help if no args are provided
This commit is contained in:
parent
c9f7e39d37
commit
debd71ebec
|
@ -1,10 +1,123 @@
|
|||
#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"
|
||||
|
||||
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
|
||||
)";
|
||||
|
||||
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::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},
|
||||
};
|
||||
|
||||
if (argc == 1) {
|
||||
std::cout << helpText;
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for a route name (first positional argument)
|
||||
if (config.route.isEmpty() && optind < argc) {
|
||||
config.route = argv[optind];
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -12,72 +125,29 @@ int main(int argc, char *argv[]) {
|
|||
#endif
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
ReplayConfig config;
|
||||
|
||||
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"}
|
||||
};
|
||||
|
||||
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});
|
||||
}
|
||||
|
||||
parser.process(app);
|
||||
const QStringList args = parser.positionalArguments();
|
||||
if (args.empty() && !parser.isSet("demo")) {
|
||||
parser.showHelp();
|
||||
}
|
||||
|
||||
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(",");
|
||||
|
||||
uint32_t replay_flags = REPLAY_FLAG_NONE;
|
||||
for (const auto &[name, flag, _] : flags) {
|
||||
if (parser.isSet(name)) {
|
||||
replay_flags |= flag;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue