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.

208 lines
6.4 KiB

#include "selfdrive/ui/replay/util.h"
#include <array>
#include <cassert>
#include <iostream>
#include <mutex>
#include <numeric>
#include <bzlib.h>
#include <curl/curl.h>
#include "selfdrive/common/timing.h"
#include "selfdrive/common/util.h"
struct CURLGlobalInitializer {
CURLGlobalInitializer() { curl_global_init(CURL_GLOBAL_DEFAULT); }
~CURLGlobalInitializer() { curl_global_cleanup(); }
};
struct MultiPartWriter {
int64_t offset;
int64_t end;
int64_t written;
FILE *fp;
};
static size_t write_cb(char *data, size_t size, size_t count, void *userp) {
MultiPartWriter *w = (MultiPartWriter *)userp;
fseek(w->fp, w->offset, SEEK_SET);
fwrite(data, size, count, w->fp);
size_t bytes = size * count;
w->offset += bytes;
w->written += bytes;
return bytes;
}
static size_t dumy_write_cb(char *data, size_t size, size_t count, void *userp) { return size * count; }
int64_t getRemoteFileSize(const std::string &url) {
CURL *curl = curl_easy_init();
if (!curl) return -1;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dumy_write_cb);
curl_easy_setopt(curl, CURLOPT_HEADER, 1);
curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
CURLcode res = curl_easy_perform(curl);
double content_length = -1;
if (res == CURLE_OK) {
res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &content_length);
} else {
std::cout << "Download failed: error code: " << res << std::endl;
}
curl_easy_cleanup(curl);
return res == CURLE_OK ? (int64_t)content_length : -1;
}
std::string formattedDataSize(size_t size) {
if (size < 1024) {
return std::to_string(size) + " B";
} else if (size < 1024 * 1024) {
return util::string_format("%.2f KB", (float)size / 1024);
} else {
return util::string_format("%.2f MB", (float)size / (1024 * 1024));
}
}
static std::atomic<bool> enable_http_logging = false;
void enableHttpLogging(bool enable) {
enable_http_logging = enable;
}
bool httpMultiPartDownload(const std::string &url, const std::string &target_file, int parts, std::atomic<bool> *abort) {
static CURLGlobalInitializer curl_initializer;
static std::mutex lock;
static uint64_t total_written = 0, prev_total_written = 0;
static double last_print_ts = 0;
int64_t content_length = getRemoteFileSize(url);
if (content_length <= 0) return false;
// create a tmp sparse file
const std::string tmp_file = target_file + ".tmp";
FILE *fp = fopen(tmp_file.c_str(), "wb");
assert(fp);
fseek(fp, content_length - 1, SEEK_SET);
fwrite("\0", 1, 1, fp);
CURLM *cm = curl_multi_init();
std::map<CURL *, MultiPartWriter> writers;
const int part_size = content_length / parts;
for (int i = 0; i < parts; ++i) {
CURL *eh = curl_easy_init();
writers[eh] = {
.fp = fp,
.offset = i * part_size,
.end = i == parts - 1 ? content_length - 1 : (i + 1) * part_size - 1,
};
curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(eh, CURLOPT_WRITEDATA, (void *)(&writers[eh]));
curl_easy_setopt(eh, CURLOPT_URL, url.c_str());
curl_easy_setopt(eh, CURLOPT_RANGE, util::string_format("%d-%d", writers[eh].offset, writers[eh].end).c_str());
curl_easy_setopt(eh, CURLOPT_HTTPGET, 1);
curl_easy_setopt(eh, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(eh, CURLOPT_FOLLOWLOCATION, 1);
curl_multi_add_handle(cm, eh);
}
int still_running = 1;
size_t prev_written = 0;
while (still_running > 0 && !(abort && *abort)) {
curl_multi_wait(cm, nullptr, 0, 1000, nullptr);
curl_multi_perform(cm, &still_running);
size_t written = std::accumulate(writers.begin(), writers.end(), 0, [=](int v, auto &w) { return v + w.second.written; });
int cur_written = written - prev_written;
prev_written = written;
std::lock_guard lk(lock);
double ts = millis_since_boot();
total_written += cur_written;
if ((ts - last_print_ts) > 2 * 1000) {
if (enable_http_logging && last_print_ts > 0) {
size_t average = (total_written - prev_total_written) / ((ts - last_print_ts) / 1000.);
int progress = std::min<int>(100, 100.0 * (double)written / (double)content_length);
size_t idx = url.find("?");
std::cout << "downloading " << (idx == std::string::npos ? url : url.substr(0, idx)) << " - " << progress << "% (" << formattedDataSize(average) << "/s)" << std::endl;
}
prev_total_written = total_written;
last_print_ts = ts;
}
}
CURLMsg *msg;
int msgs_left = -1;
int complete = 0;
while ((msg = curl_multi_info_read(cm, &msgs_left)) && !(abort && *abort)) {
if (msg->msg == CURLMSG_DONE) {
if (msg->data.result == CURLE_OK) {
long res_status = 0;
curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &res_status);
if (res_status == 206) {
complete++;
} else {
std::cout << "Download failed: http error code: " << res_status << std::endl;
}
} else {
std::cout << "Download failed: connection failure: " << msg->data.result << std::endl;
}
}
}
for (auto &[e, w] : writers) {
curl_multi_remove_handle(cm, e);
curl_easy_cleanup(e);
}
curl_multi_cleanup(cm);
fclose(fp);
bool ret = complete == parts;
ret = ret && ::rename(tmp_file.c_str(), target_file.c_str()) == 0;
return ret;
}
bool readBZ2File(const std::string_view file, std::ostream &stream) {
std::unique_ptr<FILE, decltype(&fclose)> f(fopen(file.data(), "r"), &fclose);
if (!f) return false;
int bzerror = BZ_OK;
BZFILE *bz_file = BZ2_bzReadOpen(&bzerror, f.get(), 0, 0, nullptr, 0);
if (!bz_file) return false;
std::array<char, 64 * 1024> buf;
do {
int size = BZ2_bzRead(&bzerror, bz_file, buf.data(), buf.size());
if (bzerror == BZ_OK || bzerror == BZ_STREAM_END) {
stream.write(buf.data(), size);
}
} while (bzerror == BZ_OK);
bool success = (bzerror == BZ_STREAM_END);
BZ2_bzReadClose(&bzerror, bz_file);
return success;
}
void precise_nano_sleep(long sleep_ns) {
const long estimate_ns = 1 * 1e6; // 1ms
struct timespec req = {.tv_nsec = estimate_ns};
uint64_t start_sleep = nanos_since_boot();
while (sleep_ns > estimate_ns) {
nanosleep(&req, nullptr);
uint64_t end_sleep = nanos_since_boot();
sleep_ns -= (end_sleep - start_sleep);
start_sleep = end_sleep;
}
// spin wait
if (sleep_ns > 0) {
while ((nanos_since_boot() - start_sleep) <= sleep_ns) {
usleep(0);
}
}
}