openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

200 lines
6.4 KiB

#include "selfdrive/ui/replay/route.h"
#include <QEventLoop>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QRegExp>
#include <QThread>
#include <future>
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/qt/api.h"
#include "selfdrive/ui/replay/util.h"
Route::Route(const QString &route, const QString &data_dir) : route_(route), data_dir_(data_dir) {}
bool Route::load() {
if (data_dir_.isEmpty()) {
QEventLoop loop;
auto onError = [&loop](const QString &err) { loop.quit(); };
bool ret = false;
HttpRequest http(nullptr, !Hardware::PC());
QObject::connect(&http, &HttpRequest::failedResponse, onError);
QObject::connect(&http, &HttpRequest::timeoutResponse, onError);
QObject::connect(&http, &HttpRequest::receivedResponse, [&](const QString json) {
ret = loadFromJson(json);
loop.quit();
});
http.sendRequest("https://api.commadotai.com/v1/route/" + route_ + "/files");
loop.exec();
return ret;
} else {
return loadFromLocal();
}
}
bool Route::loadFromJson(const QString &json) {
QJsonObject route_files = QJsonDocument::fromJson(json.trimmed().toUtf8()).object();
if (route_files.empty()) {
qInfo() << "JSON Parse failed";
return false;
}
QRegExp rx(R"(\/(\d+)\/)");
for (const QString &key : route_files.keys()) {
for (const auto &url : route_files[key].toArray()) {
QString url_str = url.toString();
if (rx.indexIn(url_str) != -1) {
const int seg_num = rx.cap(1).toInt();
if (segments_.size() <= seg_num) {
segments_.resize(seg_num + 1);
}
if (key == "logs") {
segments_[seg_num].rlog = url_str;
} else if (key == "qlogs") {
segments_[seg_num].qlog = url_str;
} else if (key == "cameras") {
segments_[seg_num].road_cam = url_str;
} else if (key == "dcameras") {
segments_[seg_num].driver_cam = url_str;
} else if (key == "ecameras") {
segments_[seg_num].wide_road_cam = url_str;
} else if (key == "qcameras") {
segments_[seg_num].qcamera = url_str;
}
}
}
}
return true;
}
bool Route::loadFromLocal() {
QString prefix = route_.split('|').last();
if (prefix.isEmpty()) return false;
QDir log_dir(data_dir_);
QStringList folders = log_dir.entryList(QDir::Dirs | QDir::NoDot | QDir::NoDotDot, QDir::NoSort);
if (folders.isEmpty()) return false;
for (auto folder : folders) {
const int seg_num = folder.split("--")[2].toInt();
if (segments_.size() <= seg_num) {
segments_.resize(seg_num + 1);
}
QDir segment_dir(log_dir.filePath(folder));
for (auto f : segment_dir.entryList(QDir::Files)) {
const QString file_path = segment_dir.absoluteFilePath(f);
if (f.startsWith("rlog")) {
segments_[seg_num].rlog = file_path;
} else if (f.startsWith("qlog")) {
segments_[seg_num].qlog = file_path;
} else if (f.startsWith("fcamera")) {
segments_[seg_num].road_cam = file_path;
} else if (f.startsWith("dcamera")) {
segments_[seg_num].driver_cam = file_path;
} else if (f.startsWith("ecamera")) {
segments_[seg_num].wide_road_cam = file_path;
} else if (f.startsWith("qcamera")) {
segments_[seg_num].qcamera = file_path;
}
}
}
return true;
}
// class Segment
Segment::Segment(int n, const SegmentFile &segment_files, bool load_dcam, bool load_ecam) : seg_num_(n), files_(segment_files) {
static std::once_flag once_flag;
std::call_once(once_flag, [=]() {
if (!CACHE_DIR.exists()) QDir().mkdir(CACHE_DIR.absolutePath());
});
// fallback to qcamera/qlog
road_cam_path_ = files_.road_cam.isEmpty() ? files_.qcamera : files_.road_cam;
log_path_ = files_.rlog.isEmpty() ? files_.qlog : files_.rlog;
assert (!log_path_.isEmpty() && !road_cam_path_.isEmpty());
if (!load_dcam) {
files_.driver_cam = "";
}
if (!load_ecam) {
files_.wide_road_cam = "";
}
if (!QUrl(log_path_).isLocalFile()) {
for (auto &url : {log_path_, road_cam_path_, files_.driver_cam, files_.wide_road_cam}) {
if (!url.isEmpty() && !QFile::exists(localPath(url))) {
downloadFile(url);
++downloading_;
}
}
}
if (downloading_ == 0) {
QTimer::singleShot(0, this, &Segment::load);
} else {
qDebug() << "downloading segment" << seg_num_ << "...";
}
}
Segment::~Segment() {
aborting_ = true;
if (downloading_ > 0) {
qDebug() << "cancel download segment" << seg_num_;
}
for (auto &t : download_threads_) {
if (t->isRunning()) t->wait();
}
}
void Segment::downloadFile(const QString &url) {
download_threads_.emplace_back(QThread::create([=]() {
const std::string local_file = localPath(url).toStdString();
bool ret = httpMultiPartDownload(url.toStdString(), local_file, connections_per_file, &aborting_);
if (ret && url == log_path_) {
// pre-decompress log file.
std::ofstream ostrm(local_file + "_decompressed", std::ios::binary);
readBZ2File(local_file, ostrm);
}
if (--downloading_ == 0 && !aborting_) {
load();
}
}))->start();
}
// load concurrency
void Segment::load() {
std::vector<std::future<bool>> futures;
futures.emplace_back(std::async(std::launch::async, [=]() {
const std::string bzip_file = localPath(log_path_).toStdString();
const std::string decompressed_file = bzip_file + "_decompressed";
bool is_bzip = !util::file_exists(decompressed_file);
log = std::make_unique<LogReader>();
return log->load(is_bzip ? bzip_file : decompressed_file, is_bzip);
}));
QString camera_files[] = {road_cam_path_, files_.driver_cam, files_.wide_road_cam};
for (int i = 0; i < std::size(camera_files); ++i) {
if (!camera_files[i].isEmpty()) {
futures.emplace_back(std::async(std::launch::async, [=]() {
frames[i] = std::make_unique<FrameReader>();
return frames[i]->load(localPath(camera_files[i]).toStdString());
}));
}
}
int success_cnt = std::accumulate(futures.begin(), futures.end(), 0, [=](int v, auto &f) { return f.get() + v; });
loaded_ = (success_cnt == futures.size());
emit loadFinished();
}
QString Segment::localPath(const QUrl &url) {
if (url.isLocalFile() || QFile(url.toString()).exists()) return url.toString();
QByteArray url_no_query = url.toString(QUrl::RemoveQuery).toUtf8();
return CACHE_DIR.filePath(QString(QCryptographicHash::hash(url_no_query, QCryptographicHash::Sha256).toHex()));
}