parent
cdf2e75cc3
commit
b551e7c1a2
5 changed files with 168 additions and 19 deletions
@ -0,0 +1,123 @@ |
||||
#include "tools/cabana/streams/routes.h" |
||||
|
||||
#include <QDateTime> |
||||
#include <QDialogButtonBox> |
||||
#include <QFormLayout> |
||||
#include <QJsonArray> |
||||
#include <QJsonDocument> |
||||
#include <QListWidget> |
||||
#include <QMessageBox> |
||||
#include <QPainter> |
||||
|
||||
#include "system/hardware/hw.h" |
||||
|
||||
// The RouteListWidget class extends QListWidget to display a custom message when empty
|
||||
class RouteListWidget : public QListWidget { |
||||
public: |
||||
RouteListWidget(QWidget *parent = nullptr) : QListWidget(parent) {} |
||||
void setEmptyText(const QString &text) { |
||||
empty_text_ = text; |
||||
viewport()->update(); |
||||
} |
||||
void paintEvent(QPaintEvent *event) override { |
||||
QListWidget::paintEvent(event); |
||||
if (count() == 0) { |
||||
QPainter painter(viewport()); |
||||
painter.drawText(viewport()->rect(), Qt::AlignCenter, empty_text_); |
||||
} |
||||
} |
||||
QString empty_text_ = tr("No items"); |
||||
}; |
||||
|
||||
RoutesDialog::RoutesDialog(QWidget *parent) : QDialog(parent) { |
||||
setWindowTitle(tr("Remote routes")); |
||||
|
||||
QFormLayout *layout = new QFormLayout(this); |
||||
layout->addRow(tr("Device"), device_list_ = new QComboBox(this)); |
||||
layout->addRow(tr("Duration"), period_selector_ = new QComboBox(this)); |
||||
layout->addRow(route_list_ = new RouteListWidget(this)); |
||||
auto button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); |
||||
layout->addRow(button_box); |
||||
|
||||
device_list_->addItem(tr("Loading...")); |
||||
// Populate period selector with predefined durations
|
||||
period_selector_->addItem(tr("Last week"), 7); |
||||
period_selector_->addItem(tr("Last 2 weeks"), 14); |
||||
period_selector_->addItem(tr("Last month"), 30); |
||||
period_selector_->addItem(tr("Last 6 months"), 180); |
||||
|
||||
// Connect signals and slots
|
||||
connect(device_list_, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RoutesDialog::fetchRoutes); |
||||
connect(period_selector_, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RoutesDialog::fetchRoutes); |
||||
connect(route_list_, &QListWidget::itemDoubleClicked, this, &QDialog::accept); |
||||
QObject::connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); |
||||
QObject::connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); |
||||
|
||||
// Send request to fetch devices
|
||||
HttpRequest *http = new HttpRequest(this, !Hardware::PC()); |
||||
QObject::connect(http, &HttpRequest::requestDone, this, &RoutesDialog::parseDeviceList); |
||||
http->sendRequest(CommaApi::BASE_URL + "/v1/me/devices/"); |
||||
} |
||||
|
||||
void RoutesDialog::parseDeviceList(const QString &json, bool success, QNetworkReply::NetworkError err) { |
||||
if (success) { |
||||
device_list_->clear(); |
||||
auto devices = QJsonDocument::fromJson(json.toUtf8()).array(); |
||||
for (const QJsonValue &device : devices) { |
||||
QString dongle_id = device["dongle_id"].toString(); |
||||
device_list_->addItem(dongle_id, dongle_id); |
||||
} |
||||
} else { |
||||
bool unauthorized = (err == QNetworkReply::ContentAccessDenied || err == QNetworkReply::AuthenticationRequiredError); |
||||
QMessageBox::warning(this, tr("Error"), unauthorized ? tr("Unauthorized, Authenticate with tools/lib/auth.py") : tr("Network error")); |
||||
reject(); |
||||
} |
||||
sender()->deleteLater(); |
||||
} |
||||
|
||||
void RoutesDialog::fetchRoutes() { |
||||
if (device_list_->currentIndex() == -1 || device_list_->currentData().isNull()) |
||||
return; |
||||
|
||||
route_list_->clear(); |
||||
route_list_->setEmptyText(tr("Loading...")); |
||||
|
||||
HttpRequest *http = new HttpRequest(this, !Hardware::PC()); |
||||
QObject::connect(http, &HttpRequest::requestDone, this, &RoutesDialog::parseRouteList); |
||||
|
||||
// Construct URL with selected device and date range
|
||||
auto dongle_id = device_list_->currentData().toString(); |
||||
QDateTime current = QDateTime::currentDateTime(); |
||||
QString url = QString("%1/v1/devices/%2/routes_segments?start=%3&end=%4") |
||||
.arg(CommaApi::BASE_URL).arg(dongle_id) |
||||
.arg(current.addDays(-(period_selector_->currentData().toInt())).toMSecsSinceEpoch()) |
||||
.arg(current.toMSecsSinceEpoch()); |
||||
http->sendRequest(url); |
||||
} |
||||
|
||||
void RoutesDialog::parseRouteList(const QString &json, bool success, QNetworkReply::NetworkError err) { |
||||
if (success) { |
||||
for (const QJsonValue &route : QJsonDocument::fromJson(json.toUtf8()).array()) { |
||||
uint64_t start_time = route["start_time_utc_millis"].toDouble(); |
||||
uint64_t end_time = route["end_time_utc_millis"].toDouble(); |
||||
auto datetime = QDateTime::fromMSecsSinceEpoch(start_time); |
||||
auto item = new QListWidgetItem(QString("%1 %2min").arg(datetime.toString()).arg((end_time - start_time) / (1000 * 60))); |
||||
item->setData(Qt::UserRole, route["fullname"].toString()); |
||||
route_list_->addItem(item); |
||||
} |
||||
// Select first route if available
|
||||
if (route_list_->count() > 0) route_list_->setCurrentRow(0); |
||||
} else { |
||||
QMessageBox::warning(this, tr("Error"), tr("Failed to fetch routes. Check your network connection.")); |
||||
reject(); |
||||
} |
||||
route_list_->setEmptyText(tr("No items")); |
||||
sender()->deleteLater(); |
||||
} |
||||
|
||||
void RoutesDialog::accept() { |
||||
if (auto current_item = route_list_->currentItem()) { |
||||
route_ = current_item->data(Qt::UserRole).toString(); |
||||
} |
||||
QDialog::accept(); |
||||
} |
@ -0,0 +1,26 @@ |
||||
#pragma once |
||||
|
||||
#include <QComboBox> |
||||
#include <QDialog> |
||||
|
||||
#include "selfdrive/ui/qt/api.h" |
||||
|
||||
class RouteListWidget; |
||||
|
||||
class RoutesDialog : public QDialog { |
||||
Q_OBJECT |
||||
public: |
||||
RoutesDialog(QWidget *parent); |
||||
QString route() const { return route_; } |
||||
|
||||
protected: |
||||
void accept() override; |
||||
void parseDeviceList(const QString &json, bool success, QNetworkReply::NetworkError err); |
||||
void parseRouteList(const QString &json, bool success, QNetworkReply::NetworkError err); |
||||
void fetchRoutes(); |
||||
|
||||
QComboBox *device_list_; |
||||
QComboBox *period_selector_; |
||||
RouteListWidget *route_list_; |
||||
QString route_; |
||||
}; |
Loading…
Reference in new issue