tools: add --auto flag to replay and cabana for loading routes from auto source (#34863)

* add flag to auto load a route from the most suitable source

* split to functions

* early return

* add --auto to replay

* README

* cleanup

* remove prefix

* parse datetime

* cleanup

* improve help

* do not modify logreader.py

* fix seg_num

* cleanup
pull/35134/head
Dean Lee 3 days ago committed by GitHub
parent 5a94d818bb
commit b920e2a998
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 17
      tools/auto_source.py
  2. 2
      tools/cabana/README.md
  3. 4
      tools/cabana/cabana.cc
  4. 5
      tools/cabana/streams/replaystream.cc
  5. 2
      tools/cabana/streams/replaystream.h
  6. 2
      tools/replay/README.md
  7. 14
      tools/replay/main.cc
  8. 4
      tools/replay/replay.cc
  9. 2
      tools/replay/replay.h
  10. 58
      tools/replay/route.cc
  11. 6
      tools/replay/route.h
  12. 4
      tools/replay/seg_mgr.h

@ -0,0 +1,17 @@
#!/usr/bin/env python3
import sys
from openpilot.tools.lib.logreader import LogReader
def main():
if len(sys.argv) != 2:
print("Usage: python auto_source.py <log_path>")
sys.exit(1)
log_path = sys.argv[1]
lr = LogReader(log_path, sort_by_time=True)
print("\n".join(lr.logreader_identifiers))
if __name__ == "__main__":
main()

@ -12,6 +12,8 @@ Options:
-h, --help Displays help on commandline options. -h, --help Displays help on commandline options.
--help-all Displays help including Qt specific options. --help-all Displays help including Qt specific options.
--demo use a demo route instead of providing your own --demo use a demo route instead of providing your own
--auto Auto load the route from the best available source (no video):
internal, openpilotci, comma_api, car_segments, testing_closet
--qcam load qcamera --qcam load qcamera
--ecam load wide road camera --ecam load wide road camera
--msgq read can messages from msgq --msgq read can messages from msgq

@ -23,6 +23,7 @@ int main(int argc, char *argv[]) {
cmd_parser.addHelpOption(); cmd_parser.addHelpOption();
cmd_parser.addPositionalArgument("route", "the drive to replay. find your drives at connect.comma.ai"); cmd_parser.addPositionalArgument("route", "the drive to replay. find your drives at connect.comma.ai");
cmd_parser.addOption({"demo", "use a demo route instead of providing your own"}); cmd_parser.addOption({"demo", "use a demo route instead of providing your own"});
cmd_parser.addOption({"auto", "Auto load the route from the best available source (no video): internal, openpilotci, comma_api, car_segments, testing_closet"});
cmd_parser.addOption({"qcam", "load qcamera"}); cmd_parser.addOption({"qcam", "load qcamera"});
cmd_parser.addOption({"ecam", "load wide road camera"}); cmd_parser.addOption({"ecam", "load wide road camera"});
cmd_parser.addOption({"dcam", "load driver camera"}); cmd_parser.addOption({"dcam", "load driver camera"});
@ -69,7 +70,8 @@ int main(int argc, char *argv[]) {
} }
if (!route.isEmpty()) { if (!route.isEmpty()) {
auto replay_stream = std::make_unique<ReplayStream>(&app); auto replay_stream = std::make_unique<ReplayStream>(&app);
if (!replay_stream->loadRoute(route, cmd_parser.value("data_dir"), replay_flags)) { bool auto_source = cmd_parser.isSet("auto");
if (!replay_stream->loadRoute(route, cmd_parser.value("data_dir"), replay_flags, auto_source)) {
return 0; return 0;
} }
stream = replay_stream.release(); stream = replay_stream.release();

@ -7,6 +7,7 @@
#include <QPushButton> #include <QPushButton>
#include "common/timing.h" #include "common/timing.h"
#include "common/util.h"
#include "tools/cabana/streams/routes.h" #include "tools/cabana/streams/routes.h"
ReplayStream::ReplayStream(QObject *parent) : AbstractStream(parent) { ReplayStream::ReplayStream(QObject *parent) : AbstractStream(parent) {
@ -45,9 +46,9 @@ void ReplayStream::mergeSegments() {
} }
} }
bool ReplayStream::loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags) { bool ReplayStream::loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags, bool auto_source) {
replay.reset(new Replay(route.toStdString(), {"can", "roadEncodeIdx", "driverEncodeIdx", "wideRoadEncodeIdx", "carParams"}, replay.reset(new Replay(route.toStdString(), {"can", "roadEncodeIdx", "driverEncodeIdx", "wideRoadEncodeIdx", "carParams"},
{}, nullptr, replay_flags, data_dir.toStdString())); {}, nullptr, replay_flags, data_dir.toStdString(), auto_source));
replay->setSegmentCacheLimit(settings.max_cached_minutes); replay->setSegmentCacheLimit(settings.max_cached_minutes);
replay->installEventFilter([this](const Event *event) { return eventFilter(event); }); replay->installEventFilter([this](const Event *event) { return eventFilter(event); });

@ -18,7 +18,7 @@ class ReplayStream : public AbstractStream {
public: public:
ReplayStream(QObject *parent); ReplayStream(QObject *parent);
void start() override { replay->start(); } void start() override { replay->start(); }
bool loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags = REPLAY_FLAG_NONE); bool loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags = REPLAY_FLAG_NONE, bool auto_source = false);
bool eventFilter(const Event *event); bool eventFilter(const Event *event);
void seekTo(double ts) override { replay->seekTo(std::max(double(0), ts), false); } void seekTo(double ts) override { replay->seekTo(std::max(double(0), ts), false); }
bool liveStreaming() const override { return false; } bool liveStreaming() const override { return false; }

@ -64,6 +64,8 @@ Options:
-s, --start <seconds> start from <seconds> -s, --start <seconds> start from <seconds>
-x <speed> playback <speed>. between 0.2 - 3 -x <speed> playback <speed>. between 0.2 - 3
--demo use a demo route instead of providing your own --demo use a demo route instead of providing your own
--auto Auto load the route from the best available source (no video):
internal, openpilotci, comma_api, car_segments, testing_closet
--data_dir <data_dir> local directory with routes --data_dir <data_dir> local directory with routes
--prefix <prefix> set OPENPILOT_PREFIX --prefix <prefix> set OPENPILOT_PREFIX
--dcam load driver camera --dcam load driver camera

@ -19,6 +19,8 @@ Options:
-s, --start Start from <seconds> -s, --start Start from <seconds>
-x, --playback Playback <speed> -x, --playback Playback <speed>
--demo Use a demo route instead of providing your own --demo Use a demo route instead of providing your own
--auto Auto load the route from the best available source (no video):
internal, openpilotci, comma_api, car_segments, testing_closet
-d, --data_dir Local directory with routes -d, --data_dir Local directory with routes
-p, --prefix Set OPENPILOT_PREFIX -p, --prefix Set OPENPILOT_PREFIX
--dcam Load driver camera --dcam Load driver camera
@ -39,6 +41,7 @@ struct ReplayConfig {
std::string data_dir; std::string data_dir;
std::string prefix; std::string prefix;
uint32_t flags = REPLAY_FLAG_NONE; uint32_t flags = REPLAY_FLAG_NONE;
bool auto_source = false;
int start_seconds = 0; int start_seconds = 0;
int cache_segments = -1; int cache_segments = -1;
float playback_speed = -1; float playback_speed = -1;
@ -52,6 +55,7 @@ bool parseArgs(int argc, char *argv[], ReplayConfig &config) {
{"start", required_argument, nullptr, 's'}, {"start", required_argument, nullptr, 's'},
{"playback", required_argument, nullptr, 'x'}, {"playback", required_argument, nullptr, 'x'},
{"demo", no_argument, nullptr, 0}, {"demo", no_argument, nullptr, 0},
{"auto", no_argument, nullptr, 0},
{"data_dir", required_argument, nullptr, 'd'}, {"data_dir", required_argument, nullptr, 'd'},
{"prefix", required_argument, nullptr, 'p'}, {"prefix", required_argument, nullptr, 'p'},
{"dcam", no_argument, nullptr, 0}, {"dcam", no_argument, nullptr, 0},
@ -94,11 +98,9 @@ bool parseArgs(int argc, char *argv[], ReplayConfig &config) {
case 'p': config.prefix = optarg; break; case 'p': config.prefix = optarg; break;
case 0: { case 0: {
std::string name = cli_options[option_index].name; std::string name = cli_options[option_index].name;
if (name == "demo") { if (name == "demo") config.route = DEMO_ROUTE;
config.route = DEMO_ROUTE; else if (name == "auto") config.auto_source = true;
} else { else config.flags |= flag_map.at(name);
config.flags |= flag_map.at(name);
}
break; break;
} }
case 'h': std::cout << helpText; return false; case 'h': std::cout << helpText; return false;
@ -136,7 +138,7 @@ int main(int argc, char *argv[]) {
op_prefix = std::make_unique<OpenpilotPrefix>(config.prefix); op_prefix = std::make_unique<OpenpilotPrefix>(config.prefix);
} }
Replay replay(config.route, config.allow, config.block, nullptr, config.flags, config.data_dir); Replay replay(config.route, config.allow, config.block, nullptr, config.flags, config.data_dir, config.auto_source);
if (config.cache_segments > 0) { if (config.cache_segments > 0) {
replay.setSegmentCacheLimit(config.cache_segments); replay.setSegmentCacheLimit(config.cache_segments);
} }

@ -15,8 +15,8 @@ void notifyEvent(Callback &callback, Args &&...args) {
} }
Replay::Replay(const std::string &route, std::vector<std::string> allow, std::vector<std::string> block, Replay::Replay(const std::string &route, std::vector<std::string> allow, std::vector<std::string> block,
SubMaster *sm, uint32_t flags, const std::string &data_dir) SubMaster *sm, uint32_t flags, const std::string &data_dir, bool auto_source)
: sm_(sm), flags_(flags), seg_mgr_(std::make_unique<SegmentManager>(route, flags, data_dir)) { : sm_(sm), flags_(flags), seg_mgr_(std::make_unique<SegmentManager>(route, flags, data_dir, auto_source)) {
std::signal(SIGUSR1, interrupt_sleep_handler); std::signal(SIGUSR1, interrupt_sleep_handler);
if (!(flags_ & REPLAY_FLAG_ALL_SERVICES)) { if (!(flags_ & REPLAY_FLAG_ALL_SERVICES)) {

@ -29,7 +29,7 @@ enum REPLAY_FLAGS {
class Replay { class Replay {
public: public:
Replay(const std::string &route, std::vector<std::string> allow, std::vector<std::string> block, SubMaster *sm = nullptr, Replay(const std::string &route, std::vector<std::string> allow, std::vector<std::string> block, SubMaster *sm = nullptr,
uint32_t flags = REPLAY_FLAG_NONE, const std::string &data_dir = ""); uint32_t flags = REPLAY_FLAG_NONE, const std::string &data_dir = "", bool auto_source = false);
~Replay(); ~Replay();
bool load(); bool load();
RouteLoadError lastRouteError() const { return route().lastError(); } RouteLoadError lastRouteError() const { return route().lastError(); }

@ -10,9 +10,8 @@
#include "tools/replay/replay.h" #include "tools/replay/replay.h"
#include "tools/replay/util.h" #include "tools/replay/util.h"
Route::Route(const std::string &route, const std::string &data_dir) : data_dir_(data_dir) { Route::Route(const std::string &route, const std::string &data_dir, bool auto_source)
route_ = parseRoute(route); : route_string_(route), data_dir_(data_dir), auto_source_(auto_source) {}
}
RouteIdentifier Route::parseRoute(const std::string &str) { RouteIdentifier Route::parseRoute(const std::string &str) {
RouteIdentifier identifier = {}; RouteIdentifier identifier = {};
@ -44,7 +43,7 @@ RouteIdentifier Route::parseRoute(const std::string &str) {
} }
bool Route::load() { bool Route::load() {
err_ = RouteLoadError::None; route_ = parseRoute(route_string_);
if (route_.str.empty() || (data_dir_.empty() && route_.dongle_id.empty())) { if (route_.str.empty() || (data_dir_.empty() && route_.dongle_id.empty())) {
rInfo("invalid route format"); rInfo("invalid route format");
return false; return false;
@ -56,17 +55,50 @@ bool Route::load() {
date_time_ = mktime(&tm_time); date_time_ = mktime(&tm_time);
} }
bool ret = data_dir_.empty() ? loadFromServer() : loadFromLocal(); if (!loadSegments()) {
if (ret) { rInfo("Failed to load segments");
if (route_.begin_segment == -1) route_.begin_segment = segments_.rbegin()->first; return false;
if (route_.end_segment == -1) route_.end_segment = segments_.rbegin()->first; }
for (auto it = segments_.begin(); it != segments_.end(); /**/) {
if (it->first < route_.begin_segment || it->first > route_.end_segment) { return true;
it = segments_.erase(it); }
} else {
++it; bool Route::loadSegments() {
if (!auto_source_) {
bool ret = data_dir_.empty() ? loadFromServer() : loadFromLocal();
if (ret) {
// Trim segments
if (route_.begin_segment > 0) {
segments_.erase(segments_.begin(), segments_.lower_bound(route_.begin_segment));
} }
if (route_.end_segment >= 0) {
segments_.erase(segments_.upper_bound(route_.end_segment), segments_.end());
}
}
return !segments_.empty();
}
return loadFromAutoSource();
}
bool Route::loadFromAutoSource() {
auto origin_prefix = getenv("OPENPILOT_PREFIX");
if (origin_prefix) {
setenv("OPENPILOT_PREFIX", "", 1);
}
auto cmd = util::string_format("../auto_source.py \"%s\"", route_string_.c_str());
auto log_files = split(util::check_output(cmd), '\n');
if (origin_prefix) {
setenv("OPENPILOT_PREFIX", origin_prefix, 1);
}
const static std::regex rx(R"(\/(\d+)\/)");
for (int i = 0; i < log_files.size(); ++i) {
int seg_num = i;
std::smatch match;
if (std::regex_search(log_files[i], match, rx)) {
seg_num = std::stoi(match[1]);
} }
addFileToSegment(seg_num, log_files[i]);
} }
return !segments_.empty(); return !segments_.empty();
} }

@ -40,7 +40,7 @@ struct SegmentFile {
class Route { class Route {
public: public:
Route(const std::string &route, const std::string &data_dir = {}); Route(const std::string &route, const std::string &data_dir = {}, bool auto_source = false);
bool load(); bool load();
RouteLoadError lastError() const { return err_; } RouteLoadError lastError() const { return err_; }
inline const std::string &name() const { return route_.str; } inline const std::string &name() const { return route_.str; }
@ -52,6 +52,8 @@ public:
static RouteIdentifier parseRoute(const std::string &str); static RouteIdentifier parseRoute(const std::string &str);
protected: protected:
bool loadSegments();
bool loadFromAutoSource();
bool loadFromLocal(); bool loadFromLocal();
bool loadFromServer(int retries = 3); bool loadFromServer(int retries = 3);
bool loadFromJson(const std::string &json); bool loadFromJson(const std::string &json);
@ -61,6 +63,8 @@ protected:
std::map<int, SegmentFile> segments_; std::map<int, SegmentFile> segments_;
std::time_t date_time_ = 0; std::time_t date_time_ = 0;
RouteLoadError err_ = RouteLoadError::None; RouteLoadError err_ = RouteLoadError::None;
bool auto_source_ = false;
std::string route_string_;
}; };
class Segment { class Segment {

@ -20,8 +20,8 @@ public:
bool isSegmentLoaded(int n) const { return segments.find(n) != segments.end(); } bool isSegmentLoaded(int n) const { return segments.find(n) != segments.end(); }
}; };
SegmentManager(const std::string &route_name, uint32_t flags, const std::string &data_dir = "") SegmentManager(const std::string &route_name, uint32_t flags, const std::string &data_dir = "", bool auto_source = false)
: flags_(flags), route_(route_name, data_dir), event_data_(std::make_shared<EventData>()) {} : flags_(flags), route_(route_name, data_dir, auto_source), event_data_(std::make_shared<EventData>()) {}
~SegmentManager(); ~SegmentManager();
bool load(); bool load();

Loading…
Cancel
Save