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.
		
		
		
		
		
			
		
			
				
					
					
						
							123 lines
						
					
					
						
							4.9 KiB
						
					
					
				
			
		
		
	
	
							123 lines
						
					
					
						
							4.9 KiB
						
					
					
				| #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();
 | |
| }
 | |
| 
 |