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.
 
 
 
 
 
 

217 lines
6.9 KiB

#include "tools/replay/route.h"
#include <array>
#include <filesystem>
#include <regex>
#include "third_party/json11/json11.hpp"
#include "system/hardware/hw.h"
#include "tools/replay/api.h"
#include "tools/replay/replay.h"
#include "tools/replay/util.h"
Route::Route(const std::string &route, const std::string &data_dir) : data_dir_(data_dir) {
route_ = parseRoute(route);
}
RouteIdentifier Route::parseRoute(const std::string &str) {
RouteIdentifier identifier = {};
static const std::regex pattern(R"(^(([a-z0-9]{16})[|_/])?(.{20})((--|/)((-?\d+(:(-?\d+)?)?)|(:-?\d+)))?$)");
std::smatch match;
if (std::regex_match(str, match, pattern)) {
identifier.dongle_id = match[2].str();
identifier.timestamp = match[3].str();
identifier.str = identifier.dongle_id + "|" + identifier.timestamp;
const auto separator = match[5].str();
const auto range_str = match[6].str();
if (!range_str.empty()) {
if (separator == "/") {
int pos = range_str.find(':');
int begin_seg = std::stoi(range_str.substr(0, pos));
identifier.begin_segment = identifier.end_segment = begin_seg;
if (pos != std::string::npos) {
auto end_seg_str = range_str.substr(pos + 1);
identifier.end_segment = end_seg_str.empty() ? -1 : std::stoi(end_seg_str);
}
} else if (separator == "--") {
identifier.begin_segment = std::atoi(range_str.c_str());
}
}
}
return identifier;
}
bool Route::load() {
err_ = RouteLoadError::None;
if (route_.str.empty() || (data_dir_.empty() && route_.dongle_id.empty())) {
rInfo("invalid route format");
return false;
}
struct tm tm_time = {0};
strptime(route_.timestamp.c_str(), "%Y-%m-%d--%H-%M-%S", &tm_time);
date_time_ = mktime(&tm_time);
bool ret = data_dir_.empty() ? loadFromServer() : loadFromLocal();
if (ret) {
if (route_.begin_segment == -1) route_.begin_segment = segments_.rbegin()->first;
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) {
it = segments_.erase(it);
} else {
++it;
}
}
}
return !segments_.empty();
}
bool Route::loadFromServer(int retries) {
const std::string url = CommaApi2::BASE_URL + "/v1/route/" + route_.str + "/files";
for (int i = 1; i <= retries; ++i) {
long response_code = 0;
std::string result = CommaApi2::httpGet(url, &response_code);
if (response_code == 200) {
return loadFromJson(result);
}
if (response_code == 401 || response_code == 403) {
rWarning(">> Unauthorized. Authenticate with tools/lib/auth.py <<");
err_ = RouteLoadError::Unauthorized;
break;
}
if (response_code == 404) {
rWarning("The specified route could not be found on the server.");
err_ = RouteLoadError::FileNotFound;
break;
}
err_ = RouteLoadError::NetworkError;
rWarning("Retrying %d/%d", i, retries);
util::sleep_for(3000);
}
return false;
}
bool Route::loadFromJson(const std::string &json) {
const static std::regex rx(R"(\/(\d+)\/)");
std::string err;
auto jsonData = json11::Json::parse(json, err);
if (!err.empty()) {
rWarning("JSON parsing error: %s", err.c_str());
return false;
}
for (const auto &value : jsonData.object_items()) {
const auto &urlArray = value.second.array_items();
for (const auto &url : urlArray) {
std::string url_str = url.string_value();
std::smatch match;
if (std::regex_search(url_str, match, rx)) {
addFileToSegment(std::stoi(match[1]), url_str);
}
}
}
return !segments_.empty();
}
bool Route::loadFromLocal() {
std::string pattern = route_.timestamp + "--";
for (const auto &entry : std::filesystem::directory_iterator(data_dir_)) {
if (entry.is_directory() && entry.path().filename().string().find(pattern) == 0) {
std::string segment = entry.path().string();
int seg_num = std::atoi(segment.substr(segment.rfind("--") + 2).c_str());
for (const auto &file : std::filesystem::directory_iterator(segment)) {
if (file.is_regular_file()) {
addFileToSegment(seg_num, file.path().string());
}
}
}
}
return !segments_.empty();
}
void Route::addFileToSegment(int n, const std::string &file) {
std::string name = extractFileName(file);
auto pos = name.find_last_of("--");
name = pos != std::string::npos ? name.substr(pos + 2) : name;
if (name == "rlog.bz2" || name == "rlog.zst" || name == "rlog") {
segments_[n].rlog = file;
} else if (name == "qlog.bz2" || name == "qlog.zst" || name == "qlog") {
segments_[n].qlog = file;
} else if (name == "fcamera.hevc") {
segments_[n].road_cam = file;
} else if (name == "dcamera.hevc") {
segments_[n].driver_cam = file;
} else if (name == "ecamera.hevc") {
segments_[n].wide_road_cam = file;
} else if (name == "qcamera.ts") {
segments_[n].qcamera = file;
}
}
// class Segment
Segment::Segment(int n, const SegmentFile &files, uint32_t flags, const std::vector<bool> &filters,
std::function<void(int, bool)> callback)
: seg_num(n), flags(flags), filters_(filters), on_load_finished_(callback) {
// [RoadCam, DriverCam, WideRoadCam, log]. fallback to qcamera/qlog
const std::array file_list = {
(flags & REPLAY_FLAG_QCAMERA) || files.road_cam.empty() ? files.qcamera : files.road_cam,
flags & REPLAY_FLAG_DCAM ? files.driver_cam : "",
flags & REPLAY_FLAG_ECAM ? files.wide_road_cam : "",
files.rlog.empty() ? files.qlog : files.rlog,
};
for (int i = 0; i < file_list.size(); ++i) {
if (!file_list[i].empty() && (!(flags & REPLAY_FLAG_NO_VIPC) || i >= MAX_CAMERAS)) {
++loading_;
threads_.emplace_back(&Segment::loadFile, this, i, file_list[i]);
}
}
}
Segment::~Segment() {
{
std::lock_guard lock(mutex_);
on_load_finished_ = nullptr; // Prevent callback after destruction
}
abort_ = true;
for (auto &thread : threads_) {
if (thread.joinable()) thread.join();
}
}
void Segment::loadFile(int id, const std::string file) {
const bool local_cache = !(flags & REPLAY_FLAG_NO_FILE_CACHE);
bool success = false;
if (id < MAX_CAMERAS) {
frames[id] = std::make_unique<FrameReader>();
success = frames[id]->load((CameraType)id, file, flags & REPLAY_FLAG_NO_HW_DECODER, &abort_, local_cache, 20 * 1024 * 1024, 3);
} else {
log = std::make_unique<LogReader>(filters_);
success = log->load(file, &abort_, local_cache, 0, 3);
}
if (!success) {
// abort all loading jobs.
abort_ = true;
}
if (--loading_ == 0) {
std::lock_guard lock(mutex_);
load_state_ = !abort_ ? LoadState::Loaded : LoadState::Failed;
if (on_load_finished_) {
on_load_finished_(seg_num, !abort_);
}
}
}
Segment::LoadState Segment::getState() {
std::scoped_lock lock(mutex_);
return load_state_;
}