This commit is contained in:
Your Name
2024-05-17 11:14:45 -05:00
parent 54a65f1697
commit eb05d8bfd3
38 changed files with 101 additions and 3691 deletions

View File

@@ -1,82 +0,0 @@
# PFEIFER - MAPD - Modified by FrogAi for FrogPilot to automatically update
import os
import socket
import stat
import subprocess
import time
import urllib.error
import urllib.request
from openpilot.common.realtime import Ratekeeper
VERSION = 'v1'
VERSION_URL = f"https://github.com/FrogAi/FrogPilot-Resources/raw/master/mapd_version_{VERSION}.txt"
MAPD_PATH = '/data/media/0/osm/mapd'
VERSION_PATH = '/data/media/0/osm/mapd_version'
def github_pinged(url="https://github.com", timeout=5):
no_internet = 0
while True:
if no_internet > 5:
break
try:
urllib.request.urlopen(url, timeout=timeout)
return True
except (urllib.error.URLError, socket.timeout):
no_internet += 1
time.sleep(10)
return False
def get_latest_version():
with urllib.request.urlopen(VERSION_URL) as response:
return response.read().decode('utf-8').strip()
def download(current_version):
url = f"https://github.com/pfeiferj/openpilot-mapd/releases/download/{current_version}/mapd"
mapd_dir = os.path.dirname(MAPD_PATH)
if not os.path.exists(mapd_dir):
os.makedirs(mapd_dir)
with urllib.request.urlopen(url) as f:
with open(MAPD_PATH, 'wb') as output:
output.write(f.read())
os.fsync(output)
current_permissions = stat.S_IMODE(os.lstat(MAPD_PATH).st_mode) # <-- preserve permissions
os.chmod(MAPD_PATH, current_permissions | stat.S_IEXEC) # <-- preserve permissions
with open(VERSION_PATH, 'w') as output:
output.write(current_version)
os.fsync(output)
def mapd_thread(sm=None, pm=None):
rk = Ratekeeper(0.05, print_delay_threshold=None)
while True:
try:
if github_pinged():
current_version = get_latest_version()
if not os.path.exists(MAPD_PATH):
download(current_version)
continue
if not os.path.exists(VERSION_PATH):
download(current_version)
continue
with open(VERSION_PATH) as f:
content = f.read()
if content != current_version:
download(current_version)
continue
process = subprocess.Popen(MAPD_PATH)
process.wait()
except Exception as e:
print(e)
rk.keep_time()
def main(sm=None, pm=None):
mapd_thread(sm, pm)
if __name__ == "__main__":
main()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 654 KiB

View File

@@ -1,329 +0,0 @@
/****************************************************************************
** Meta object code from reading C++ file 'navigation_settings.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.12.8)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
#include "navigation_settings.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'navigation_settings.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.12.8. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif
QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_Primeless_t {
QByteArrayData data[3];
char stringdata0[21];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_Primeless_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_Primeless_t qt_meta_stringdata_Primeless = {
{
QT_MOC_LITERAL(0, 0, 9), // "Primeless"
QT_MOC_LITERAL(1, 10, 9), // "backPress"
QT_MOC_LITERAL(2, 20, 0) // ""
},
"Primeless\0backPress\0"
};
#undef QT_MOC_LITERAL
static const uint qt_meta_data_Primeless[] = {
// content:
8, // revision
0, // classname
0, 0, // classinfo
1, 14, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
1, // signalCount
// signals: name, argc, parameters, tag, flags
1, 0, 19, 2, 0x06 /* Public */,
// signals: parameters
QMetaType::Void,
0 // eod
};
void Primeless::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
auto *_t = static_cast<Primeless *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->backPress(); break;
default: ;
}
} else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast<int *>(_a[0]);
{
using _t = void (Primeless::*)();
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Primeless::backPress)) {
*result = 0;
return;
}
}
}
Q_UNUSED(_a);
}
QT_INIT_METAOBJECT const QMetaObject Primeless::staticMetaObject = { {
&QWidget::staticMetaObject,
qt_meta_stringdata_Primeless.data,
qt_meta_data_Primeless,
qt_static_metacall,
nullptr,
nullptr
} };
const QMetaObject *Primeless::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void *Primeless::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_Primeless.stringdata0))
return static_cast<void*>(this);
return QWidget::qt_metacast(_clname);
}
int Primeless::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QWidget::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id < 1)
qt_static_metacall(this, _c, _id, _a);
_id -= 1;
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
if (_id < 1)
*reinterpret_cast<int*>(_a[0]) = -1;
_id -= 1;
}
return _id;
}
// SIGNAL 0
void Primeless::backPress()
{
QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}
struct qt_meta_stringdata_SelectMaps_t {
QByteArrayData data[4];
char stringdata0[30];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_SelectMaps_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_SelectMaps_t qt_meta_stringdata_SelectMaps = {
{
QT_MOC_LITERAL(0, 0, 10), // "SelectMaps"
QT_MOC_LITERAL(1, 11, 9), // "backPress"
QT_MOC_LITERAL(2, 21, 0), // ""
QT_MOC_LITERAL(3, 22, 7) // "setMaps"
},
"SelectMaps\0backPress\0\0setMaps"
};
#undef QT_MOC_LITERAL
static const uint qt_meta_data_SelectMaps[] = {
// content:
8, // revision
0, // classname
0, 0, // classinfo
2, 14, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
2, // signalCount
// signals: name, argc, parameters, tag, flags
1, 0, 24, 2, 0x06 /* Public */,
3, 0, 25, 2, 0x06 /* Public */,
// signals: parameters
QMetaType::Void,
QMetaType::Void,
0 // eod
};
void SelectMaps::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
auto *_t = static_cast<SelectMaps *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->backPress(); break;
case 1: _t->setMaps(); break;
default: ;
}
} else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast<int *>(_a[0]);
{
using _t = void (SelectMaps::*)();
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&SelectMaps::backPress)) {
*result = 0;
return;
}
}
{
using _t = void (SelectMaps::*)();
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&SelectMaps::setMaps)) {
*result = 1;
return;
}
}
}
Q_UNUSED(_a);
}
QT_INIT_METAOBJECT const QMetaObject SelectMaps::staticMetaObject = { {
&QWidget::staticMetaObject,
qt_meta_stringdata_SelectMaps.data,
qt_meta_data_SelectMaps,
qt_static_metacall,
nullptr,
nullptr
} };
const QMetaObject *SelectMaps::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void *SelectMaps::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_SelectMaps.stringdata0))
return static_cast<void*>(this);
return QWidget::qt_metacast(_clname);
}
int SelectMaps::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QWidget::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id < 2)
qt_static_metacall(this, _c, _id, _a);
_id -= 2;
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
if (_id < 2)
*reinterpret_cast<int*>(_a[0]) = -1;
_id -= 2;
}
return _id;
}
// SIGNAL 0
void SelectMaps::backPress()
{
QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}
// SIGNAL 1
void SelectMaps::setMaps()
{
QMetaObject::activate(this, &staticMetaObject, 1, nullptr);
}
struct qt_meta_stringdata_FrogPilotNavigationPanel_t {
QByteArrayData data[1];
char stringdata0[25];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_FrogPilotNavigationPanel_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_FrogPilotNavigationPanel_t qt_meta_stringdata_FrogPilotNavigationPanel = {
{
QT_MOC_LITERAL(0, 0, 24) // "FrogPilotNavigationPanel"
},
"FrogPilotNavigationPanel"
};
#undef QT_MOC_LITERAL
static const uint qt_meta_data_FrogPilotNavigationPanel[] = {
// content:
8, // revision
0, // classname
0, 0, // classinfo
0, 0, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
0, // signalCount
0 // eod
};
void FrogPilotNavigationPanel::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
Q_UNUSED(_o);
Q_UNUSED(_id);
Q_UNUSED(_c);
Q_UNUSED(_a);
}
QT_INIT_METAOBJECT const QMetaObject FrogPilotNavigationPanel::staticMetaObject = { {
&QFrame::staticMetaObject,
qt_meta_stringdata_FrogPilotNavigationPanel.data,
qt_meta_data_FrogPilotNavigationPanel,
qt_static_metacall,
nullptr,
nullptr
} };
const QMetaObject *FrogPilotNavigationPanel::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void *FrogPilotNavigationPanel::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_FrogPilotNavigationPanel.stringdata0))
return static_cast<void*>(this);
return QFrame::qt_metacast(_clname);
}
int FrogPilotNavigationPanel::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QFrame::qt_metacall(_c, _id, _a);
return _id;
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE

View File

@@ -1,311 +0,0 @@
#pragma once
#include <deque>
#include <filesystem>
#include <QDir>
#include <QDirIterator>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include "selfdrive/ui/qt/offroad/settings.h"
#include "selfdrive/ui/qt/widgets/controls.h"
#include "selfdrive/ui/ui.h"
QMap<QString, QString> northeastMap = {
{"CT", "Connecticut"}, {"DE", "Delaware"}, {"MA", "Massachusetts"},
{"MD", "Maryland"}, {"ME", "Maine"}, {"NH", "New Hampshire"},
{"NJ", "New Jersey"}, {"NY", "New York"}, {"PA", "Pennsylvania"},
{"RI", "Rhode Island"}, {"VT", "Vermont"}
};
QMap<QString, QString> midwestMap = {
{"IA", "Iowa"}, {"IL", "Illinois"}, {"IN", "Indiana"},
{"KS", "Kansas"}, {"MI", "Michigan"}, {"MN", "Minnesota"},
{"MO", "Missouri"}, {"ND", "North Dakota"}, {"NE", "Nebraska"},
{"OH", "Ohio"}, {"SD", "South Dakota"}, {"WI", "Wisconsin"}
};
QMap<QString, QString> southMap = {
{"AL", "Alabama"}, {"AR", "Arkansas"}, {"FL", "Florida"},
{"GA", "Georgia"}, {"KY", "Kentucky"}, {"LA", "Louisiana"},
{"MS", "Mississippi"}, {"NC", "North Carolina"}, {"OK", "Oklahoma"},
{"SC", "South Carolina"}, {"TN", "Tennessee"}, {"TX", "Texas"},
{"VA", "Virginia"}, {"WV", "West Virginia"}
};
QMap<QString, QString> westMap = {
{"AK", "Alaska"}, {"AZ", "Arizona"}, {"CA", "California"},
{"CO", "Colorado"}, {"HI", "Hawaii"}, {"ID", "Idaho"},
{"MT", "Montana"}, {"NM", "New Mexico"}, {"NV", "Nevada"},
{"OR", "Oregon"}, {"UT", "Utah"}, {"WA", "Washington"},
{"WY", "Wyoming"}
};
QMap<QString, QString> territoriesMap = {
{"AS", "American Samoa"}, {"DC", "District of Columbia"},
{"GM", "Guam"}, {"MP", "North Mariana Islands"},
{"PR", "Puerto Rico"}, {"VI", "Virgin Islands"}
};
QMap<QString, QString> africaMap = {
{"DZ", "Algeria"}, {"AO", "Angola"}, {"BJ", "Benin"}, {"BW", "Botswana"},
{"BF", "Burkina Faso"}, {"BI", "Burundi"}, {"CM", "Cameroon"}, {"CV", "Cape Verde"},
{"CF", "Central African Republic"}, {"TD", "Chad"}, {"KM", "Comoros"}, {"CG", "Congo (Brazzaville)"},
{"CD", "Congo (Kinshasa)"}, {"CI", "Ivory Coast"}, {"DJ", "Djibouti"}, {"EG", "Egypt"},
{"GQ", "Equatorial Guinea"}, {"ER", "Eritrea"}, {"SZ", "Eswatini"}, {"ET", "Ethiopia"},
{"GA", "Gabon"}, {"GM", "Gambia"}, {"GH", "Ghana"}, {"GN", "Guinea"},
{"GW", "Guinea-Bissau"}, {"KE", "Kenya"}, {"LS", "Lesotho"}, {"LR", "Liberia"},
{"LY", "Libya"}, {"MG", "Madagascar"}, {"MW", "Malawi"}, {"ML", "Mali"},
{"MR", "Mauritania"}, {"MU", "Mauritius"}, {"MA", "Morocco"}, {"MZ", "Mozambique"},
{"NA", "Namibia"}, {"NE", "Niger"}, {"NG", "Nigeria"}, {"RW", "Rwanda"},
{"ST", "Sao Tome and Principe"}, {"SN", "Senegal"}, {"SC", "Seychelles"}, {"SL", "Sierra Leone"},
{"SO", "Somalia"}, {"ZA", "South Africa"}, {"SS", "South Sudan"}, {"SD", "Sudan"},
{"TZ", "Tanzania"}, {"TG", "Togo"}, {"TN", "Tunisia"}, {"UG", "Uganda"},
{"EH", "Western Sahara"}, {"ZM", "Zambia"}, {"ZW", "Zimbabwe"}
};
QMap<QString, QString> antarcticaMap = {
{"AQ", "Antarctica"}
};
QMap<QString, QString> asiaMap = {
{"AF", "Afghanistan"}, {"AM", "Armenia"}, {"AZ", "Azerbaijan"}, {"BH", "Bahrain"},
{"BD", "Bangladesh"}, {"BT", "Bhutan"}, {"BN", "Brunei"}, {"KH", "Cambodia"},
{"CN", "China"}, {"CY", "Cyprus"}, {"GE", "Georgia"}, {"IN", "India"},
{"ID", "Indonesia"}, {"IR", "Iran"}, {"IQ", "Iraq"}, {"IL", "Israel"},
{"JP", "Japan"}, {"JO", "Jordan"}, {"KZ", "Kazakhstan"}, {"KP", "North Korea"},
{"KR", "South Korea"}, {"KW", "Kuwait"}, {"KG", "Kyrgyzstan"}, {"LA", "Laos"},
{"LB", "Lebanon"}, {"MY", "Malaysia"}, {"MV", "Maldives"}, {"MN", "Mongolia"},
{"MM", "Myanmar"}, {"NP", "Nepal"}, {"OM", "Oman"}, {"PK", "Pakistan"},
{"PS", "Palestine"}, {"PH", "Philippines"}, {"QA", "Qatar"}, {"SA", "Saudi Arabia"},
{"SG", "Singapore"}, {"LK", "Sri Lanka"}, {"SY", "Syria"}, {"TW", "Taiwan"},
{"TJ", "Tajikistan"}, {"TH", "Thailand"}, {"TL", "Timor-Leste"}, {"TR", "Turkey"},
{"TM", "Turkmenistan"}, {"AE", "United Arab Emirates"}, {"UZ", "Uzbekistan"}, {"VN", "Vietnam"},
{"YE", "Yemen"}
};
QMap<QString, QString> europeMap = {
{"AL", "Albania"}, {"AD", "Andorra"}, {"AT", "Austria"}, {"BY", "Belarus"},
{"BE", "Belgium"}, {"BA", "Bosnia and Herzegovina"}, {"BG", "Bulgaria"}, {"HR", "Croatia"},
{"CY", "Cyprus"}, {"CZ", "Czech Republic"}, {"DK", "Denmark"}, {"EE", "Estonia"},
{"FI", "Finland"}, {"FR", "France"}, {"DE", "Germany"}, {"GR", "Greece"},
{"HU", "Hungary"}, {"IS", "Iceland"}, {"IE", "Ireland"}, {"IT", "Italy"},
{"LV", "Latvia"}, {"LI", "Liechtenstein"}, {"LT", "Lithuania"}, {"LU", "Luxembourg"},
{"MT", "Malta"}, {"MD", "Moldova"}, {"MC", "Monaco"}, {"ME", "Montenegro"},
{"NL", "Netherlands"}, {"MK", "North Macedonia"}, {"NO", "Norway"}, {"PL", "Poland"},
{"PT", "Portugal"}, {"RO", "Romania"}, {"RU", "Russia"}, {"SM", "San Marino"},
{"RS", "Serbia"}, {"SK", "Slovakia"}, {"SI", "Slovenia"}, {"ES", "Spain"},
{"SE", "Sweden"}, {"CH", "Switzerland"}, {"TR", "Turkey"}, {"UA", "Ukraine"},
{"GB", "United Kingdom"}, {"VA", "Vatican City"}
};
QMap<QString, QString> northAmericaMap = {
{"AG", "Antigua and Barbuda"}, {"BS", "Bahamas"}, {"BB", "Barbados"}, {"BZ", "Belize"},
{"CA", "Canada"}, {"CR", "Costa Rica"}, {"CU", "Cuba"}, {"DM", "Dominica"},
{"DO", "Dominican Republic"}, {"SV", "El Salvador"}, {"GD", "Grenada"}, {"GT", "Guatemala"},
{"HT", "Haiti"}, {"HN", "Honduras"}, {"JM", "Jamaica"}, {"MX", "Mexico"},
{"NI", "Nicaragua"}, {"PA", "Panama"}, {"KN", "Saint Kitts and Nevis"}, {"LC", "Saint Lucia"},
{"VC", "Saint Vincent and the Grenadines"}, {"TT", "Trinidad and Tobago"}, {"US", "United States"}
};
QMap<QString, QString> oceaniaMap = {
{"AU", "Australia"}, {"FJ", "Fiji"}, {"KI", "Kiribati"}, {"MH", "Marshall Islands"},
{"FM", "Micronesia"}, {"NR", "Nauru"}, {"NZ", "New Zealand"}, {"PW", "Palau"},
{"PG", "Papua New Guinea"}, {"WS", "Samoa"}, {"SB", "Solomon Islands"}, {"TO", "Tonga"},
{"TV", "Tuvalu"}, {"VU", "Vanuatu"}
};
QMap<QString, QString> southAmericaMap = {
{"AR", "Argentina"}, {"BO", "Bolivia"}, {"BR", "Brazil"}, {"CL", "Chile"},
{"CO", "Colombia"}, {"EC", "Ecuador"}, {"GY", "Guyana"}, {"PY", "Paraguay"},
{"PE", "Peru"}, {"SR", "Suriname"}, {"TT", "Trinidad and Tobago"}, {"UY", "Uruguay"},
{"VE", "Venezuela"}
};
class ButtonSelectionControl : public QWidget {
public:
static QString selectedStates;
static QString selectedCountries;
explicit ButtonSelectionControl(const QString &id, const QString &title, const QString &description,
const QMap<QString, QString> &map, bool isCountry, QWidget *parent = nullptr)
: QWidget(parent), country(isCountry) {
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setAlignment(Qt::AlignTop);
layout->setSpacing(10);
QHBoxLayout *buttonsLayout = new QHBoxLayout();
buttonsLayout->setSpacing(10);
layout->addLayout(buttonsLayout);
int count = 0;
int max = country ? 3 : 4;
QJsonObject mapsSelected = QJsonDocument::fromJson(QString::fromStdString(Params().get("MapsSelected")).toUtf8()).object();
for (const QString &stateCode : map.keys()) {
if (count % max == 0 && count != 0) {
buttonsLayout = new QHBoxLayout();
buttonsLayout->setSpacing(10);
layout->addLayout(buttonsLayout);
}
QPushButton *button = createButton(buttonsLayout, map[stateCode], stateCode);
QString key = country ? "nations" : "states";
if (mapsSelected.contains(key)) {
QJsonArray selectedItems = mapsSelected.value(key).toArray();
button->setChecked(selectedItems.contains(stateCode));
}
count++;
}
adjustButtonWidths(buttonsLayout);
}
private:
bool country;
const QString buttonStyle = R"(
QPushButton {
border-radius: 50px; font-size: 40px; font-weight: 500;
height: 100px; padding: 0 25 0 25; color: #E4E4E4;
background-color: #393939;
}
QPushButton:pressed, QPushButton:checked {
background-color: #4a4a4a;
}
QPushButton:checked:enabled {
background-color: #33Ab4C;
}
QPushButton:disabled {
color: #33E4E4E4;
}
)";
QPushButton *createButton(QHBoxLayout *layout, const QString &label, const QString &stateCode) {
QPushButton *button = new QPushButton(label, this);
button->setCheckable(true);
button->setStyleSheet(buttonStyle);
connect(button, &QPushButton::clicked, this, [this, button, stateCode] { updateState(stateCode, button); });
layout->addWidget(button);
return button;
}
void adjustButtonWidths(QHBoxLayout *layout) {
if (!layout || layout->count() == (country ? 3 : 4)) return;
for (int i = 0; i < layout->count(); ++i) {
QWidget *widget = layout->itemAt(i)->widget();
QPushButton *button = qobject_cast<QPushButton *>(widget);
if (button) {
button->setMinimumWidth(button->sizeHint().width());
}
}
}
void updateState(const QString &newState, QPushButton *button) {
QString &selectedList = country ? selectedCountries : selectedStates;
QStringList tempList = selectedList.split(',');
if (button->isChecked()) {
if (!selectedList.isEmpty()) selectedList += ",";
selectedList += newState;
} else {
tempList.removeAll(newState);
selectedList = tempList.join(',');
}
Params("/dev/shm/params").remove("OSMDownloadLocations");
}
};
QString ButtonSelectionControl::selectedStates = "";
QString ButtonSelectionControl::selectedCountries = "";
namespace {
template <typename T>
T extractFromJson(const std::string &jsonData, const std::string &key, T defaultValue = 0) {
std::string::size_type pos = jsonData.find(key);
return pos != std::string::npos ? std::stol(jsonData.substr(pos + key.length())) : defaultValue;
}
}
QString formatTime(long timeInSeconds) {
long minutes = timeInSeconds / 60;
long seconds = timeInSeconds % 60;
QString formattedTime = (minutes > 0) ? QString::number(minutes) + "m " : "";
formattedTime += QString::number(seconds) + "s";
return formattedTime;
}
QString formatDateTime(const std::chrono::time_point<std::chrono::system_clock> &timePoint) {
return QDateTime::fromTime_t(std::chrono::system_clock::to_time_t(timePoint)).toString("h:mm ap");
}
QString calculateElapsedTime(int totalFiles, int downloadedFiles, const std::chrono::steady_clock::time_point &startTime) {
using namespace std::chrono;
if (totalFiles <= 0 || downloadedFiles >= totalFiles) return "Calculating...";
long elapsed = duration_cast<seconds>(steady_clock::now() - startTime).count();
return formatTime(elapsed);
}
QString calculateETA(int totalFiles, int downloadedFiles, const std::chrono::steady_clock::time_point &startTime) {
using namespace std::chrono;
if (totalFiles <= 0 || downloadedFiles >= totalFiles) return "Calculating...";
long elapsed = duration_cast<seconds>(steady_clock::now() - startTime).count();
if (downloadedFiles == 0 || elapsed <= 0) {
return "Calculating...";
}
double averageTimePerFile = static_cast<double>(elapsed) / downloadedFiles;
int remainingFiles = totalFiles - downloadedFiles;
long estimatedTimeRemaining = static_cast<long>(averageTimePerFile * remainingFiles);
std::chrono::time_point<std::chrono::system_clock> estimatedCompletionTime = system_clock::now() + seconds(estimatedTimeRemaining);
QString estimatedTimeStr = formatDateTime(estimatedCompletionTime);
return formatTime(estimatedTimeRemaining) + " (" + estimatedTimeStr + ")";
}
QString formatDownloadStatus(int totalFiles, int downloadedFiles) {
if (totalFiles <= 0) return "Calculating...";
if (downloadedFiles >= totalFiles) return "Downloaded";
int percentage = static_cast<int>(100 * downloadedFiles / totalFiles);
return QString::asprintf("Downloading: %d/%d (%d%%)", downloadedFiles, totalFiles, percentage);
}
quint64 calculateDirectorySize(const QString &path) {
quint64 totalSize = 0;
QDirIterator it(path, QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext()) {
it.next();
QFileInfo fileInfo(it.filePath());
if (fileInfo.isFile()) {
totalSize += fileInfo.size();
}
}
return totalSize;
}
QString formatSize(qint64 size) {
const qint64 kb = 1024;
const qint64 mb = 1024 * kb;
const qint64 gb = 1024 * mb;
if (size < gb) {
double sizeMB = size / static_cast<double>(mb);
return QString::number(sizeMB, 'f', 2) + " MB";
} else {
double sizeGB = size / static_cast<double>(gb);
return QString::number(sizeGB, 'f', 2) + " GB";
}
}

View File

@@ -1,574 +0,0 @@
#include <QMouseEvent>
#include "selfdrive/frogpilot/navigation/ui/navigation_functions.h"
#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h"
FrogPilotNavigationPanel::FrogPilotNavigationPanel(QWidget *parent) : QFrame(parent), scene(uiState()->scene) {
mainLayout = new QStackedLayout(this);
navigationWidget = new QWidget();
QVBoxLayout *navigationLayout = new QVBoxLayout(navigationWidget);
navigationLayout->setMargin(40);
FrogPilotListWidget *list = new FrogPilotListWidget(navigationWidget);
Primeless *primelessPanel = new Primeless(this);
mainLayout->addWidget(primelessPanel);
ButtonControl *manageNOOButton = new ButtonControl(tr("Manage Primeless Navigation Settings"), tr("MANAGE"), tr("Manage primeless navigate on openpilot settings."));
QObject::connect(manageNOOButton, &ButtonControl::clicked, [=]() { mainLayout->setCurrentWidget(primelessPanel); });
QObject::connect(primelessPanel, &Primeless::backPress, [=]() { mainLayout->setCurrentWidget(navigationWidget); });
list->addItem(manageNOOButton);
manageNOOButton->setVisible(!uiState()->hasPrime());
std::vector<QString> scheduleOptions{tr("Manually"), tr("Weekly"), tr("Monthly")};
ButtonParamControl *preferredSchedule = new ButtonParamControl("PreferredSchedule", tr("Maps Scheduler"),
tr("Choose the frequency for updating maps with the latest OpenStreetMap (OSM) changes. "
"Weekly updates begin at midnight every Sunday, while monthly updates start at midnight on the 1st of each month. "
"If your device is off or not connected to WiFi during a scheduled update, the download will be conducted the next "
"time you're offroad with a WiFi connection."),
"",
scheduleOptions);
schedule = params.getInt("PreferredSchedule");
schedulePending = params.getBool("SchedulePending");
list->addItem(preferredSchedule);
list->addItem(offlineMapsSize = new LabelControl(tr("Offline Maps Size"), formatSize(calculateDirectorySize(offlineFolderPath))));
offlineMapsSize->setVisible(true);
list->addItem(lastMapsDownload = new LabelControl(tr("Last Download"), ""));
lastMapsDownload->setVisible(!params.get("LastMapsUpdate").empty());
lastMapsDownload->setText(QString::fromStdString(params.get("LastMapsUpdate")));
list->addItem(offlineMapsStatus = new LabelControl(tr("Offline Maps Status"), ""));
offlineMapsStatus->setVisible(false);
list->addItem(offlineMapsETA = new LabelControl(tr("Offline Maps ETA"), ""));
offlineMapsETA->setVisible(false);
list->addItem(offlineMapsElapsed = new LabelControl(tr("Time Elapsed"), ""));
offlineMapsElapsed->setVisible(false);
cancelDownloadButton = new ButtonControl(tr("Cancel Download"), tr("CANCEL"), tr("Cancel your current download."));
QObject::connect(cancelDownloadButton, &ButtonControl::clicked, [this] { cancelDownload(this); });
list->addItem(cancelDownloadButton);
cancelDownloadButton->setVisible(false);
downloadOfflineMapsButton = new ButtonControl(tr("Download Offline Maps"), tr("DOWNLOAD"), tr("Download your selected offline maps to use with openpilot."));
QObject::connect(downloadOfflineMapsButton, &ButtonControl::clicked, [this] { downloadMaps(); });
list->addItem(downloadOfflineMapsButton);
downloadOfflineMapsButton->setVisible(!params.get("MapsSelected").empty());
SelectMaps *mapsPanel = new SelectMaps(this);
mainLayout->addWidget(mapsPanel);
QObject::connect(mapsPanel, &SelectMaps::setMaps, [=]() { setMaps(); });
ButtonControl *selectMapsButton = new ButtonControl(tr("Select Offline Maps"), tr("SELECT"), tr("Select your maps to use with OSM."));
QObject::connect(selectMapsButton, &ButtonControl::clicked, [=]() { mainLayout->setCurrentWidget(mapsPanel); });
QObject::connect(mapsPanel, &SelectMaps::backPress, [=]() { mainLayout->setCurrentWidget(navigationWidget); });
list->addItem(selectMapsButton);
removeOfflineMapsButton = new ButtonControl(tr("Remove Offline Maps"), tr("REMOVE"), tr("Remove your downloaded offline maps to clear up storage space."));
QObject::connect(removeOfflineMapsButton, &ButtonControl::clicked, [this] { removeMaps(this); });
list->addItem(removeOfflineMapsButton);
removeOfflineMapsButton->setVisible(QDir(offlineFolderPath).exists());
navigationLayout->addWidget(new ScrollView(list, navigationWidget));
navigationWidget->setLayout(navigationLayout);
mainLayout->addWidget(navigationWidget);
mainLayout->setCurrentWidget(navigationWidget);
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotNavigationPanel::updateState);
checkIfUpdateMissed();
}
void FrogPilotNavigationPanel::hideEvent(QHideEvent *event) {
QWidget::hideEvent(event);
mainLayout->setCurrentWidget(navigationWidget);
}
void FrogPilotNavigationPanel::updateState() {
if (!isVisible() && downloadActive) updateVisibility(downloadActive);
if (downloadActive) updateStatuses();
if (schedule) downloadSchedule();
}
void FrogPilotNavigationPanel::updateStatuses() {
static std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
osmDownloadProgress = params.get("OSMDownloadProgress");
const int totalFiles = extractFromJson<int>(osmDownloadProgress, "\"total_files\":");
const int downloadedFiles = extractFromJson<int>(osmDownloadProgress, "\"downloaded_files\":");
if (paramsMemory.get("OSMDownloadLocations").empty()) {
downloadActive = false;
updateDownloadedLabel();
qint64 fileSize = calculateDirectorySize(offlineFolderPath);
offlineMapsSize->setText(formatSize(fileSize));
previousOSMDownloadProgress = osmDownloadProgress;
}
if (osmDownloadProgress != previousOSMDownloadProgress && isVisible()) {
qint64 fileSize = calculateDirectorySize(offlineFolderPath);
offlineMapsSize->setText(formatSize(fileSize));
previousOSMDownloadProgress = osmDownloadProgress;
}
elapsedTime = calculateElapsedTime(totalFiles, downloadedFiles, startTime);
offlineMapsElapsed->setText(elapsedTime);
offlineMapsETA->setText(calculateETA(totalFiles, downloadedFiles, startTime));
offlineMapsStatus->setText(formatDownloadStatus(totalFiles, downloadedFiles));
if (downloadActive != previousDownloadActive) {
startTime = !downloadActive ? std::chrono::steady_clock::now() : startTime;
updateVisibility(downloadActive);
previousDownloadActive = downloadActive;
}
}
void FrogPilotNavigationPanel::updateVisibility(bool visibility) {
cancelDownloadButton->setVisible(visibility);
offlineMapsElapsed->setVisible(visibility);
offlineMapsETA->setVisible(visibility);
offlineMapsStatus->setVisible(visibility);
downloadOfflineMapsButton->setVisible(!visibility);
lastMapsDownload->setVisible(QDir(offlineFolderPath).exists() && !downloadActive);
removeOfflineMapsButton->setVisible(QDir(offlineFolderPath).exists() && !downloadActive);
update();
}
void FrogPilotNavigationPanel::checkIfUpdateMissed() {
std::string lastMapsUpdate = params.get("LastMapsUpdate");
if (lastMapsUpdate.empty() || schedule == 0) {
return;
}
std::time_t currentTime = std::time(nullptr);
std::tm *now = std::localtime(&currentTime);
std::tm lastUpdate = {};
sscanf(lastMapsUpdate.c_str(), "%d-%d-%d", &lastUpdate.tm_year, &lastUpdate.tm_mon, &lastUpdate.tm_mday);
lastUpdate.tm_year -= 1900;
lastUpdate.tm_mon -= 1;
std::time_t lastUpdateTime = std::mktime(&lastUpdate);
std::tm *lastUpdateDay = std::localtime(&lastUpdateTime);
if (schedule == 1) {
schedulePending = (now->tm_wday == 0 && lastUpdateDay->tm_wday != 0) || (now->tm_wday > lastUpdateDay->tm_wday);
} else if (schedule == 2) {
schedulePending = (now->tm_mday == 1 && lastUpdate.tm_mday != 1) || (now->tm_mon != lastUpdate.tm_mon);
}
}
void FrogPilotNavigationPanel::updateDownloadedLabel() {
std::time_t t = std::time(nullptr);
std::tm now = *std::localtime(&t);
char dateBuffer[11];
std::strftime(dateBuffer, sizeof(dateBuffer), "%Y-%m-%d", &now);
QDate date = QDate::fromString(dateBuffer, "yyyy-MM-dd");
int day = date.day();
std::string suffix = (day == 1 || day == 21 || day == 31) ? "st" :
(day == 2 || day == 22) ? "nd" :
(day == 3 || day == 23) ? "rd" : "th";
std::string lastMapsUpdate = date.toString("MMMM d").toStdString() + suffix + date.toString(", yyyy").toStdString();
lastMapsDownload->setText(QString::fromStdString(lastMapsUpdate));
params.put("LastMapsUpdate", lastMapsUpdate);
}
void FrogPilotNavigationPanel::downloadSchedule() {
const bool wifi = (*uiState()->sm)["deviceState"].getDeviceState().getNetworkType() == cereal::DeviceState::NetworkType::WIFI;
const std::time_t t = std::time(nullptr);
const std::tm *now = std::localtime(&t);
const bool isScheduleTime = (schedule == 1 && now->tm_wday == 0) || (schedule == 2 && now->tm_mday == 1);
if ((isScheduleTime || schedulePending) && !(scene.started || scheduleCompleted) && wifi) {
downloadMaps();
scheduleCompleted = true;
if (schedulePending) {
schedulePending = false;
params.putBool("SchedulePending", false);
}
} else if (!isScheduleTime) {
scheduleCompleted = false;
} else {
if (!schedulePending) {
params.putBool("SchedulePending", true);
}
schedulePending = true;
}
}
void FrogPilotNavigationPanel::cancelDownload(QWidget *parent) {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to cancel the download?"), parent)) {
std::thread([&] {
std::system("pkill mapd");
}).detach();
if (FrogPilotConfirmationDialog::toggle(tr("Reboot required to re-enable map downloads"), tr("Reboot Now"), parent)) {
Hardware::soft_reboot();
}
downloadActive = false;
updateVisibility(downloadActive);
downloadOfflineMapsButton->setVisible(downloadActive);
}
}
void FrogPilotNavigationPanel::downloadMaps() {
params.remove("OSMDownloadProgress");
paramsMemory.put("OSMDownloadLocations", params.get("MapsSelected"));
removeOfflineMapsButton->setVisible(true);
downloadActive = true;
}
void FrogPilotNavigationPanel::removeMaps(QWidget *parent) {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to delete all of your downloaded maps?"), parent)) {
std::thread([&] {
lastMapsDownload->setVisible(false);
removeOfflineMapsButton->setVisible(false);
offlineMapsSize->setText(formatSize(0));
params.remove("LastMapsUpdate");
std::system("rm -rf /data/media/0/osm/offline");
}).detach();
}
}
void FrogPilotNavigationPanel::setMaps() {
std::thread([&] {
QStringList states = ButtonSelectionControl::selectedStates.split(',', QString::SkipEmptyParts);
QStringList countries = ButtonSelectionControl::selectedCountries.split(',', QString::SkipEmptyParts);
QJsonObject json;
json.insert("states", QJsonArray::fromStringList(states));
json.insert("nations", QJsonArray::fromStringList(countries));
params.put("MapsSelected", QJsonDocument(json).toJson(QJsonDocument::Compact).toStdString());
if (!states.isEmpty() || !countries.isEmpty()) {
downloadOfflineMapsButton->setVisible(true);
update();
} else {
params.remove("MapsSelected");
}
}).detach();
}
SelectMaps::SelectMaps(QWidget *parent) : QWidget(parent) {
QVBoxLayout *mainLayout = new QVBoxLayout(this);
QHBoxLayout *buttonsLayout = new QHBoxLayout();
buttonsLayout->setContentsMargins(20, 40, 20, 0);
backButton = new QPushButton(tr("Back"), this);
statesButton = new QPushButton(tr("States"), this);
countriesButton = new QPushButton(tr("Countries"), this);
backButton->setFixedSize(400, 100);
statesButton->setFixedSize(400, 100);
countriesButton->setFixedSize(400, 100);
buttonsLayout->addWidget(backButton);
buttonsLayout->addStretch();
buttonsLayout->addWidget(statesButton);
buttonsLayout->addStretch();
buttonsLayout->addWidget(countriesButton);
mainLayout->addLayout(buttonsLayout);
mainLayout->addWidget(horizontalLine());
mainLayout->setSpacing(20);
mapsLayout = new QStackedLayout();
mapsLayout->setMargin(40);
mapsLayout->setSpacing(20);
mainLayout->addLayout(mapsLayout);
QObject::connect(backButton, &QPushButton::clicked, this, [this]() { emit backPress(), emit setMaps(); });
FrogPilotListWidget *statesList = new FrogPilotListWidget();
LabelControl *northeastLabel = new LabelControl(tr("United States - Northeast"), "");
statesList->addItem(northeastLabel);
ButtonSelectionControl *northeastControl = new ButtonSelectionControl("", tr(""), tr(""), northeastMap, false);
statesList->addItem(northeastControl);
LabelControl *midwestLabel = new LabelControl(tr("United States - Midwest"), "");
statesList->addItem(midwestLabel);
ButtonSelectionControl *midwestControl = new ButtonSelectionControl("", tr(""), tr(""), midwestMap, false);
statesList->addItem(midwestControl);
LabelControl *southLabel = new LabelControl(tr("United States - South"), "");
statesList->addItem(southLabel);
ButtonSelectionControl *southControl = new ButtonSelectionControl("", tr(""), tr(""), southMap, false);
statesList->addItem(southControl);
LabelControl *westLabel = new LabelControl(tr("United States - West"), "");
statesList->addItem(westLabel);
ButtonSelectionControl *westControl = new ButtonSelectionControl("", tr(""), tr(""), westMap, false);
statesList->addItem(westControl);
LabelControl *territoriesLabel = new LabelControl(tr("United States - Territories"), "");
statesList->addItem(territoriesLabel);
ButtonSelectionControl *territoriesControl = new ButtonSelectionControl("", tr(""), tr(""), territoriesMap, false);
statesList->addItem(territoriesControl);
statesScrollView = new ScrollView(statesList);
mapsLayout->addWidget(statesScrollView);
QObject::connect(statesButton, &QPushButton::clicked, this, [this]() {
mapsLayout->setCurrentWidget(statesScrollView);
statesButton->setStyleSheet(activeButtonStyle);
countriesButton->setStyleSheet(normalButtonStyle);
});
FrogPilotListWidget *countriesList = new FrogPilotListWidget();
LabelControl *africaLabel = new LabelControl(tr("Africa"), "");
countriesList->addItem(africaLabel);
ButtonSelectionControl *africaControl = new ButtonSelectionControl("", tr(""), tr(""), africaMap, true);
countriesList->addItem(africaControl);
LabelControl *antarcticaLabel = new LabelControl(tr("Antarctica"), "");
countriesList->addItem(antarcticaLabel);
ButtonSelectionControl *antarcticaControl = new ButtonSelectionControl("", tr(""), tr(""), antarcticaMap, true);
countriesList->addItem(antarcticaControl);
LabelControl *asiaLabel = new LabelControl(tr("Asia"), "");
countriesList->addItem(asiaLabel);
ButtonSelectionControl *asiaControl = new ButtonSelectionControl("", tr(""), tr(""), asiaMap, true);
countriesList->addItem(asiaControl);
LabelControl *europeLabel = new LabelControl(tr("Europe"), "");
countriesList->addItem(europeLabel);
ButtonSelectionControl *europeControl = new ButtonSelectionControl("", tr(""), tr(""), europeMap, true);
countriesList->addItem(europeControl);
LabelControl *northAmericaLabel = new LabelControl(tr("North America"), "");
countriesList->addItem(northAmericaLabel);
ButtonSelectionControl *northAmericaControl = new ButtonSelectionControl("", tr(""), tr(""), northAmericaMap, true);
countriesList->addItem(northAmericaControl);
LabelControl *oceaniaLabel = new LabelControl(tr("Oceania"), "");
countriesList->addItem(oceaniaLabel);
ButtonSelectionControl *oceaniaControl = new ButtonSelectionControl("", tr(""), tr(""), oceaniaMap, true);
countriesList->addItem(oceaniaControl);
LabelControl *southAmericaLabel = new LabelControl(tr("South America"), "");
countriesList->addItem(southAmericaLabel);
ButtonSelectionControl *southAmericaControl = new ButtonSelectionControl("", tr(""), tr(""), southAmericaMap, true);
countriesList->addItem(southAmericaControl);
countriesScrollView = new ScrollView(countriesList);
mapsLayout->addWidget(countriesScrollView);
QObject::connect(countriesButton, &QPushButton::clicked, this, [this]() {
mapsLayout->setCurrentWidget(countriesScrollView);
statesButton->setStyleSheet(normalButtonStyle);
countriesButton->setStyleSheet(activeButtonStyle);
});
mapsLayout->setCurrentWidget(statesScrollView);
statesButton->setStyleSheet(activeButtonStyle);
setStyleSheet(R"(
QPushButton {
font-size: 50px;
margin: 0px;
padding: 15px;
border-width: 0;
border-radius: 30px;
color: #dddddd;
background-color: #393939;
}
QPushButton:pressed {
background-color: #4a4a4a;
}
)");
}
QString SelectMaps::activeButtonStyle = R"(
font-size: 50px;
margin: 0px;
padding: 15px;
border-width: 0;
border-radius: 30px;
color: #dddddd;
background-color: #33Ab4C;
)";
QString SelectMaps::normalButtonStyle = R"(
font-size: 50px;
margin: 0px;
padding: 15px;
border-width: 0;
border-radius: 30px;
color: #dddddd;
background-color: #393939;
)";
QFrame *SelectMaps::horizontalLine(QWidget *parent) const {
QFrame *line = new QFrame(parent);
line->setFrameShape(QFrame::StyledPanel);
line->setStyleSheet(R"(
border-width: 2px;
border-bottom-style: solid;
border-color: gray;
)");
line->setFixedHeight(2);
return line;
}
void SelectMaps::hideEvent(QHideEvent *event) {
QWidget::hideEvent(event);
emit setMaps();
}
Primeless::Primeless(QWidget *parent) : QWidget(parent) {
QStackedLayout *primelessLayout = new QStackedLayout(this);
QWidget *mainWidget = new QWidget();
mainLayout = new QVBoxLayout(mainWidget);
mainLayout->setMargin(40);
backButton = new QPushButton(tr("Back"), this);
backButton->setObjectName("backButton");
backButton->setFixedSize(400, 100);
QObject::connect(backButton, &QPushButton::clicked, this, [this]() { emit backPress(); });
mainLayout->addWidget(backButton, 0, Qt::AlignLeft);
list = new FrogPilotListWidget(mainWidget);
wifi = new WifiManager(this);
ipLabel = new LabelControl(tr("Manage Your Settings At"), QString("%1:8082").arg(wifi->getIp4Address()));
list->addItem(ipLabel);
std::vector<QString> searchOptions{tr("MapBox"), tr("Amap"), tr("Google")};
ButtonParamControl *searchInput = new ButtonParamControl("SearchInput", tr("Destination Search Provider"),
tr("Select a search provider for destination queries in Navigate on Openpilot. Options include MapBox (recommended), Amap, and Google Maps."),
"", searchOptions);
list->addItem(searchInput);
createMapboxKeyControl(publicMapboxKeyControl, tr("Public Mapbox Key"), "MapboxPublicKey", "pk.");
createMapboxKeyControl(secretMapboxKeyControl, tr("Secret Mapbox Key"), "MapboxSecretKey", "sk.");
mapboxPublicKeySet = !params.get("MapboxPublicKey").empty();
mapboxSecretKeySet = !params.get("MapboxSecretKey").empty();
setupCompleted = mapboxPublicKeySet && mapboxSecretKeySet;
QHBoxLayout *setupLayout = new QHBoxLayout();
setupLayout->setMargin(0);
imageLabel = new QLabel(this);
pixmap.load(currentStep);
imageLabel->setPixmap(pixmap.scaledToWidth(1500, Qt::SmoothTransformation));
setupLayout->addWidget(imageLabel, 0, Qt::AlignCenter);
imageLabel->hide();
ButtonControl *setupButton = new ButtonControl(tr("Setup Instructions"), tr("VIEW"), tr("View the instructions to set up MapBox for Primeless Navigation."), this);
QObject::connect(setupButton, &ButtonControl::clicked, this, [this]() {
updateStep();
backButton->hide();
list->setVisible(false);
imageLabel->show();
});
list->addItem(setupButton);
QObject::connect(uiState(), &UIState::uiUpdate, this, &Primeless::updateState);
mainLayout->addLayout(setupLayout);
mainLayout->addWidget(new ScrollView(list, mainWidget));
mainWidget->setLayout(mainLayout);
primelessLayout->addWidget(mainWidget);
setLayout(primelessLayout);
setStyleSheet(R"(
QPushButton {
font-size: 50px;
margin: 0px;
padding: 15px;
border-width: 0;
border-radius: 30px;
color: #dddddd;
background-color: #393939;
}
QPushButton:pressed {
background-color: #4a4a4a;
}
)");
}
void Primeless::hideEvent(QHideEvent *event) {
QWidget::hideEvent(event);
backButton->show();
list->setVisible(true);
imageLabel->hide();
}
void Primeless::mousePressEvent(QMouseEvent *event) {
backButton->show();
list->setVisible(true);
imageLabel->hide();
}
void Primeless::updateState() {
if (!isVisible()) return;
QString ipAddress = wifi->getIp4Address();
ipLabel->setText(ipAddress.isEmpty() ? tr("Device Offline") : QString("%1:8082").arg(ipAddress));
mapboxPublicKeySet = !params.get("MapboxPublicKey").empty();
mapboxSecretKeySet = !params.get("MapboxSecretKey").empty();
setupCompleted = mapboxPublicKeySet && mapboxSecretKeySet && setupCompleted;
publicMapboxKeyControl->setText(mapboxPublicKeySet ? tr("REMOVE") : tr("ADD"));
secretMapboxKeyControl->setText(mapboxSecretKeySet ? tr("REMOVE") : tr("ADD"));
if (imageLabel->isVisible()) {
updateStep();
}
}
void Primeless::createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string &paramKey, const QString &prefix) {
control = new ButtonControl(label, "", tr("Manage your %1."), this);
QObject::connect(control, &ButtonControl::clicked, this, [this, control, label, paramKey, prefix] {
if (control->text() == tr("ADD")) {
QString key = InputDialog::getText(tr("Enter your %1").arg(label), this);
if (!key.startsWith(prefix)) {
key = prefix + key;
}
if (key.length() >= 80) {
params.put(paramKey, key.toStdString());
}
} else {
params.remove(paramKey);
}
});
list->addItem(control);
control->setText(params.get(paramKey).empty() ? tr("ADD") : tr("REMOVE"));
}
void Primeless::updateStep() {
currentStep = setupCompleted ? "../frogpilot/navigation/navigation_training/setup_completed.png" :
(mapboxPublicKeySet && mapboxSecretKeySet) ? "../frogpilot/navigation/navigation_training/both_keys_set.png" :
mapboxPublicKeySet ? "../frogpilot/navigation/navigation_training/public_key_set.png" : "../frogpilot/navigation/navigation_training/no_keys_set.png";
pixmap.load(currentStep);
imageLabel->setPixmap(pixmap.scaledToWidth(1500, Qt::SmoothTransformation));
}

View File

@@ -1,121 +0,0 @@
#pragma once
#include "selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h"
#include "selfdrive/ui/qt/network/wifi_manager.h"
#include "selfdrive/ui/qt/offroad/settings.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
#include "selfdrive/ui/ui.h"
class Primeless : public QWidget {
Q_OBJECT
public:
explicit Primeless(QWidget *parent = nullptr);
protected:
void mousePressEvent(QMouseEvent *event) override;
void hideEvent(QHideEvent *event) override;
private:
void createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string &paramKey, const QString &prefix);
void updateState();
void updateStep();
QVBoxLayout *mainLayout;
FrogPilotListWidget *list;
QPushButton *backButton;
QLabel *imageLabel;
ButtonControl *publicMapboxKeyControl;
ButtonControl *secretMapboxKeyControl;
LabelControl *ipLabel;
WifiManager *wifi;
bool mapboxPublicKeySet;
bool mapboxSecretKeySet;
bool setupCompleted;
QPixmap pixmap;
QString currentStep = "../assets/images/setup_completed.png";
Params params;
signals:
void backPress();
};
class SelectMaps : public QWidget {
Q_OBJECT
public:
explicit SelectMaps(QWidget *parent = nullptr);
QFrame *horizontalLine(QWidget *parent = nullptr) const;
private:
void hideEvent(QHideEvent *event);
ScrollView *countriesScrollView;
ScrollView *statesScrollView;
QStackedLayout *mapsLayout;
QPushButton *backButton;
QPushButton *statesButton;
QPushButton *countriesButton;
static QString activeButtonStyle;
static QString normalButtonStyle;
signals:
void backPress();
void setMaps();
};
class FrogPilotNavigationPanel : public QFrame {
Q_OBJECT
public:
explicit FrogPilotNavigationPanel(QWidget *parent = 0);
private:
void cancelDownload(QWidget *parent);
void checkIfUpdateMissed();
void downloadMaps();
void downloadSchedule();
void hideEvent(QHideEvent *event);
void removeMaps(QWidget *parent);
void setMaps();
void updateDownloadedLabel();
void updateState();
void updateStatuses();
void updateVisibility(bool visibility);
QStackedLayout *mainLayout;
QWidget *navigationWidget;
ButtonControl *cancelDownloadButton;
ButtonControl *downloadOfflineMapsButton;
ButtonControl *redownloadOfflineMapsButton;
ButtonControl *removeOfflineMapsButton;
LabelControl *lastMapsDownload;
LabelControl *offlineMapsSize;
LabelControl *offlineMapsStatus;
LabelControl *offlineMapsETA;
LabelControl *offlineMapsElapsed;
bool downloadActive;
bool previousDownloadActive;
bool scheduleCompleted;
bool schedulePending;
int schedule;
QString elapsedTime;
QString offlineFolderPath = "/data/media/0/osm/offline";
std::string osmDownloadProgress;
std::string previousOSMDownloadProgress;
Params params;
Params paramsMemory{"/dev/shm/params"};
UIScene &scene;
};

View File

@@ -21,19 +21,14 @@ qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"]
qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs)
widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/widgets/wifi.cc",
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc",
"qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc",
"qt/widgets/offroad_alerts.cc", "qt/widgets/keyboard.cc",
"qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc",
"qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc",
"../frogpilot/ui/qt/widgets/frogpilot_controls.cc", "../frogpilot/navigation/ui/navigation_settings.cc",
"../frogpilot/ui/qt/widgets/frogpilot_controls.cc",
"../frogpilot/ui/qt/offroad/control_settings.cc", "../frogpilot/ui/qt/offroad/vehicle_settings.cc",
"../frogpilot/ui/qt/offroad/visual_settings.cc"]
qt_env['CPPDEFINES'] = []
if maps:
base_libs += ['QMapLibre']
widgets_src += ["qt/maps/map_helpers.cc", "qt/maps/map_settings.cc", "qt/maps/map.cc", "qt/maps/map_panel.cc",
"qt/maps/map_eta.cc", "qt/maps/map_instructions.cc"]
qt_env['CPPDEFINES'] += ["ENABLE_MAPS"]
widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs)
Export('widgets')

View File

@@ -8,15 +8,10 @@
#include "selfdrive/ui/qt/offroad/experimental_mode.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/drive_stats.h"
#include "selfdrive/ui/qt/widgets/prime.h"
#include "system/hardware/hw.h"
#include <QWebEngineView>
#ifdef ENABLE_MAPS
#include "selfdrive/ui/qt/maps/map_settings.h"
#endif
// HomeWindow: the container for the offroad and onroad UIs
HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
@@ -40,7 +35,6 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
slayout->addWidget(home);
onroad = new OnroadWindow(this);
QObject::connect(onroad, &OnroadWindow::mapPanelRequested, this, [=] { sidebar->hide(); });
slayout->addWidget(onroad);
// CLEARPILOT
@@ -71,10 +65,6 @@ void HomeWindow::showSidebar(bool show) {
sidebar->setVisible(show);
}
void HomeWindow::showMapPanel(bool show) {
onroad->showMapPanel(show);
}
void HomeWindow::updateState(const UIState &s) {
// const SubMaster &sm = *(s.sm);
if (s.scene.started) {
@@ -89,7 +79,6 @@ void HomeWindow::offroadTransition(bool offroad) {
} else {
sidebar->setVisible(false);
slayout->setCurrentWidget(onroad);
uiState()->scene.map_open = onroad->isMapVisible();
}
}

View File

@@ -60,7 +60,6 @@ public slots:
void showDriverView(bool show, bool started=false);
void showOnroad();
void showSidebar(bool show);
void showMapPanel(bool show);
protected:
void mousePressEvent(QMouseEvent* e) override;

View File

@@ -8,7 +8,6 @@
#include "selfdrive/ui/qt/offroad/experimental_mode.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/drive_stats.h"
#include "selfdrive/ui/qt/widgets/prime.h"
#include "system/hardware/hw.h"
#ifdef ENABLE_MAPS

View File

@@ -1,469 +0,0 @@
#include "selfdrive/ui/qt/maps/map.h"
#include <algorithm>
#include <eigen3/Eigen/Dense>
#include <QDebug>
#include "selfdrive/ui/qt/maps/map_helpers.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/ui.h"
const int INTERACTION_TIMEOUT = 100;
const float MAX_ZOOM = 17;
const float MIN_ZOOM = 14;
const float MAX_PITCH = 50;
const float MIN_PITCH = 0;
const float MAP_SCALE = 2;
MapWindow::MapWindow(const QMapLibre::Settings &settings) : m_settings(settings), velocity_filter(0, 10, 0.05, false) {
QObject::connect(uiState(), &UIState::uiUpdate, this, &MapWindow::updateState);
map_overlay = new QWidget (this);
map_overlay->setAttribute(Qt::WA_TranslucentBackground, true);
QVBoxLayout *overlay_layout = new QVBoxLayout(map_overlay);
overlay_layout->setContentsMargins(0, 0, 0, 0);
// Instructions
map_instructions = new MapInstructions(this);
map_instructions->setVisible(false);
map_eta = new MapETA(this);
map_eta->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
map_eta->setFixedHeight(120);
error = new QLabel(this);
error->setStyleSheet(R"(color:white;padding:50px 11px;font-size: 90px; background-color:rgba(0, 0, 0, 150);)");
error->setAlignment(Qt::AlignCenter);
overlay_layout->addWidget(error);
overlay_layout->addWidget(map_instructions);
overlay_layout->addStretch(1);
overlay_layout->addWidget(map_eta);
last_position = coordinate_from_param("LastGPSPosition");
grabGesture(Qt::GestureType::PinchGesture);
qDebug() << "MapWindow initialized";
}
MapWindow::~MapWindow() {
makeCurrent();
}
void MapWindow::initLayers() {
// This doesn't work from initializeGL
if (!m_map->layerExists("modelPathLayer")) {
qDebug() << "Initializing modelPathLayer";
QVariantMap modelPath;
//modelPath["id"] = "modelPathLayer";
modelPath["type"] = "line";
modelPath["source"] = "modelPathSource";
m_map->addLayer("modelPathLayer", modelPath);
m_map->setPaintProperty("modelPathLayer", "line-color", QColor("red"));
m_map->setPaintProperty("modelPathLayer", "line-width", 5.0);
m_map->setLayoutProperty("modelPathLayer", "line-cap", "round");
}
if (!m_map->layerExists("navLayer")) {
qDebug() << "Initializing navLayer";
QVariantMap nav;
nav["type"] = "line";
nav["source"] = "navSource";
m_map->addLayer("navLayer", nav, "road-intersection");
QVariantMap transition;
transition["duration"] = 400; // ms
m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(uiState()->scene.navigate_on_openpilot));
m_map->setPaintProperty("navLayer", "line-color-transition", transition);
m_map->setPaintProperty("navLayer", "line-width", 7.5);
m_map->setLayoutProperty("navLayer", "line-cap", "round");
}
if (!m_map->layerExists("pinLayer")) {
qDebug() << "Initializing pinLayer";
m_map->addImage("default_marker", QImage("../assets/navigation/default_marker.svg"));
QVariantMap pin;
pin["type"] = "symbol";
pin["source"] = "pinSource";
m_map->addLayer("pinLayer", pin);
m_map->setLayoutProperty("pinLayer", "icon-pitch-alignment", "viewport");
m_map->setLayoutProperty("pinLayer", "icon-image", "default_marker");
m_map->setLayoutProperty("pinLayer", "icon-ignore-placement", true);
m_map->setLayoutProperty("pinLayer", "icon-allow-overlap", true);
m_map->setLayoutProperty("pinLayer", "symbol-sort-key", 0);
m_map->setLayoutProperty("pinLayer", "icon-anchor", "bottom");
}
if (!m_map->layerExists("carPosLayer")) {
qDebug() << "Initializing carPosLayer";
m_map->addImage("label-arrow", QImage("../assets/images/triangle.svg"));
QVariantMap carPos;
carPos["type"] = "symbol";
carPos["source"] = "carPosSource";
m_map->addLayer("carPosLayer", carPos);
m_map->setLayoutProperty("carPosLayer", "icon-pitch-alignment", "map");
m_map->setLayoutProperty("carPosLayer", "icon-image", "label-arrow");
m_map->setLayoutProperty("carPosLayer", "icon-size", 0.5);
m_map->setLayoutProperty("carPosLayer", "icon-ignore-placement", true);
m_map->setLayoutProperty("carPosLayer", "icon-allow-overlap", true);
// TODO: remove, symbol-sort-key does not seem to matter outside of each layer
m_map->setLayoutProperty("carPosLayer", "symbol-sort-key", 0);
}
if (!m_map->layerExists("buildingsLayer")) {
qDebug() << "Initializing buildingsLayer";
QVariantMap buildings;
buildings["id"] = "buildingsLayer";
buildings["source"] = "composite";
buildings["source-layer"] = "building";
buildings["type"] = "fill-extrusion";
buildings["minzoom"] = 15;
m_map->addLayer("buildingsLayer", buildings);
m_map->setFilter("buildingsLayer", QVariantList({"==", "extrude", "true"}));
QVariantList fillExtrusionHight = {
"interpolate",
QVariantList{"linear"},
QVariantList{"zoom"},
15, 0,
15.05, QVariantList{"get", "height"}
};
QVariantList fillExtrusionBase = {
"interpolate",
QVariantList{"linear"},
QVariantList{"zoom"},
15, 0,
15.05, QVariantList{"get", "min_height"}
};
QVariantList fillExtrusionOpacity = {
"interpolate",
QVariantList{"linear"},
QVariantList{"zoom"},
15, 0,
15.5, .6,
17, .6,
20, 0
};
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-color", QColor("grey"));
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-opacity", fillExtrusionOpacity);
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-height", fillExtrusionHight);
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-base", fillExtrusionBase);
m_map->setLayoutProperty("buildingsLayer", "visibility", "visible");
}
}
void MapWindow::updateState(const UIState &s) {
if (!uiState()->scene.started) {
return;
}
const SubMaster &sm = *(s.sm);
update();
if (sm.updated("modelV2")) {
// set path color on change, and show map on rising edge of navigate on openpilot
bool nav_enabled = sm["modelV2"].getModelV2().getNavEnabled() &&
(sm["controlsState"].getControlsState().getEnabled() || sm["frogpilotCarControl"].getFrogpilotCarControl().getAlwaysOnLateral());
if (nav_enabled != uiState()->scene.navigate_on_openpilot) {
if (loaded_once) {
m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(nav_enabled));
}
if (nav_enabled) {
emit requestVisible(true);
}
}
uiState()->scene.navigate_on_openpilot = nav_enabled;
}
if (sm.updated("liveLocationKalman")) {
auto locationd_location = sm["liveLocationKalman"].getLiveLocationKalman();
auto locationd_pos = locationd_location.getPositionGeodetic();
auto locationd_orientation = locationd_location.getCalibratedOrientationNED();
auto locationd_velocity = locationd_location.getVelocityCalibrated();
// Check std norm
auto pos_ecef_std = locationd_location.getPositionECEF().getStd();
bool pos_accurate_enough = sqrt(pow(pos_ecef_std[0], 2) + pow(pos_ecef_std[1], 2) + pow(pos_ecef_std[2], 2)) < 100;
locationd_valid = (locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid() && pos_accurate_enough);
if (locationd_valid) {
last_position = QMapLibre::Coordinate(locationd_pos.getValue()[0], locationd_pos.getValue()[1]);
last_bearing = RAD2DEG(locationd_orientation.getValue()[2]);
velocity_filter.update(std::max(10.0, locationd_velocity.getValue()[0]));
}
}
// Credit to jakethesnake420
if (loaded_once && (sm.rcv_frame("uiPlan") != model_rcv_frame)) {
auto locationd_location = sm["liveLocationKalman"].getLiveLocationKalman();
auto model_path = model_to_collection(locationd_location.getCalibratedOrientationECEF(), locationd_location.getPositionECEF(), sm["uiPlan"].getUiPlan().getPosition());
QMapLibre::Feature model_path_feature(QMapLibre::Feature::LineStringType, model_path, {}, {});
QVariantMap modelV2Path;
modelV2Path["type"] = "geojson";
modelV2Path["data"] = QVariant::fromValue<QMapLibre::Feature>(model_path_feature);
m_map->updateSource("modelPathSource", modelV2Path);
model_rcv_frame = sm.rcv_frame("uiPlan");
}
if (sm.updated("navRoute") && sm["navRoute"].getNavRoute().getCoordinates().size()) {
auto nav_dest = coordinate_from_param("NavDestination");
bool allow_open = std::exchange(last_valid_nav_dest, nav_dest) != nav_dest &&
nav_dest && !isVisible();
qWarning() << "Got new navRoute from navd. Opening map:" << allow_open;
// Show map on destination set/change
if (allow_open) {
emit requestSettings(false);
emit requestVisible(true);
}
}
loaded_once = loaded_once || (m_map && m_map->isFullyLoaded());
if (!loaded_once) {
setError(tr("Map Loading"));
return;
}
initLayers();
if (!locationd_valid) {
setError(tr("Waiting for GPS"));
} else if (routing_problem) {
setError(tr("Waiting for route"));
} else {
setError("");
}
if (locationd_valid) {
// Update current location marker
auto point = coordinate_to_collection(*last_position);
QMapLibre::Feature feature1(QMapLibre::Feature::PointType, point, {}, {});
QVariantMap carPosSource;
carPosSource["type"] = "geojson";
carPosSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature1);
m_map->updateSource("carPosSource", carPosSource);
// Map bearing isn't updated when interacting, keep location marker up to date
if (last_bearing) {
m_map->setLayoutProperty("carPosLayer", "icon-rotate", *last_bearing - m_map->bearing());
}
}
if (interaction_counter == 0) {
if (last_position) m_map->setCoordinate(*last_position);
if (last_bearing) m_map->setBearing(*last_bearing);
m_map->setZoom(util::map_val<float>(velocity_filter.x(), 0, 30, MAX_ZOOM, MIN_ZOOM));
} else {
interaction_counter--;
}
if (sm.updated("navInstruction")) {
// an invalid navInstruction packet with a nav destination is only possible if:
// - API exception/no internet
// - route response is empty
// - any time navd is waiting for recompute_countdown
routing_problem = !sm.valid("navInstruction") && coordinate_from_param("NavDestination").has_value();
if (sm.valid("navInstruction")) {
auto i = sm["navInstruction"].getNavInstruction();
map_eta->updateETA(i.getTimeRemaining(), i.getTimeRemainingTypical(), i.getDistanceRemaining());
if (locationd_valid) {
m_map->setPitch(MAX_PITCH); // TODO: smooth pitching based on maneuver distance
map_instructions->updateInstructions(i);
}
} else {
clearRoute();
}
}
if (sm.rcv_frame("navRoute") != route_rcv_frame) {
qWarning() << "Updating navLayer with new route";
auto route = sm["navRoute"].getNavRoute();
auto route_points = capnp_coordinate_list_to_collection(route.getCoordinates());
QMapLibre::Feature feature(QMapLibre::Feature::LineStringType, route_points, {}, {});
QVariantMap navSource;
navSource["type"] = "geojson";
navSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature);
m_map->updateSource("navSource", navSource);
m_map->setLayoutProperty("navLayer", "visibility", "visible");
route_rcv_frame = sm.rcv_frame("navRoute");
updateDestinationMarker();
}
// Map Styling - Credit goes to OPKR!
int map_style = uiState()->scene.map_style;
if (map_style != previous_map_style) {
std::unordered_map<int, std::string> styleUrls = {
{0, "mapbox://styles/commaai/clkqztk0f00ou01qyhsa5bzpj"}, // Stock openpilot
{1, "mapbox://styles/mapbox/streets-v11"}, // Mapbox Streets
{2, "mapbox://styles/mapbox/outdoors-v11"}, // Mapbox Outdoors
{3, "mapbox://styles/mapbox/light-v10"}, // Mapbox Light
{4, "mapbox://styles/mapbox/dark-v10"}, // Mapbox Dark
{5, "mapbox://styles/mapbox/satellite-v9"}, // Mapbox Satellite
{6, "mapbox://styles/mapbox/satellite-streets-v11"}, // Mapbox Satellite Streets
{7, "mapbox://styles/mapbox/navigation-day-v1"}, // Mapbox Navigation Day
{8, "mapbox://styles/mapbox/navigation-night-v1"}, // Mapbox Navigation Night
{9, "mapbox://styles/mapbox/traffic-night-v2"}, // Mapbox Traffic Night
{10, "mapbox://styles/mike854/clt0hm8mw01ok01p4blkr27jp"}, // mike854's (Satellite hybrid)
};
std::unordered_map<int, std::string>::iterator it = styleUrls.find(map_style);
m_map->setStyleUrl(QString::fromStdString(it->second));
}
previous_map_style = map_style;
}
void MapWindow::setError(const QString &err_str) {
if (err_str != error->text()) {
error->setText(err_str);
error->setVisible(!err_str.isEmpty());
if (!err_str.isEmpty()) map_instructions->setVisible(false);
}
}
void MapWindow::resizeGL(int w, int h) {
m_map->resize(size() / MAP_SCALE);
map_overlay->setFixedSize(width(), height());
}
void MapWindow::initializeGL() {
m_map.reset(new QMapLibre::Map(this, m_settings, size(), 1));
if (last_position) {
m_map->setCoordinateZoom(*last_position, MAX_ZOOM);
} else {
m_map->setCoordinateZoom(QMapLibre::Coordinate(64.31990695292795, -149.79038934046247), MIN_ZOOM);
}
m_map->setMargins({0, 350, 0, 50});
m_map->setPitch(MIN_PITCH);
m_map->setStyleUrl("mapbox://styles/commaai/clkqztk0f00ou01qyhsa5bzpj");
QObject::connect(m_map.data(), &QMapLibre::Map::mapChanged, [=](QMapLibre::Map::MapChange change) {
// set global animation duration to 0 ms so visibility changes are instant
if (change == QMapLibre::Map::MapChange::MapChangeDidFinishLoadingStyle) {
m_map->setTransitionOptions(0, 0);
}
if (change == QMapLibre::Map::MapChange::MapChangeDidFinishLoadingMap) {
loaded_once = true;
}
});
}
void MapWindow::paintGL() {
if (!isVisible() || m_map.isNull()) return;
m_map->render();
}
void MapWindow::clearRoute() {
if (!m_map.isNull()) {
m_map->setLayoutProperty("navLayer", "visibility", "none");
m_map->setPitch(MIN_PITCH);
updateDestinationMarker();
}
map_instructions->setVisible(false);
map_eta->setVisible(false);
last_valid_nav_dest = std::nullopt;
}
void MapWindow::mousePressEvent(QMouseEvent *ev) {
m_lastPos = ev->localPos();
ev->accept();
}
void MapWindow::mouseDoubleClickEvent(QMouseEvent *ev) {
if (last_position) m_map->setCoordinate(*last_position);
if (last_bearing) m_map->setBearing(*last_bearing);
m_map->setZoom(util::map_val<float>(velocity_filter.x(), 0, 30, MAX_ZOOM, MIN_ZOOM));
update();
interaction_counter = 0;
}
void MapWindow::mouseMoveEvent(QMouseEvent *ev) {
QPointF delta = ev->localPos() - m_lastPos;
if (!delta.isNull()) {
interaction_counter = INTERACTION_TIMEOUT;
m_map->moveBy(delta / MAP_SCALE);
update();
}
m_lastPos = ev->localPos();
ev->accept();
}
void MapWindow::wheelEvent(QWheelEvent *ev) {
if (ev->orientation() == Qt::Horizontal) {
return;
}
float factor = ev->delta() / 1200.;
if (ev->delta() < 0) {
factor = factor > -1 ? factor : 1 / factor;
}
m_map->scaleBy(1 + factor, ev->pos() / MAP_SCALE);
update();
interaction_counter = INTERACTION_TIMEOUT;
ev->accept();
}
bool MapWindow::event(QEvent *event) {
if (event->type() == QEvent::Gesture) {
return gestureEvent(static_cast<QGestureEvent*>(event));
}
return QWidget::event(event);
}
bool MapWindow::gestureEvent(QGestureEvent *event) {
if (QGesture *pinch = event->gesture(Qt::PinchGesture)) {
pinchTriggered(static_cast<QPinchGesture *>(pinch));
}
return true;
}
void MapWindow::pinchTriggered(QPinchGesture *gesture) {
QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();
if (changeFlags & QPinchGesture::ScaleFactorChanged) {
// TODO: figure out why gesture centerPoint doesn't work
m_map->scaleBy(gesture->scaleFactor(), {width() / 2.0 / MAP_SCALE, height() / 2.0 / MAP_SCALE});
update();
interaction_counter = INTERACTION_TIMEOUT;
}
}
void MapWindow::offroadTransition(bool offroad) {
if (offroad) {
clearRoute();
uiState()->scene.navigate_on_openpilot = false;
routing_problem = false;
} else {
auto dest = coordinate_from_param("NavDestination");
emit requestVisible(dest.has_value());
}
last_bearing = {};
}
void MapWindow::updateDestinationMarker() {
auto nav_dest = coordinate_from_param("NavDestination");
if (nav_dest.has_value()) {
auto point = coordinate_to_collection(*nav_dest);
QMapLibre::Feature feature(QMapLibre::Feature::PointType, point, {}, {});
QVariantMap pinSource;
pinSource["type"] = "geojson";
pinSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature);
m_map->updateSource("pinSource", pinSource);
m_map->setPaintProperty("pinLayer", "visibility", "visible");
} else {
m_map->setPaintProperty("pinLayer", "visibility", "none");
}
}

View File

@@ -1,97 +0,0 @@
#pragma once
#include <optional>
#include <QGeoCoordinate>
#include <QGestureEvent>
#include <QLabel>
#include <QMap>
#include <QMapLibre/Map>
#include <QMapLibre/Settings>
#include <QMouseEvent>
#include <QOpenGLWidget>
#include <QPixmap>
#include <QPushButton>
#include <QScopedPointer>
#include <QString>
#include <QVBoxLayout>
#include <QWheelEvent>
#include "cereal/messaging/messaging.h"
#include "common/params.h"
#include "common/util.h"
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/maps/map_eta.h"
#include "selfdrive/ui/qt/maps/map_instructions.h"
class MapWindow : public QOpenGLWidget {
Q_OBJECT
public:
MapWindow(const QMapLibre::Settings &);
~MapWindow();
private:
void initializeGL() final;
void paintGL() final;
void resizeGL(int w, int h) override;
QMapLibre::Settings m_settings;
QScopedPointer<QMapLibre::Map> m_map;
void initLayers();
void mousePressEvent(QMouseEvent *ev) final;
void mouseDoubleClickEvent(QMouseEvent *ev) final;
void mouseMoveEvent(QMouseEvent *ev) final;
void wheelEvent(QWheelEvent *ev) final;
bool event(QEvent *event) final;
bool gestureEvent(QGestureEvent *event);
void pinchTriggered(QPinchGesture *gesture);
void setError(const QString &err_str);
bool loaded_once = false;
// Panning
QPointF m_lastPos;
int interaction_counter = 0;
// Position
std::optional<QMapLibre::Coordinate> last_valid_nav_dest;
std::optional<QMapLibre::Coordinate> last_position;
std::optional<float> last_bearing;
FirstOrderFilter velocity_filter;
bool locationd_valid = false;
bool routing_problem = false;
QWidget *map_overlay;
QLabel *error;
MapInstructions* map_instructions;
MapETA* map_eta;
// Blue with normal nav, green when nav is input into the model
QColor getNavPathColor(bool nav_enabled) {
return nav_enabled ? QColor("#31ee73") : QColor("#31a1ee");
}
void clearRoute();
void updateDestinationMarker();
uint64_t route_rcv_frame = 0;
// FrogPilot variables
Params params;
int previous_map_style;
uint64_t model_rcv_frame = 0;
private slots:
void updateState(const UIState &s);
public slots:
void offroadTransition(bool offroad);
signals:
void requestVisible(bool visible);
void requestSettings(bool settings);
};

View File

@@ -1,56 +0,0 @@
#include "selfdrive/ui/qt/maps/map_eta.h"
#include <QDateTime>
#include <QPainter>
#include "selfdrive/ui/qt/maps/map_helpers.h"
#include "selfdrive/ui/ui.h"
const float MANEUVER_TRANSITION_THRESHOLD = 10;
MapETA::MapETA(QWidget *parent) : QWidget(parent) {
setVisible(false);
setAttribute(Qt::WA_TranslucentBackground);
eta_doc.setUndoRedoEnabled(false);
eta_doc.setDefaultStyleSheet("body {font-family:Inter;font-size:70px;color:white;} b{font-weight:600;} td{padding:0 3px;}");
}
void MapETA::paintEvent(QPaintEvent *event) {
if (!eta_doc.isEmpty()) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
p.setBrush(QColor(0, 0, 0, 255));
QSizeF txt_size = eta_doc.size();
p.drawRoundedRect((width() - txt_size.width()) / 2 - UI_BORDER_SIZE, 0, txt_size.width() + UI_BORDER_SIZE * 2, height() + 25, 25, 25);
p.translate((width() - txt_size.width()) / 2, (height() - txt_size.height()) / 2);
eta_doc.drawContents(&p);
}
}
void MapETA::updateETA(float s, float s_typical, float d) {
// ETA
auto eta_t = QDateTime::currentDateTime().addSecs(s).time();
auto eta = format_24h ? std::pair{eta_t.toString("HH:mm"), tr("eta")}
: std::pair{eta_t.toString("h:mm a").split(' ')[0], eta_t.toString("a")};
// Remaining time
auto remaining = s < 3600 ? std::pair{QString::number(int(s / 60)), tr("min")}
: std::pair{QString("%1:%2").arg((int)s / 3600).arg(((int)s % 3600) / 60, 2, 10, QLatin1Char('0')), tr("hr")};
QString color = "#25DA6E";
if (s / s_typical > 1.5)
color = "#DA3025";
else if (s / s_typical > 1.2)
color = "#DAA725";
// Distance
auto distance = map_format_distance(d, uiState()->scene.is_metric);
eta_doc.setHtml(QString(R"(<body><table><tr style="vertical-align:bottom;"><td><b>%1</b></td><td>%2</td>
<td style="padding-left:40px;color:%3;"><b>%4</b></td><td style="padding-right:40px;color:%3;">%5</td>
<td><b>%6</b></td><td>%7</td></tr></body>)")
.arg(eta.first, eta.second, color, remaining.first, remaining.second, distance.first, distance.second));
setVisible(d >= MANEUVER_TRANSITION_THRESHOLD);
update();
}

View File

@@ -1,23 +0,0 @@
#pragma once
#include <QPaintEvent>
#include <QTextDocument>
#include <QWidget>
#include "common/params.h"
class MapETA : public QWidget {
Q_OBJECT
public:
MapETA(QWidget * parent=nullptr);
void updateETA(float seconds, float seconds_typical, float distance);
private:
void paintEvent(QPaintEvent *event) override;
void showEvent(QShowEvent *event) override { format_24h = param.getBool("NavSettingTime24h"); }
bool format_24h = false;
QTextDocument eta_doc;
Params param;
};

View File

@@ -1,153 +0,0 @@
#include "selfdrive/ui/qt/maps/map_helpers.h"
#include <algorithm>
#include <string>
#include <utility>
#include <QJsonDocument>
#include <QJsonObject>
#include "common/params.h"
#include "system/hardware/hw.h"
#include "selfdrive/ui/qt/api.h"
QString get_mapbox_token() {
// Valid for 4 weeks since we can't swap tokens on the fly
return MAPBOX_TOKEN.isEmpty() ? CommaApi::create_jwt({}, 4 * 7 * 24 * 3600) : MAPBOX_TOKEN;
}
QMapLibre::Settings get_mapbox_settings() {
QMapLibre::Settings settings;
settings.setProviderTemplate(QMapLibre::Settings::ProviderTemplate::MapboxProvider);
if (!Hardware::PC()) {
settings.setCacheDatabasePath(MAPS_CACHE_PATH);
settings.setCacheDatabaseMaximumSize(100 * 1024 * 1024);
}
settings.setApiBaseUrl(MAPS_HOST);
settings.setApiKey(get_mapbox_token());
return settings;
}
QGeoCoordinate to_QGeoCoordinate(const QMapLibre::Coordinate &in) {
return QGeoCoordinate(in.first, in.second);
}
QMapLibre::CoordinatesCollections model_to_collection(
const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF,
const cereal::LiveLocationKalman::Measurement::Reader &positionECEF,
const cereal::XYZTData::Reader &line){
Eigen::Vector3d ecef(positionECEF.getValue()[0], positionECEF.getValue()[1], positionECEF.getValue()[2]);
Eigen::Vector3d orient(calibratedOrientationECEF.getValue()[0], calibratedOrientationECEF.getValue()[1], calibratedOrientationECEF.getValue()[2]);
Eigen::Matrix3d ecef_from_local = euler2rot(orient);
QMapLibre::Coordinates coordinates;
auto x = line.getX();
auto y = line.getY();
auto z = line.getZ();
for (int i = 0; i < x.size(); i++) {
Eigen::Vector3d point_ecef = ecef_from_local * Eigen::Vector3d(x[i], y[i], z[i]) + ecef;
Geodetic point_geodetic = ecef2geodetic((ECEF){.x = point_ecef[0], .y = point_ecef[1], .z = point_ecef[2]});
coordinates.push_back({point_geodetic.lat, point_geodetic.lon});
}
return {QMapLibre::CoordinatesCollection{coordinates}};
}
QMapLibre::CoordinatesCollections coordinate_to_collection(const QMapLibre::Coordinate &c) {
QMapLibre::Coordinates coordinates{c};
return {QMapLibre::CoordinatesCollection{coordinates}};
}
QMapLibre::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader& coordinate_list) {
QMapLibre::Coordinates coordinates;
for (auto const &c : coordinate_list) {
coordinates.push_back({c.getLatitude(), c.getLongitude()});
}
return {QMapLibre::CoordinatesCollection{coordinates}};
}
QMapLibre::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list) {
QMapLibre::Coordinates coordinates;
for (auto &c : coordinate_list) {
coordinates.push_back({c.latitude(), c.longitude()});
}
return {QMapLibre::CoordinatesCollection{coordinates}};
}
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString) {
QList<QGeoCoordinate> path;
if (polylineString.isEmpty())
return path;
QByteArray data = polylineString.toLatin1();
bool parsingLatitude = true;
int shift = 0;
int value = 0;
QGeoCoordinate coord(0, 0);
for (int i = 0; i < data.length(); ++i) {
unsigned char c = data.at(i) - 63;
value |= (c & 0x1f) << shift;
shift += 5;
// another chunk
if (c & 0x20)
continue;
int diff = (value & 1) ? ~(value >> 1) : (value >> 1);
if (parsingLatitude) {
coord.setLatitude(coord.latitude() + (double)diff/1e6);
} else {
coord.setLongitude(coord.longitude() + (double)diff/1e6);
path.append(coord);
}
parsingLatitude = !parsingLatitude;
value = 0;
shift = 0;
}
return path;
}
std::optional<QMapLibre::Coordinate> coordinate_from_param(const std::string &param) {
QString json_str = QString::fromStdString(Params().get(param));
if (json_str.isEmpty()) return {};
QJsonDocument doc = QJsonDocument::fromJson(json_str.toUtf8());
if (doc.isNull()) return {};
QJsonObject json = doc.object();
if (json["latitude"].isDouble() && json["longitude"].isDouble()) {
QMapLibre::Coordinate coord(json["latitude"].toDouble(), json["longitude"].toDouble());
return coord;
} else {
return {};
}
}
// return {distance, unit}
std::pair<QString, QString> map_format_distance(float d, bool is_metric) {
auto round_distance = [](float d) -> float {
return (d > 10) ? std::nearbyint(d) : std::nearbyint(d * 10) / 10.0;
};
d = std::max(d, 0.0f);
if (is_metric) {
return (d > 500) ? std::pair{QString::number(round_distance(d / 1000)), QObject::tr("km")}
: std::pair{QString::number(50 * std::nearbyint(d / 50)), QObject::tr("m")};
} else {
float feet = d * METER_TO_FOOT;
return (feet > 500) ? std::pair{QString::number(round_distance(d * METER_TO_MILE)), QObject::tr("mi")}
: std::pair{QString::number(50 * std::nearbyint(d / 50)), QObject::tr("ft")};
}
}

View File

@@ -1,33 +0,0 @@
#pragma once
#include <optional>
#include <string>
#include <utility>
#include <QMapLibre/Map>
#include <QMapLibre/Settings>
#include <eigen3/Eigen/Dense>
#include <QGeoCoordinate>
#include "common/params.h"
#include "common/util.h"
#include "common/transformations/coordinates.hpp"
#include "common/transformations/orientation.hpp"
#include "cereal/messaging/messaging.h"
const QString MAPBOX_TOKEN = QString::fromStdString(Params().get("MapboxPublicKey"));
const QString MAPS_HOST = util::getenv("MAPS_HOST", MAPBOX_TOKEN.isEmpty() ? "https://maps.comma.ai" : "https://api.mapbox.com").c_str();
const QString MAPS_CACHE_PATH = "/data/mbgl-cache-navd.db";
QString get_mapbox_token();
QMapLibre::Settings get_mapbox_settings();
QGeoCoordinate to_QGeoCoordinate(const QMapLibre::Coordinate &in);
QMapLibre::CoordinatesCollections model_to_collection(
const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF,
const cereal::LiveLocationKalman::Measurement::Reader &positionECEF,
const cereal::XYZTData::Reader &line);
QMapLibre::CoordinatesCollections coordinate_to_collection(const QMapLibre::Coordinate &c);
QMapLibre::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader &coordinate_list);
QMapLibre::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list);
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString);
std::optional<QMapLibre::Coordinate> coordinate_from_param(const std::string &param);
std::pair<QString, QString> map_format_distance(float d, bool is_metric);

View File

@@ -1,144 +0,0 @@
#include "selfdrive/ui/qt/maps/map_instructions.h"
#include <QDir>
#include <QVBoxLayout>
#include "selfdrive/ui/qt/maps/map_helpers.h"
#include "selfdrive/ui/ui.h"
const QString ICON_SUFFIX = ".png";
MapInstructions::MapInstructions(QWidget *parent) : QWidget(parent) {
is_rhd = Params().getBool("IsRhdDetected");
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(11, UI_BORDER_SIZE, 11, 20);
QHBoxLayout *top_layout = new QHBoxLayout;
top_layout->addWidget(icon_01 = new QLabel, 0, Qt::AlignTop);
QVBoxLayout *right_layout = new QVBoxLayout;
right_layout->setContentsMargins(9, 9, 9, 0);
right_layout->addWidget(distance = new QLabel);
distance->setStyleSheet(R"(font-size: 90px;)");
right_layout->addWidget(primary = new QLabel);
primary->setStyleSheet(R"(font-size: 60px;)");
primary->setWordWrap(true);
right_layout->addWidget(secondary = new QLabel);
secondary->setStyleSheet(R"(font-size: 50px;)");
secondary->setWordWrap(true);
top_layout->addLayout(right_layout);
main_layout->addLayout(top_layout);
main_layout->addLayout(lane_layout = new QHBoxLayout);
lane_layout->setAlignment(Qt::AlignHCenter);
lane_layout->setSpacing(10);
setStyleSheet("color:white");
QPalette pal = palette();
pal.setColor(QPalette::Background, QColor(0, 0, 0, 150));
setAutoFillBackground(true);
setPalette(pal);
buildPixmapCache();
}
void MapInstructions::buildPixmapCache() {
QDir dir("../assets/navigation");
for (QString fn : dir.entryList({"*" + ICON_SUFFIX}, QDir::Files)) {
QPixmap pm(dir.filePath(fn));
QString key = fn.left(fn.size() - ICON_SUFFIX.length());
pm = pm.scaledToWidth(200, Qt::SmoothTransformation);
// Maneuver icons
pixmap_cache[key] = pm;
// lane direction icons
if (key.contains("turn_")) {
pixmap_cache["lane_" + key] = pm.scaled({125, 125}, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
// for rhd, reflect direction and then flip
if (key.contains("_left")) {
pixmap_cache["rhd_" + key.replace("_left", "_right")] = pm.transformed(QTransform().scale(-1, 1));
} else if (key.contains("_right")) {
pixmap_cache["rhd_" + key.replace("_right", "_left")] = pm.transformed(QTransform().scale(-1, 1));
}
}
}
void MapInstructions::updateInstructions(cereal::NavInstruction::Reader instruction) {
setUpdatesEnabled(false);
// Show instruction text
QString primary_str = QString::fromStdString(instruction.getManeuverPrimaryText());
QString secondary_str = QString::fromStdString(instruction.getManeuverSecondaryText());
primary->setText(primary_str);
secondary->setVisible(secondary_str.length() > 0);
secondary->setText(secondary_str);
auto distance_str_pair = map_format_distance(instruction.getManeuverDistance(), uiState()->scene.is_metric);
distance->setText(QString("%1 %2").arg(distance_str_pair.first, distance_str_pair.second));
// Show arrow with direction
QString type = QString::fromStdString(instruction.getManeuverType());
QString modifier = QString::fromStdString(instruction.getManeuverModifier());
if (!type.isEmpty()) {
QString fn = "direction_" + type;
if (!modifier.isEmpty()) {
fn += "_" + modifier;
}
fn = fn.replace(' ', '_');
bool rhd = is_rhd && (fn.contains("_left") || fn.contains("_right"));
icon_01->setPixmap(pixmap_cache[!rhd ? fn : "rhd_" + fn]);
icon_01->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
icon_01->setVisible(true);
} else {
icon_01->setVisible(false);
}
// Hide distance after arrival
distance->setVisible(type != "arrive" || instruction.getManeuverDistance() > 0);
// Show lanes
auto lanes = instruction.getLanes();
for (int i = 0; i < lanes.size(); ++i) {
bool active = lanes[i].getActive();
const auto active_direction = lanes[i].getActiveDirection();
// TODO: Make more images based on active direction and combined directions
QString fn = "lane_direction_";
// active direction has precedence
if (active && active_direction != cereal::NavInstruction::Direction::NONE) {
fn += "turn_" + DIRECTIONS[active_direction];
} else {
for (auto const &direction : lanes[i].getDirections()) {
if (direction != cereal::NavInstruction::Direction::NONE) {
fn += "turn_" + DIRECTIONS[direction];
break;
}
}
}
if (!active) {
fn += "_inactive";
}
QLabel *label = (i < lane_labels.size()) ? lane_labels[i] : lane_labels.emplace_back(new QLabel);
if (!label->parentWidget()) {
lane_layout->addWidget(label);
}
label->setPixmap(pixmap_cache[fn]);
label->setVisible(true);
}
for (int i = lanes.size(); i < lane_labels.size(); ++i) {
lane_labels[i]->setVisible(false);
}
setUpdatesEnabled(true);
setVisible(true);
}

View File

@@ -1,38 +0,0 @@
#pragma once
#include <map>
#include <vector>
#include <QHash>
#include <QHBoxLayout>
#include <QLabel>
#include "cereal/gen/cpp/log.capnp.h"
static std::map<cereal::NavInstruction::Direction, QString> DIRECTIONS = {
{cereal::NavInstruction::Direction::NONE, "none"},
{cereal::NavInstruction::Direction::LEFT, "left"},
{cereal::NavInstruction::Direction::RIGHT, "right"},
{cereal::NavInstruction::Direction::STRAIGHT, "straight"},
{cereal::NavInstruction::Direction::SLIGHT_LEFT, "slight_left"},
{cereal::NavInstruction::Direction::SLIGHT_RIGHT, "slight_right"},
};
class MapInstructions : public QWidget {
Q_OBJECT
private:
QLabel *distance;
QLabel *primary;
QLabel *secondary;
QLabel *icon_01;
QHBoxLayout *lane_layout;
bool is_rhd = false;
std::vector<QLabel *> lane_labels;
QHash<QString, QPixmap> pixmap_cache;
public:
MapInstructions(QWidget * parent=nullptr);
void buildPixmapCache();
void updateInstructions(cereal::NavInstruction::Reader instruction);
};

View File

@@ -1,43 +0,0 @@
#include "selfdrive/ui/qt/maps/map_panel.h"
#include <QHBoxLayout>
#include <QWidget>
#include "selfdrive/ui/qt/maps/map.h"
#include "selfdrive/ui/qt/maps/map_settings.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/ui.h"
MapPanel::MapPanel(const QMapLibre::Settings &mapboxSettings, QWidget *parent) : QFrame(parent) {
content_stack = new QStackedLayout(this);
content_stack->setContentsMargins(0, 0, 0, 0);
auto map = new MapWindow(mapboxSettings);
QObject::connect(uiState(), &UIState::offroadTransition, map, &MapWindow::offroadTransition);
QObject::connect(device(), &Device::interactiveTimeout, this, [=]() {
content_stack->setCurrentIndex(0);
});
QObject::connect(map, &MapWindow::requestVisible, this, [=](bool visible) {
// when we show the map for a new route, signal HomeWindow to hide the sidebar
if (visible) { emit mapPanelRequested(); }
setVisible(visible);
});
QObject::connect(map, &MapWindow::requestSettings, this, [=](bool settings) {
content_stack->setCurrentIndex(settings ? 1 : 0);
});
content_stack->addWidget(map);
auto settings = new MapSettings(true, parent);
QObject::connect(settings, &MapSettings::closeSettings, this, [=]() {
content_stack->setCurrentIndex(0);
});
content_stack->addWidget(settings);
}
void MapPanel::toggleMapSettings() {
// show settings if not visible, then toggle between map and settings
int new_index = isVisible() ? (1 - content_stack->currentIndex()) : 1;
content_stack->setCurrentIndex(new_index);
emit mapPanelRequested();
show();
}

View File

@@ -1,21 +0,0 @@
#pragma once
#include <QFrame>
#include <QMapLibre/Settings>
#include <QStackedLayout>
class MapPanel : public QFrame {
Q_OBJECT
public:
explicit MapPanel(const QMapLibre::Settings &settings, QWidget *parent = nullptr);
signals:
void mapPanelRequested();
public slots:
void toggleMapSettings();
private:
QStackedLayout *content_stack;
};

View File

@@ -1,393 +0,0 @@
#include "selfdrive/ui/qt/maps/map_settings.h"
#include <utility>
#include <QApplication>
#include <QDebug>
#include "common/util.h"
#include "selfdrive/ui/qt/request_repeater.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
static void swap(QJsonValueRef v1, QJsonValueRef v2) { std::swap(v1, v2); }
static bool locationEqual(const QJsonValue &v1, const QJsonValue &v2) {
return v1["latitude"] == v2["latitude"] && v1["longitude"] == v2["longitude"];
}
static qint64 convertTimestampToEpoch(const QString &timestamp) {
QDateTime dt = QDateTime::fromString(timestamp, Qt::ISODate);
return dt.isValid() ? dt.toSecsSinceEpoch() : 0;
}
MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) {
setContentsMargins(0, 0, 0, 0);
setAttribute(Qt::WA_NoMousePropagation);
auto *frame = new QVBoxLayout(this);
frame->setContentsMargins(40, 40, 40, 0);
frame->setSpacing(0);
auto *heading_frame = new QHBoxLayout;
heading_frame->setContentsMargins(0, 0, 0, 0);
heading_frame->setSpacing(32);
{
if (closeable) {
auto *close_btn = new QPushButton("");
close_btn->setStyleSheet(R"(
QPushButton {
color: #FFFFFF;
font-size: 100px;
padding-bottom: 8px;
border 1px grey solid;
border-radius: 70px;
background-color: #292929;
font-weight: 500;
}
QPushButton:pressed {
background-color: #3B3B3B;
}
)");
close_btn->setFixedSize(140, 140);
QObject::connect(close_btn, &QPushButton::clicked, [=]() { emit closeSettings(); });
// TODO: read map_on_left from ui state
heading_frame->addWidget(close_btn);
}
auto *heading = new QVBoxLayout;
heading->setContentsMargins(0, 0, 0, 0);
heading->setSpacing(16);
{
auto *title = new QLabel(tr("NAVIGATION"), this);
title->setStyleSheet("color: #FFFFFF; font-size: 54px; font-weight: 600;");
heading->addWidget(title);
subtitle = new QLabel(tr("Manage at connect.comma.ai"), this);
subtitle->setStyleSheet("color: #A0A0A0; font-size: 40px; font-weight: 300;");
heading->addWidget(subtitle);
}
heading_frame->addLayout(heading, 1);
}
frame->addLayout(heading_frame);
frame->addSpacing(32);
current_widget = new DestinationWidget(this);
QObject::connect(current_widget, &DestinationWidget::actionClicked,
[]() { NavManager::instance()->setCurrentDestination({}); });
frame->addWidget(current_widget);
frame->addSpacing(32);
QWidget *destinations_container = new QWidget(this);
destinations_layout = new QVBoxLayout(destinations_container);
destinations_layout->setContentsMargins(0, 32, 0, 32);
destinations_layout->setSpacing(20);
destinations_layout->addWidget(home_widget = new DestinationWidget(this));
destinations_layout->addWidget(work_widget = new DestinationWidget(this));
QObject::connect(home_widget, &DestinationWidget::navigateTo, this, &MapSettings::navigateTo);
QObject::connect(work_widget, &DestinationWidget::navigateTo, this, &MapSettings::navigateTo);
destinations_layout->addStretch();
ScrollView *destinations_scroller = new ScrollView(destinations_container, this);
destinations_scroller->setFrameShape(QFrame::NoFrame);
frame->addWidget(destinations_scroller);
setStyleSheet("MapSettings { background-color: #333333; }");
QObject::connect(NavManager::instance(), &NavManager::updated, this, &MapSettings::refresh);
wifi = new WifiManager(this);
}
void MapSettings::showEvent(QShowEvent *event) {
refresh();
}
void MapSettings::refresh() {
if (!isVisible()) return;
setUpdatesEnabled(false);
auto get_w = [this](int i) {
auto w = i < widgets.size() ? widgets[i] : widgets.emplace_back(new DestinationWidget);
if (!w->parentWidget()) {
destinations_layout->insertWidget(destinations_layout->count() - 1, w);
QObject::connect(w, &DestinationWidget::navigateTo, this, &MapSettings::navigateTo);
}
return w;
};
const auto current_dest = NavManager::instance()->currentDestination();
if (!current_dest.isEmpty()) {
current_widget->set(current_dest, true);
} else {
current_widget->unset("", true);
}
home_widget->unset(NAV_FAVORITE_LABEL_HOME);
work_widget->unset(NAV_FAVORITE_LABEL_WORK);
int n = 0;
for (auto location : NavManager::instance()->currentLocations()) {
DestinationWidget *w = nullptr;
auto dest = location.toObject();
if (dest["save_type"].toString() == NAV_TYPE_FAVORITE) {
auto label = dest["label"].toString();
if (label == NAV_FAVORITE_LABEL_HOME) w = home_widget;
if (label == NAV_FAVORITE_LABEL_WORK) w = work_widget;
}
w = w ? w : get_w(n++);
w->set(dest, false);
w->setVisible(!locationEqual(dest, current_dest));
}
for (; n < widgets.size(); ++n) widgets[n]->setVisible(false);
setUpdatesEnabled(true);
// Use IP for NOO without Prime
if (!uiState()->hasPrime()) {
QString ipAddress = QString("%1:8082").arg(wifi->getIp4Address());
subtitle->setText(tr("Manage at %1").arg(ipAddress));
}
}
void MapSettings::navigateTo(const QJsonObject &place) {
NavManager::instance()->setCurrentDestination(place);
emit closeSettings();
}
DestinationWidget::DestinationWidget(QWidget *parent) : QPushButton(parent) {
setContentsMargins(0, 0, 0, 0);
auto *frame = new QHBoxLayout(this);
frame->setContentsMargins(32, 24, 32, 24);
frame->setSpacing(32);
icon = new QLabel(this);
icon->setAlignment(Qt::AlignCenter);
icon->setFixedSize(96, 96);
icon->setObjectName("icon");
frame->addWidget(icon);
auto *inner_frame = new QVBoxLayout;
inner_frame->setContentsMargins(0, 0, 0, 0);
inner_frame->setSpacing(0);
{
title = new ElidedLabel(this);
title->setAttribute(Qt::WA_TransparentForMouseEvents);
inner_frame->addWidget(title);
subtitle = new ElidedLabel(this);
subtitle->setAttribute(Qt::WA_TransparentForMouseEvents);
subtitle->setObjectName("subtitle");
inner_frame->addWidget(subtitle);
}
frame->addLayout(inner_frame, 1);
action = new QPushButton(this);
action->setFixedSize(96, 96);
action->setObjectName("action");
action->setStyleSheet("font-size: 65px; font-weight: 600;");
QObject::connect(action, &QPushButton::clicked, this, &QPushButton::clicked);
QObject::connect(action, &QPushButton::clicked, this, &DestinationWidget::actionClicked);
frame->addWidget(action);
setFixedHeight(164);
setStyleSheet(R"(
DestinationWidget { background-color: #202123; border-radius: 10px; }
QLabel { color: #FFFFFF; font-size: 48px; font-weight: 400; }
#icon { background-color: #3B4356; border-radius: 48px; }
#subtitle { color: #9BA0A5; }
#action { border: none; border-radius: 48px; color: #FFFFFF; padding-bottom: 4px; }
/* current destination */
[current="true"] { background-color: #E8E8E8; }
[current="true"] QLabel { color: #000000; }
[current="true"] #icon { background-color: #42906B; }
[current="true"] #subtitle { color: #333333; }
[current="true"] #action { color: #202123; }
/* no saved destination */
[set="false"] QLabel { color: #9BA0A5; }
[current="true"][set="false"] QLabel { color: #A0000000; }
/* pressed */
[current="false"]:pressed { background-color: #18191B; }
[current="true"] #action:pressed { background-color: #D6D6D6; }
)");
QObject::connect(this, &QPushButton::clicked, [this]() { if (!dest.isEmpty()) emit navigateTo(dest); });
}
void DestinationWidget::set(const QJsonObject &destination, bool current) {
if (dest == destination) return;
dest = destination;
setProperty("current", current);
setProperty("set", true);
auto icon_pixmap = current ? icons().directions : icons().recent;
auto title_text = destination["place_name"].toString();
auto subtitle_text = destination["place_details"].toString();
if (destination["save_type"] == NAV_TYPE_FAVORITE) {
if (destination["label"] == NAV_FAVORITE_LABEL_HOME) {
icon_pixmap = icons().home;
subtitle_text = title_text + ", " + subtitle_text;
title_text = tr("Home");
} else if (destination["label"] == NAV_FAVORITE_LABEL_WORK) {
icon_pixmap = icons().work;
subtitle_text = title_text + ", " + subtitle_text;
title_text = tr("Work");
} else {
icon_pixmap = icons().favorite;
}
}
icon->setPixmap(icon_pixmap);
title->setText(title_text);
subtitle->setText(subtitle_text);
subtitle->setVisible(true);
// TODO: use pixmap
action->setAttribute(Qt::WA_TransparentForMouseEvents, !current);
action->setText(current ? "×" : "");
action->setVisible(true);
setStyleSheet(styleSheet());
}
void DestinationWidget::unset(const QString &label, bool current) {
dest = {};
setProperty("current", current);
setProperty("set", false);
if (label.isEmpty()) {
icon->setPixmap(icons().directions);
title->setText(tr("No destination set"));
} else {
QString title_text = label == NAV_FAVORITE_LABEL_HOME ? tr("home") : tr("work");
icon->setPixmap(label == NAV_FAVORITE_LABEL_HOME ? icons().home : icons().work);
title->setText(tr("No %1 location set").arg(title_text));
}
subtitle->setVisible(false);
action->setVisible(false);
setStyleSheet(styleSheet());
setVisible(true);
}
// singleton NavManager
NavManager *NavManager::instance() {
static NavManager *request = new NavManager(qApp);
return request;
}
NavManager::NavManager(QObject *parent) : QObject(parent) {
locations = QJsonDocument::fromJson(params.get("NavPastDestinations").c_str()).array();
current_dest = QJsonDocument::fromJson(params.get("NavDestination").c_str()).object();
if (auto dongle_id = getDongleId()) {
{
// Fetch favorite and recent locations
QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/locations";
RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_NavDestinations", 30, true);
QObject::connect(repeater, &RequestRepeater::requestDone, this, &NavManager::parseLocationsResponse);
}
{
auto param_watcher = new ParamWatcher(this);
QObject::connect(param_watcher, &ParamWatcher::paramChanged, this, &NavManager::updated);
// Destination set while offline
QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/next";
HttpRequest *deleter = new HttpRequest(this);
RequestRepeater *repeater = new RequestRepeater(this, url, "", 10, true);
QObject::connect(repeater, &RequestRepeater::requestDone, [=](const QString &resp, bool success) {
if (success && resp != "null") {
if (params.get("NavDestination").empty()) {
qWarning() << "Setting NavDestination from /next" << resp;
params.put("NavDestination", resp.toStdString());
} else {
qWarning() << "Got location from /next, but NavDestination already set";
}
// Send DELETE to clear destination server side
deleter->sendRequest(url, HttpRequest::Method::DELETE);
}
// athena can set destination at any time
param_watcher->addParam("NavDestination");
current_dest = QJsonDocument::fromJson(params.get("NavDestination").c_str()).object();
emit updated();
});
}
}
}
void NavManager::parseLocationsResponse(const QString &response, bool success) {
if (!success || response == prev_response) return;
prev_response = response;
QJsonDocument doc = QJsonDocument::fromJson(response.trimmed().toUtf8());
if (doc.isNull()) {
qWarning() << "JSON Parse failed on navigation locations" << response;
return;
}
// set last activity time.
auto remote_locations = doc.array();
for (QJsonValueRef loc : remote_locations) {
auto obj = loc.toObject();
auto serverTime = convertTimestampToEpoch(obj["modified"].toString());
obj.insert("time", qMax(serverTime, getLastActivity(obj)));
loc = obj;
}
locations = remote_locations;
sortLocations();
emit updated();
}
void NavManager::sortLocations() {
// Sort: alphabetical FAVORITES, and then most recent.
// We don't need to care about the ordering of HOME and WORK. DestinationWidget always displays them at the top.
std::stable_sort(locations.begin(), locations.end(), [](const QJsonValue &a, const QJsonValue &b) {
if (a["save_type"] == NAV_TYPE_FAVORITE || b["save_type"] == NAV_TYPE_FAVORITE) {
return (std::tuple(a["save_type"].toString(), a["place_name"].toString()) <
std::tuple(b["save_type"].toString(), b["place_name"].toString()));
} else {
return a["time"].toVariant().toLongLong() > b["time"].toVariant().toLongLong();
}
});
write_param_future = std::async(std::launch::async, [destinations = QJsonArray(locations)]() {
Params().put("NavPastDestinations", QJsonDocument(destinations).toJson().toStdString());
});
}
qint64 NavManager::getLastActivity(const QJsonObject &loc) const {
qint64 last_activity = 0;
auto it = std::find_if(locations.begin(), locations.end(),
[&loc](const QJsonValue &l) { return locationEqual(loc, l); });
if (it != locations.end()) {
auto tm = it->toObject().value("time");
if (!tm.isUndefined() && !tm.isNull()) {
last_activity = tm.toVariant().toLongLong();
}
}
return last_activity;
}
void NavManager::setCurrentDestination(const QJsonObject &loc) {
current_dest = loc;
if (!current_dest.isEmpty()) {
current_dest["time"] = QDateTime::currentSecsSinceEpoch();
auto it = std::find_if(locations.begin(), locations.end(),
[&loc](const QJsonValue &l) { return locationEqual(loc, l); });
if (it != locations.end()) {
*it = current_dest;
sortLocations();
}
params.put("NavDestination", QJsonDocument(current_dest).toJson().toStdString());
} else {
params.remove("NavDestination");
}
emit updated();
}

View File

@@ -1,108 +0,0 @@
#pragma once
#include <future>
#include <vector>
#include <QFrame>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include "common/params.h"
#include "selfdrive/ui/qt/network/wifi_manager.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/controls.h"
const QString NAV_TYPE_FAVORITE = "favorite";
const QString NAV_TYPE_RECENT = "recent";
const QString NAV_FAVORITE_LABEL_HOME = "home";
const QString NAV_FAVORITE_LABEL_WORK = "work";
class DestinationWidget;
class NavManager : public QObject {
Q_OBJECT
public:
static NavManager *instance();
QJsonArray currentLocations() const { return locations; }
QJsonObject currentDestination() const { return current_dest; }
void setCurrentDestination(const QJsonObject &loc);
qint64 getLastActivity(const QJsonObject &loc) const;
signals:
void updated();
private:
NavManager(QObject *parent);
void parseLocationsResponse(const QString &response, bool success);
void sortLocations();
Params params;
QString prev_response;
QJsonArray locations;
QJsonObject current_dest;
std::future<void> write_param_future;
};
class MapSettings : public QFrame {
Q_OBJECT
public:
explicit MapSettings(bool closeable = false, QWidget *parent = nullptr);
void navigateTo(const QJsonObject &place);
private:
void showEvent(QShowEvent *event) override;
void refresh();
QVBoxLayout *destinations_layout;
DestinationWidget *current_widget;
DestinationWidget *home_widget;
DestinationWidget *work_widget;
std::vector<DestinationWidget *> widgets;
// FrogPilot variables
QLabel *subtitle;
WifiManager *wifi;
signals:
void closeSettings();
};
class DestinationWidget : public QPushButton {
Q_OBJECT
public:
explicit DestinationWidget(QWidget *parent = nullptr);
void set(const QJsonObject &location, bool current = false);
void unset(const QString &label, bool current = false);
signals:
void actionClicked();
void navigateTo(const QJsonObject &destination);
private:
struct NavIcons {
QPixmap home, work, favorite, recent, directions;
};
static NavIcons icons() {
static NavIcons nav_icons {
loadPixmap("../assets/navigation/icon_home.svg", {48, 48}),
loadPixmap("../assets/navigation/icon_work.svg", {48, 48}),
loadPixmap("../assets/navigation/icon_favorite.svg", {48, 48}),
loadPixmap("../assets/navigation/icon_recent.svg", {48, 48}),
loadPixmap("../assets/navigation/icon_directions.svg", {48, 48}),
};
return nav_icons;
}
private:
QLabel *icon, *title, *subtitle;
QPushButton *action;
QJsonObject dest;
};

View File

@@ -205,16 +205,6 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
});
list->addItem(hiddenNetworkButton);
// Set initial config
wifi->updateGsmSettings(roamingEnabled, QString::fromStdString(params.get("GsmApn")), metered);
connect(uiState(), &UIState::primeTypeChanged, this, [=](PrimeType prime_type) {
bool gsmVisible = prime_type == PrimeType::NONE || prime_type == PrimeType::LITE;
roamingToggle->setVisible(gsmVisible);
editApnButton->setVisible(gsmVisible);
meteredToggle->setVisible(gsmVisible);
});
main_layout->addWidget(new ScrollView(list, this));
main_layout->addStretch(1);
}

View File

@@ -1,7 +1,6 @@
#include "selfdrive/ui/qt/network/wifi_manager.h"
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/widgets/prime.h"
#include "common/params.h"
#include "common/swaglog.h"
@@ -432,10 +431,8 @@ void WifiManager::addTetheringConnection() {
}
void WifiManager::tetheringActivated(QDBusPendingCallWatcher *call) {
int prime_type = uiState()->primeType();
int ipv4_forward = (prime_type == PrimeType::NONE || prime_type == PrimeType::LITE);
if (!ipv4_forward) {
// Todo: this is false if not prime. Override?
if (!ipv4_forward && false) {
QTimer::singleShot(5000, this, [=] {
qWarning() << "net.ipv4.ip_forward = 0";
std::system("sudo sysctl net.ipv4.ip_forward=0");

View File

@@ -26,7 +26,6 @@
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/control_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h"
@@ -79,20 +78,6 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
tr("Display speed in km/h instead of mph."),
"../assets/offroad/icon_metric.png",
},
#ifdef ENABLE_MAPS
{
"NavSettingTime24h",
tr("Show ETA in 24h Format"),
tr("Use 24h format instead of am/pm"),
"../assets/offroad/icon_metric.png",
},
{
"NavSettingLeftSide",
tr("Show Map on Left Side of UI"),
tr("Show map on left side when in split screen view."),
"../assets/offroad/icon_road.png",
},
#endif
};
@@ -161,8 +146,6 @@ void TogglesPanel::updateToggles() {
disengage_on_accelerator_toggle->setVisible(!params.getBool("AlwaysOnLateral"));
auto driver_camera_toggle = toggles["RecordFront"];
driver_camera_toggle->setVisible(!(params.getBool("DeviceManagement") && params.getBool("NoLogging") && params.getBool("NoUploads")));
auto nav_settings_left_toggle = toggles["NavSettingLeftSide"];
nav_settings_left_toggle->setVisible(!params.getBool("FullMap"));
auto experimental_mode_toggle = toggles["ExperimentalMode"];
auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"];
@@ -178,10 +161,6 @@ void TogglesPanel::updateToggles() {
.arg(tr("Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. "
"Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; "
"mistakes should be expected."))
.arg(tr("Navigate on openpilot"))
.arg(tr("When navigation has a destination, openpilot will input the map information into the model. This provides useful context for the model and allows openpilot to keep left or right "
"appropriately at forks/exits. Lane change behavior is unchanged and still activated by the driver. This is an alpha quality feature; mistakes should be expected, particularly around "
"exits and forks. These mistakes can include unintended laneline crossings, late exit taking, driving towards dividing barriers in the gore areas, etc."))
.arg(tr("New Driving Visualization"))
.arg(tr("The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. "
"When a navigation destination is set and the driving model is using it as input, the driving path on the map will turn green."));
@@ -651,7 +630,6 @@ void SettingsWindow::showEvent(QShowEvent *event) {
void SettingsWindow::setCurrentPanel(int index, const QString &param) {
panel_widget->setCurrentIndex(index);
nav_btns->buttons()[index]->setChecked(true);
if (!param.isEmpty()) {
emit expandToggleDescription(param);
}
@@ -715,16 +693,13 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
{tr("Toggles"), toggles},
{tr("Software"), new SoftwarePanel(this)},
{tr("Controls"), frogpilotControls},
{tr("Navigation"), new FrogPilotNavigationPanel(this)},
{tr("Vehicles"), new FrogPilotVehiclesPanel(this)},
{tr("Visuals"), frogpilotVisuals},
};
nav_btns = new QButtonGroup(this);
for (auto &[name, panel] : panels) {
QPushButton *btn = new QPushButton(name);
btn->setCheckable(true);
btn->setChecked(nav_btns->buttons().size() == 0);
btn->setStyleSheet(R"(
QPushButton {
color: grey;
@@ -741,7 +716,6 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
}
)");
btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
nav_btns->addButton(btn);
sidebar_layout->addWidget(btn, 0, Qt::AlignRight);
const int lr_margin = name != tr("Network") ? 50 : 0; // Network panel handles its own margins

View File

@@ -43,7 +43,6 @@ signals:
private:
QPushButton *sidebar_alert_widget;
QWidget *sidebar_widget;
QButtonGroup *nav_btns;
QStackedWidget *panel_widget;
// FrogPilot variables

View File

@@ -13,10 +13,6 @@
#include "common/swaglog.h"
#include "common/timing.h"
#include "selfdrive/ui/qt/util.h"
#ifdef ENABLE_MAPS
#include "selfdrive/ui/qt/maps/map_helpers.h"
#include "selfdrive/ui/qt/maps/map_panel.h"
#endif
static void drawIcon(QPainter &p, const QPoint &center, const QPixmap &img, const QBrush &bg, float opacity, const int angle = 0) {
p.setRenderHint(QPainter::Antialiasing);
@@ -60,16 +56,6 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent), scene(uiState()->
split->setSpacing(0);
split->addWidget(nvg);
if (getenv("DUAL_CAMERA_VIEW")) {
CameraWidget *arCam = new CameraWidget("camerad", VISION_STREAM_ROAD, true, this);
split->insertWidget(0, arCam);
}
if (getenv("MAP_RENDER_VIEW")) {
CameraWidget *map_render = new CameraWidget("navd", VISION_STREAM_MAP, false, this);
split->insertWidget(0, map_render);
}
stacked_layout->addWidget(split_wrapper);
alerts = new OnroadAlerts(this);
@@ -82,7 +68,6 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent), scene(uiState()->
setAttribute(Qt::WA_OpaquePaintEvent);
QObject::connect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState);
QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition);
QObject::connect(uiState(), &UIState::primeChanged, this, &OnroadWindow::primeChanged);
QObject::connect(&clickTimer, &QTimer::timeout, this, [this]() {
clickTimer.stop();
@@ -99,12 +84,6 @@ void OnroadWindow::updateState(const UIState &s) {
Alert alert = Alert::get(*(s.sm), s.scene.started_frame);
alerts->updateAlert(alert);
if (s.scene.map_on_left || scene.full_map) {
split->setDirection(QBoxLayout::LeftToRight);
} else {
split->setDirection(QBoxLayout::RightToLeft);
}
nvg->updateState(s);
QColor bgColor = bg_colors[s.status];
@@ -194,19 +173,6 @@ void OnroadWindow::mousePressEvent(QMouseEvent* e) {
// widgetClicked = true;
// }
#ifdef ENABLE_MAPS
if (map != nullptr && !widgetClicked) {
// Switch between map and sidebar when using navigate on openpilot
bool sidebarVisible = geometry().x() > 0;
bool show_map = uiState()->scene.navigate_on_openpilot ? sidebarVisible : !sidebarVisible;
map->setVisible(show_map && !map->isVisible());
if (scene.big_map) {
map->setFixedWidth(width());
} else {
map->setFixedWidth(topWidget(this)->width() / 2 - UI_BORDER_SIZE);
}
}
#endif
// propagation event to parent(HomeWindow)
if (!widgetClicked) {
QWidget::mousePressEvent(e);
@@ -214,39 +180,10 @@ void OnroadWindow::mousePressEvent(QMouseEvent* e) {
}
void OnroadWindow::offroadTransition(bool offroad) {
#ifdef ENABLE_MAPS
if (!offroad) {
if (map == nullptr && (uiState()->hasPrime() || !MAPBOX_TOKEN.isEmpty())) {
auto m = new MapPanel(get_mapbox_settings());
map = m;
QObject::connect(m, &MapPanel::mapPanelRequested, this, &OnroadWindow::mapPanelRequested);
QObject::connect(nvg->map_settings_btn, &MapSettingsButton::clicked, m, &MapPanel::toggleMapSettings);
QObject::connect(nvg->map_settings_btn_bottom, &MapSettingsButton::clicked, m, &MapPanel::toggleMapSettings);
nvg->map_settings_btn->setEnabled(true);
m->setFixedWidth(topWidget(this)->width() / 2 - UI_BORDER_SIZE);
split->insertWidget(0, m);
// hidden by default, made visible when navRoute is published
m->setVisible(false);
}
}
#endif
alerts->updateAlert({});
}
void OnroadWindow::primeChanged(bool prime) {
#ifdef ENABLE_MAPS
if (map && (!prime && MAPBOX_TOKEN.isEmpty())) {
nvg->map_settings_btn->setEnabled(false);
nvg->map_settings_btn->setVisible(false);
map->deleteLater();
map = nullptr;
}
#endif
}
void OnroadWindow::paintEvent(QPaintEvent *event) {
QPainter p(this);
@@ -448,100 +385,85 @@ void ExperimentalButton::changeMode() {
}
void ExperimentalButton::updateState(const UIState &s, bool leadInfo) {
const auto cs = (*s.sm)["controlsState"].getControlsState();
bool eng = cs.getEngageable() || cs.getEnabled() || scene.always_on_lateral_active;
if ((cs.getExperimentalMode() != experimental_mode) || (eng != engageable)) {
engageable = eng;
experimental_mode = cs.getExperimentalMode();
update();
}
// const auto cs = (*s.sm)["controlsState"].getControlsState();
// bool eng = cs.getEngageable() || cs.getEnabled() || scene.always_on_lateral_active;
// if ((cs.getExperimentalMode() != experimental_mode) || (eng != engageable)) {
// engageable = eng;
// experimental_mode = cs.getExperimentalMode();
// update();
// }
// FrogPilot variables
int randomEvent = scene.current_random_event;
// // FrogPilot variables
// int randomEvent = scene.current_random_event;
rotatingWheel = scene.rotating_wheel;
wheelIcon = scene.wheel_icon;
wheelIconGif = 0;
// rotatingWheel = scene.rotating_wheel;
// wheelIcon = scene.wheel_icon;
// wheelIconGif = 0;
y_offset = leadInfo ? 10 : 0;
// y_offset = leadInfo ? 10 : 0;
if (randomEvent == 0 && gifLabel) {
delete gifLabel;
gifLabel = nullptr;
} else if (randomEvent == 1) {
static int rotationDegree = 0;
rotationDegree = (rotationDegree + 36) % 360;
steeringAngleDeg = rotationDegree;
wheelIcon = 7;
update();
// if (randomEvent == 0 && gifLabel) {
// delete gifLabel;
// gifLabel = nullptr;
// } else if (randomEvent == 1) {
// static int rotationDegree = 0;
// rotationDegree = (rotationDegree + 36) % 360;
// steeringAngleDeg = rotationDegree;
// wheelIcon = 7;
// update();
} else if (randomEvent == 2 || randomEvent == 3 || randomEvent == 4) {
if (!gifLabel) {
gifLabel = new QLabel(this);
QMovie *movie = wheelImagesGif[randomEvent - 1];
if (movie) {
gifLabel->setMovie(movie);
gifLabel->setFixedSize(img_size, img_size);
gifLabel->move((width() - gifLabel->width()) / 2, (height() - gifLabel->height()) / 2 + y_offset);
gifLabel->movie()->start();
}
}
gifLabel->show();
wheelIconGif = randomEvent - 1;
update();
// } else if (randomEvent == 2 || randomEvent == 3 || randomEvent == 4) {
// if (!gifLabel) {
// gifLabel = new QLabel(this);
// QMovie *movie = wheelImagesGif[randomEvent - 1];
// if (movie) {
// gifLabel->setMovie(movie);
// gifLabel->setFixedSize(img_size, img_size);
// gifLabel->move((width() - gifLabel->width()) / 2, (height() - gifLabel->height()) / 2 + y_offset);
// gifLabel->movie()->start();
// }
// }
// gifLabel->show();
// wheelIconGif = randomEvent - 1;
// update();
} else if (rotatingWheel && steeringAngleDeg != scene.steering_angle_deg) {
steeringAngleDeg = scene.steering_angle_deg;
update();
steeringAngleDeg = scene.steering_angle_deg;
} else if (!rotatingWheel) {
steeringAngleDeg = 0;
}
// } else if (rotatingWheel && steeringAngleDeg != scene.steering_angle_deg) {
// steeringAngleDeg = scene.steering_angle_deg;
// update();
// steeringAngleDeg = scene.steering_angle_deg;
// } else if (!rotatingWheel) {
// steeringAngleDeg = 0;
// }
}
void ExperimentalButton::paintEvent(QPaintEvent *event) {
if (wheelIcon < 0) {
return;
}
// if (wheelIcon < 0) {
// return;
// }
QPainter p(this);
engage_img = wheelImages[wheelIcon];
QPixmap img = wheelIcon != 0 ? engage_img : (experimental_mode ? experimental_img : engage_img);
QMovie *gif = wheelImagesGif[wheelIconGif];
// QPainter p(this);
// engage_img = wheelImages[wheelIcon];
// QPixmap img = wheelIcon != 0 ? engage_img : (experimental_mode ? experimental_img : engage_img);
// QMovie *gif = wheelImagesGif[wheelIconGif];
QColor background_color = wheelIcon != 0 && !isDown() && engageable ?
(scene.always_on_lateral_active ? QColor(10, 186, 181, 255) :
(scene.conditional_status == 1 || scene.conditional_status == 3 || scene.conditional_status == 5 ? QColor(255, 246, 0, 255) :
(experimental_mode ? QColor(218, 111, 37, 241) :
(scene.traffic_mode_active ? QColor(201, 34, 49, 255) :
(scene.navigate_on_openpilot ? QColor(49, 161, 238, 255) : QColor(0, 0, 0, 166)))))) :
QColor(0, 0, 0, 166);
// QColor background_color = wheelIcon != 0 && !isDown() && engageable ?
// (scene.always_on_lateral_active ? QColor(10, 186, 181, 255) :
// (scene.conditional_status == 1 || scene.conditional_status == 3 || scene.conditional_status == 5 ? QColor(255, 246, 0, 255) :
// (experimental_mode ? QColor(218, 111, 37, 241) :
// (scene.traffic_mode_active ? QColor(201, 34, 49, 255) :
// (scene.navigate_on_openpilot ? QColor(49, 161, 238, 255) : QColor(0, 0, 0, 166)))))) :
// QColor(0, 0, 0, 166);
if (!(scene.map_open && scene.big_map)) {
if (wheelIconGif != 0) {
drawIconGif(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), *gif, background_color, 1.0);
} else {
drawIcon(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, background_color, (isDown() || !engageable) ? 0.6 : 1.0, steeringAngleDeg);
}
}
// if (!(scene.map_open && scene.big_map)) {
// if (wheelIconGif != 0) {
// drawIconGif(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), *gif, background_color, 1.0);
// } else {
// drawIcon(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, background_color, (isDown() || !engageable) ? 0.6 : 1.0, steeringAngleDeg);
// }
// }
}
// MapSettingsButton
MapSettingsButton::MapSettingsButton(QWidget *parent) : QPushButton(parent) {
setFixedSize(btn_size, btn_size + 20);
settings_img = loadPixmap("../assets/navigation/icon_directions_outlined.svg", {img_size, img_size});
// hidden by default, made visible if map is created (has prime or mapbox token)
setVisible(false);
setEnabled(false);
}
void MapSettingsButton::paintEvent(QPaintEvent *event) {
QPainter p(this);
drawIcon(p, QPoint(btn_size / 2, btn_size / 2), settings_img, QColor(0, 0, 0, 166), isDown() ? 0.6 : 1.0);
}
// Window that shows camera view and variety of info drawn on top
AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* parent) : fps_filter(UI_FREQ, 3, 1. / UI_FREQ), CameraWidget("camerad", type, true, parent), scene(uiState()->scene) {
@@ -572,9 +494,6 @@ AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* par
main_layout->addLayout(top_right_layout, 0);
main_layout->setAlignment(top_right_layout, Qt::AlignTop | Qt::AlignRight);
map_settings_btn = new MapSettingsButton(this);
main_layout->addWidget(map_settings_btn, 0, Qt::AlignBottom | Qt::AlignRight);
dm_img = loadPixmap("../assets/img_driver_face.png", {img_size + 5, img_size + 5});
// Initialize FrogPilot widgets
@@ -586,10 +505,8 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
const SubMaster &sm = *(s.sm);
const bool cs_alive = sm.alive("controlsState");
const bool nav_alive = sm.alive("navInstruction") && sm["navInstruction"].getValid();
const auto cs = sm["controlsState"].getControlsState();
const auto car_state = sm["carState"].getCarState();
const auto nav_instruction = sm["navInstruction"].getNavInstruction();
// Handle older routes where vCruiseCluster is not set
float v_cruise = cs.getVCruiseCluster() == 0.0 ? cs.getVCruise() : cs.getVCruiseCluster();
@@ -605,19 +522,19 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
speed = cs_alive ? std::max<float>(0.0, v_ego) : 0.0;
speed *= s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH;
auto speed_limit_sign = nav_instruction.getSpeedLimitSign();
speedLimit = slcOverridden ? scene.speed_limit_overridden_speed : speedLimitController ? scene.speed_limit : nav_alive ? nav_instruction.getSpeedLimit() : 0.0;
speedLimit *= (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH);
if (speedLimitController && !slcOverridden) {
speedLimit = speedLimit - (showSLCOffset ? slcSpeedLimitOffset : 0);
}
// auto speed_limit_sign = nav_instruction.getSpeedLimitSign();
// speedLimit = slcOverridden ? scene.speed_limit_overridden_speed : speedLimitController ? scene.speed_limit : nav_alive ? nav_instruction.getSpeedLimit() : 0.0;
// speedLimit *= (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH);
// if (speedLimitController && !slcOverridden) {
// speedLimit = speedLimit - (showSLCOffset ? slcSpeedLimitOffset : 0);
// }
has_us_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::MUTCD) || (speedLimitController && !useViennaSLCSign);
has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA) && !(speedLimitController && !useViennaSLCSign) || (speedLimitController && useViennaSLCSign);
is_metric = s.scene.is_metric;
speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph");
hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE || customSignals != 0 && (turnSignalLeft || turnSignalRight) || bigMapOpen);
status = s.status;
// has_us_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::MUTCD) || (speedLimitController && !useViennaSLCSign);
// has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA) && !(speedLimitController && !useViennaSLCSign) || (speedLimitController && useViennaSLCSign);
// is_metric = s.scene.is_metric;
// speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph");
// hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE || customSignals != 0 && (turnSignalLeft || turnSignalRight));
// status = s.status;
// update engageability/experimental mode button
experimental_btn->updateState(s, leadInfo);
@@ -631,12 +548,6 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
// DM icon transition
dm_fade_state = std::clamp(dm_fade_state+0.2*(0.5-dmActive), 0.0, 1.0);
// hide map settings button for alerts and flip for right hand DM
if (map_settings_btn->isEnabled()) {
map_settings_btn->setVisible(!hideBottomIcons && compass && !scene.hide_map_icon);
main_layout->setAlignment(map_settings_btn, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignTop);
}
updateFrogPilotWidgets();
}
@@ -777,7 +688,7 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) {
}
// current speed
if (!(scene.hide_speed || bigMapOpen)) {
if (!(scene.hide_speed)) {
// CLEARPILOT changes to 120 from ~176
// Maybe we want to hide this?
p.setFont(InterFont(140, QFont::Bold));
@@ -1193,9 +1104,6 @@ void AnnotatedCameraWidget::initializeFrogPilotWidgets() {
compass_img = new Compass(this);
// bottom_layout->addWidget(compass_img);
map_settings_btn_bottom = new MapSettingsButton(this);
bottom_layout->addWidget(map_settings_btn_bottom);
main_layout->addLayout(bottom_layout);
themeConfiguration = {
@@ -1293,9 +1201,6 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets() {
obstacleDistance = scene.obstacle_distance;
obstacleDistanceStock = scene.obstacle_distance_stock;
mapOpen = scene.map_open;
bigMapOpen = mapOpen && scene.big_map;
onroadDistanceButton = scene.onroad_distance_button;
roadNameUI = scene.road_name_ui;
@@ -1350,7 +1255,7 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets() {
}
void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
if ((showAlwaysOnLateralStatusBar || showConditionalExperimentalStatusBar || roadNameUI) && !bigMapOpen) {
if ((showAlwaysOnLateralStatusBar || showConditionalExperimentalStatusBar || roadNameUI)) {
drawStatusBar(p);
}
@@ -1363,7 +1268,7 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
// animationTimer->stop();
// }
if (leadInfo && !bigMapOpen) {
if (leadInfo) {
drawLeadInfo(p);
}
@@ -1391,12 +1296,6 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
// pedal_icons->updateState();
// }
map_settings_btn_bottom->setEnabled(map_settings_btn->isEnabled());
if (map_settings_btn_bottom->isEnabled()) {
map_settings_btn_bottom->setVisible(!hideBottomIcons && !compass && !scene.hide_map_icon);
bottom_layout->setAlignment(map_settings_btn_bottom, rightHandDM ? Qt::AlignLeft : Qt::AlignRight);
}
// recorder_btn->setVisible(scene.screen_recorder && !mapOpen);
recorder_btn->setVisible(false);
}
@@ -1581,7 +1480,7 @@ void AnnotatedCameraWidget::drawLeadInfo(QPainter &p) {
constexpr int maxAccelDuration = 5000;
QString accelerationUnit = tr(" m/s²");
leadDistanceUnit = tr(mapOpen ? "m" : "meters");
leadDistanceUnit = tr("meters");
leadSpeedUnit = tr("m/s");
float accelerationConversion = 1.0f;
@@ -1635,15 +1534,15 @@ void AnnotatedCameraWidget::drawLeadInfo(QPainter &p) {
.arg(accelerationUnit);
QString maxAccSuffix;
if (!mapOpen) {
maxAccSuffix = tr(" - Max: %1%2")
.arg(maxAcceleration * accelerationConversion, 0, 'f', 2)
.arg(accelerationUnit);
}
QString obstacleText = createText(mapOpen ? tr(" | Obstacle: ") : tr(" | Obstacle Factor: "), obstacleDistance);
QString stopText = createText(mapOpen ? tr(" - Stop: ") : tr(" - Stop Factor: "), scene.stopped_equivalence);
QString followText = " = " + createText(mapOpen ? tr("Follow: ") : tr("Follow Distance: "), scene.desired_follow);
QString obstacleText = createText(tr(" | Obstacle Factor: "), obstacleDistance);
QString stopText = createText(tr(" - Stop Factor: "), scene.stopped_equivalence);
QString followText = " = " + createText(tr("Follow Distance: "), scene.desired_follow);
auto createDiffText = [&](const double data, const double stockData) {
double difference = std::round((data - stockData) * distanceConversion);
@@ -1784,21 +1683,21 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
{4, tr("Experimental Mode manually activated")},
{5, tr("Conditional Experimental overridden")},
{6, tr("Experimental Mode manually activated")},
{7, tr("Experimental Mode activated for") + (mapOpen ? tr(" intersection") : tr(" upcoming intersection"))},
{8, tr("Experimental Mode activated for") + (mapOpen ? tr(" turn") : tr(" upcoming turn"))},
{9, tr("Experimental Mode activated due to") + (mapOpen ? tr(" SLC") : tr(" no speed limit set"))},
{10, tr("Experimental Mode activated due to") + (mapOpen ? tr(" speed") : tr(" speed being less than ") + QString::number(scene.conditional_speed_lead) + (is_metric ? tr(" kph") : tr(" mph")))},
{11, tr("Experimental Mode activated due to") + (mapOpen ? tr(" speed") : tr(" speed being less than ") + QString::number(scene.conditional_speed) + (is_metric ? tr(" kph") : tr(" mph")))},
{7, tr("Experimental Mode activated for") + (tr(" upcoming intersection"))},
{8, tr("Experimental Mode activated for") + (tr(" upcoming turn"))},
{9, tr("Experimental Mode activated due to") + (tr(" no speed limit set"))},
{10, tr("Experimental Mode activated due to") + (tr(" speed being less than ") + QString::number(scene.conditional_speed_lead) + (is_metric ? tr(" kph") : tr(" mph")))},
{11, tr("Experimental Mode activated due to") + (tr(" speed being less than ") + QString::number(scene.conditional_speed) + (is_metric ? tr(" kph") : tr(" mph")))},
{12, tr("Experimental Mode activated for slower lead")},
{13, tr("Experimental Mode activated for turn") + (mapOpen ? "" : tr(" / lane change"))},
{13, tr("Experimental Mode activated for turn") + (tr(" / lane change"))},
{14, tr("Experimental Mode activated for curve")},
{15, tr("Experimental Mode activated for stop") + (mapOpen ? "" : tr(" sign / stop light"))},
{15, tr("Experimental Mode activated for stop") + (tr(" sign / stop light"))},
};
QString roadName = roadNameUI ? QString::fromStdString(paramsMemory.get("RoadName")) : QString();
if (alwaysOnLateralActive && showAlwaysOnLateralStatusBar) {
newStatus = tr("Always On Lateral active") + (mapOpen ? "" : tr(". Press the \"Cruise Control\" button to disable"));
newStatus = tr("Always On Lateral active") + (tr(". Press the \"Cruise Control\" button to disable"));
} else if (showConditionalExperimentalStatusBar) {
newStatus = conditionalStatusMap[status != STATUS_DISENGAGED ? conditionalStatus : 0];
}
@@ -1807,7 +1706,7 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
QString lkasSuffix = tr(". Double press the \"LKAS\" button to revert");
QString screenSuffix = tr(". Double tap the screen to revert");
if (!alwaysOnLateralActive && !mapOpen && status != STATUS_DISENGAGED && !newStatus.isEmpty()) {
if (!alwaysOnLateralActive && status != STATUS_DISENGAGED && !newStatus.isEmpty()) {
if (conditionalStatus == 1 || conditionalStatus == 2) {
newStatus += distanceSuffix;
} else if (conditionalStatus == 3 || conditionalStatus == 4) {

View File

@@ -127,19 +127,6 @@ private:
int y_offset;
};
class MapSettingsButton : public QPushButton {
Q_OBJECT
public:
explicit MapSettingsButton(QWidget *parent = 0);
private:
void paintEvent(QPaintEvent *event) override;
QPixmap settings_img;
};
class PedalIcons : public QWidget {
Q_OBJECT
@@ -170,9 +157,6 @@ public:
void updateState(const UIState &s);
void updateLaneEdgeColor(QColor &bgColor);
MapSettingsButton *map_settings_btn;
MapSettingsButton *map_settings_btn_bottom;
private:
void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255);
@@ -221,13 +205,11 @@ private:
QHBoxLayout *bottom_layout;
bool alwaysOnLateralActive;
bool bigMapOpen;
bool blindSpotLeft;
bool blindSpotRight;
bool compass;
bool experimentalMode;
bool leadInfo;
bool mapOpen;
bool onroadDistanceButton;
bool roadNameUI;
bool showAlwaysOnLateralStatusBar;
@@ -297,11 +279,8 @@ class OnroadWindow : public QWidget {
public:
OnroadWindow(QWidget* parent = 0);
bool isMapVisible() const { return map && map->isVisible(); }
void showMapPanel(bool show) { if (map) map->setVisible(show); }
signals:
void mapPanelRequested();
// signals:
private:
void paintEvent(QPaintEvent *event);
@@ -322,6 +301,5 @@ private:
private slots:
void offroadTransition(bool offroad);
void primeChanged(bool prime);
void updateState(const UIState &s);
};

View File

@@ -1,283 +0,0 @@
#include "selfdrive/ui/qt/widgets/prime.h"
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLabel>
#include <QPushButton>
#include <QStackedWidget>
#include <QTimer>
#include <QVBoxLayout>
#include <QrCode.hpp>
#include "selfdrive/ui/qt/request_repeater.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/widgets/wifi.h"
using qrcodegen::QrCode;
PairingQRWidget::PairingQRWidget(QWidget* parent) : QWidget(parent) {
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &PairingQRWidget::refresh);
}
void PairingQRWidget::showEvent(QShowEvent *event) {
refresh();
timer->start(5 * 60 * 1000);
device()->setOffroadBrightness(100);
}
void PairingQRWidget::hideEvent(QHideEvent *event) {
timer->stop();
device()->setOffroadBrightness(BACKLIGHT_OFFROAD);
}
void PairingQRWidget::refresh() {
QString pairToken = CommaApi::create_jwt({{"pair", true}});
QString qrString = "https://connect.comma.ai/?pair=" + pairToken;
this->updateQrCode(qrString);
update();
}
void PairingQRWidget::updateQrCode(const QString &text) {
QrCode qr = QrCode::encodeText(text.toUtf8().data(), QrCode::Ecc::LOW);
qint32 sz = qr.getSize();
QImage im(sz, sz, QImage::Format_RGB32);
QRgb black = qRgb(0, 0, 0);
QRgb white = qRgb(255, 255, 255);
for (int y = 0; y < sz; y++) {
for (int x = 0; x < sz; x++) {
im.setPixel(x, y, qr.getModule(x, y) ? black : white);
}
}
// Integer division to prevent anti-aliasing
int final_sz = ((width() / sz) - 1) * sz;
img = QPixmap::fromImage(im.scaled(final_sz, final_sz, Qt::KeepAspectRatio), Qt::MonoOnly);
}
void PairingQRWidget::paintEvent(QPaintEvent *e) {
QPainter p(this);
p.fillRect(rect(), Qt::white);
QSize s = (size() - img.size()) / 2;
p.drawPixmap(s.width(), s.height(), img);
}
PairingPopup::PairingPopup(QWidget *parent) : DialogBase(parent) {
QHBoxLayout *hlayout = new QHBoxLayout(this);
hlayout->setContentsMargins(0, 0, 0, 0);
hlayout->setSpacing(0);
setStyleSheet("PairingPopup { background-color: #E0E0E0; }");
// text
QVBoxLayout *vlayout = new QVBoxLayout();
vlayout->setContentsMargins(85, 70, 50, 70);
vlayout->setSpacing(50);
hlayout->addLayout(vlayout, 1);
{
QPushButton *close = new QPushButton(QIcon(":/icons/close.svg"), "", this);
close->setIconSize(QSize(80, 80));
close->setStyleSheet("border: none;");
vlayout->addWidget(close, 0, Qt::AlignLeft);
QObject::connect(close, &QPushButton::clicked, this, &QDialog::reject);
vlayout->addSpacing(30);
QLabel *title = new QLabel(tr("Pair your device to your comma account"), this);
title->setStyleSheet("font-size: 75px; color: black;");
title->setWordWrap(true);
vlayout->addWidget(title);
QLabel *instructions = new QLabel(QString(R"(
<ol type='1' style='margin-left: 15px;'>
<li style='margin-bottom: 50px;'>%1</li>
<li style='margin-bottom: 50px;'>%2</li>
<li style='margin-bottom: 50px;'>%3</li>
</ol>
)").arg(tr("Go to https://connect.comma.ai on your phone"))
.arg(tr("Click \"add new device\" and scan the QR code on the right"))
.arg(tr("Bookmark connect.comma.ai to your home screen to use it like an app")), this);
instructions->setStyleSheet("font-size: 47px; font-weight: bold; color: black;");
instructions->setWordWrap(true);
vlayout->addWidget(instructions);
vlayout->addStretch();
}
// QR code
PairingQRWidget *qr = new PairingQRWidget(this);
hlayout->addWidget(qr, 1);
}
PrimeUserWidget::PrimeUserWidget(QWidget *parent) : QFrame(parent) {
setObjectName("primeWidget");
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(56, 40, 56, 40);
mainLayout->setSpacing(20);
QLabel *subscribed = new QLabel(tr("✓ SUBSCRIBED"));
subscribed->setStyleSheet("font-size: 41px; font-weight: bold; color: #86FF4E;");
mainLayout->addWidget(subscribed);
QLabel *commaPrime = new QLabel(tr("comma prime"));
commaPrime->setStyleSheet("font-size: 75px; font-weight: bold;");
mainLayout->addWidget(commaPrime);
}
PrimeAdWidget::PrimeAdWidget(QWidget* parent) : QFrame(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(80, 90, 80, 60);
main_layout->setSpacing(0);
QLabel *upgrade = new QLabel(tr("Upgrade Now"));
upgrade->setStyleSheet("font-size: 75px; font-weight: bold;");
main_layout->addWidget(upgrade, 0, Qt::AlignTop);
main_layout->addSpacing(50);
QLabel *description = new QLabel(tr("Become a comma prime member at connect.comma.ai"));
description->setStyleSheet("font-size: 56px; font-weight: light; color: white;");
description->setWordWrap(true);
main_layout->addWidget(description, 0, Qt::AlignTop);
main_layout->addStretch();
QLabel *features = new QLabel(tr("PRIME FEATURES:"));
features->setStyleSheet("font-size: 41px; font-weight: bold; color: #E5E5E5;");
main_layout->addWidget(features, 0, Qt::AlignBottom);
main_layout->addSpacing(30);
QVector<QString> bullets = {tr("Remote access"), tr("24/7 LTE connectivity"), tr("1 year of drive storage"), tr("Turn-by-turn navigation")};
for (auto &b : bullets) {
const QString check = "<b><font color='#465BEA'>✓</font></b> ";
QLabel *l = new QLabel(check + b);
l->setAlignment(Qt::AlignLeft);
l->setStyleSheet("font-size: 50px; margin-bottom: 15px;");
main_layout->addWidget(l, 0, Qt::AlignBottom);
}
setStyleSheet(R"(
PrimeAdWidget {
border-radius: 10px;
background-color: #333333;
}
)");
}
SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
mainLayout = new QStackedWidget;
// Unpaired, registration prompt layout
QFrame* finishRegistration = new QFrame;
finishRegistration->setObjectName("primeWidget");
QVBoxLayout* finishRegistationLayout = new QVBoxLayout(finishRegistration);
finishRegistationLayout->setSpacing(38);
finishRegistationLayout->setContentsMargins(64, 48, 64, 48);
QLabel* registrationTitle = new QLabel(tr("Finish Setup"));
registrationTitle->setStyleSheet("font-size: 75px; font-weight: bold;");
finishRegistationLayout->addWidget(registrationTitle);
QLabel* registrationDescription = new QLabel(tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
registrationDescription->setWordWrap(true);
registrationDescription->setStyleSheet("font-size: 50px; font-weight: light;");
finishRegistationLayout->addWidget(registrationDescription);
finishRegistationLayout->addStretch();
QPushButton* pair = new QPushButton(tr("Pair device"));
pair->setStyleSheet(R"(
QPushButton {
font-size: 55px;
font-weight: 500;
border-radius: 10px;
background-color: #465BEA;
padding: 64px;
}
QPushButton:pressed {
background-color: #3049F4;
}
)");
finishRegistationLayout->addWidget(pair);
popup = new PairingPopup(this);
QObject::connect(pair, &QPushButton::clicked, popup, &PairingPopup::exec);
mainLayout->addWidget(finishRegistration);
// build stacked layout
QVBoxLayout *outer_layout = new QVBoxLayout(this);
outer_layout->setContentsMargins(0, 0, 0, 0);
outer_layout->addWidget(mainLayout);
QWidget *content = new QWidget;
QVBoxLayout *content_layout = new QVBoxLayout(content);
content_layout->setContentsMargins(0, 0, 0, 0);
content_layout->setSpacing(30);
primeUser = new PrimeUserWidget;
content_layout->addWidget(primeUser);
WiFiPromptWidget *wifi_prompt = new WiFiPromptWidget;
QObject::connect(wifi_prompt, &WiFiPromptWidget::openSettings, this, &SetupWidget::openSettings);
content_layout->addWidget(wifi_prompt);
content_layout->addStretch();
mainLayout->addWidget(content);
primeUser->setVisible(uiState()->primeType());
mainLayout->setCurrentIndex(1);
setStyleSheet(R"(
#primeWidget {
border-radius: 10px;
background-color: #333333;
}
)");
// Retain size while hidden
QSizePolicy sp_retain = sizePolicy();
sp_retain.setRetainSizeWhenHidden(true);
setSizePolicy(sp_retain);
// set up API requests
if (auto dongleId = getDongleId()) {
QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/";
RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_Device", 5);
QObject::connect(repeater, &RequestRepeater::requestDone, this, &SetupWidget::replyFinished);
}
}
void SetupWidget::replyFinished(const QString &response, bool success) {
if (!success) return;
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (doc.isNull()) {
qDebug() << "JSON Parse failed on getting pairing and prime status";
return;
}
QJsonObject json = doc.object();
PrimeType prime_type = static_cast<PrimeType>(json["prime_type"].toInt());
uiState()->setPrimeType(prime_type);
if (!json["is_paired"].toBool()) {
mainLayout->setCurrentIndex(0);
} else {
popup->reject();
primeUser->setVisible(prime_type);
mainLayout->setCurrentIndex(1);
}
}

View File

@@ -1,73 +0,0 @@
#pragma once
#include <QLabel>
#include <QStackedWidget>
#include <QVBoxLayout>
#include <QWidget>
#include "selfdrive/ui/qt/widgets/input.h"
// pairing QR code
class PairingQRWidget : public QWidget {
Q_OBJECT
public:
explicit PairingQRWidget(QWidget* parent = 0);
void paintEvent(QPaintEvent*) override;
private:
QPixmap img;
QTimer *timer;
void updateQrCode(const QString &text);
void showEvent(QShowEvent *event) override;
void hideEvent(QHideEvent *event) override;
private slots:
void refresh();
};
// pairing popup widget
class PairingPopup : public DialogBase {
Q_OBJECT
public:
explicit PairingPopup(QWidget* parent);
};
// widget for paired users with prime
class PrimeUserWidget : public QFrame {
Q_OBJECT
public:
explicit PrimeUserWidget(QWidget* parent = 0);
};
// widget for paired users without prime
class PrimeAdWidget : public QFrame {
Q_OBJECT
public:
explicit PrimeAdWidget(QWidget* parent = 0);
};
// container widget
class SetupWidget : public QFrame {
Q_OBJECT
public:
explicit SetupWidget(QWidget* parent = 0);
signals:
void openSettings(int index = 0, const QString &param = "");
private:
PairingPopup *popup;
QStackedWidget *mainLayout;
PrimeUserWidget *primeUser;
private slots:
void replyFinished(const QString &response, bool success);
};

View File

@@ -74,12 +74,7 @@ void MainWindow::closeSettings() {
main_layout->setCurrentWidget(homeWindow);
if (uiState()->scene.started) {
// Map is always shown when using navigate on openpilot
if (uiState()->scene.navigate_on_openpilot) {
homeWindow->showMapPanel(true);
} else {
homeWindow->showSidebar(params.getBool("Sidebar"));
}
homeWindow->showSidebar(params.getBool("Sidebar"));
}
}

View File

@@ -96,15 +96,9 @@ def setup_onroad(click, pm: PubMaster):
server.send(cam_meta.stream, IMG_BYTES, cs.frameId, cs.timestampSof, cs.timestampEof)
@mock_messages(['liveLocationKalman'])
def setup_onroad_map(click, pm: PubMaster):
setup_onroad(click, pm)
click(500, 500)
time.sleep(UI_DELAY) # give time for the map to render
def setup_onroad_sidebar(click, pm: PubMaster):
setup_onroad_map(click, pm)
click(500, 500)
CASES = {
@@ -112,7 +106,6 @@ CASES = {
"settings_device": setup_settings_device,
"settings_network": setup_settings_network,
"onroad": setup_onroad,
"onroad_map": setup_onroad_map,
"onroad_sidebar": setup_onroad_sidebar
}

View File

@@ -305,7 +305,6 @@ static void update_state(UIState *s) {
void ui_update_params(UIState *s) {
auto params = Params();
s->scene.is_metric = params.getBool("IsMetric");
s->scene.map_on_left = params.getBool("NavSettingLeftSide");
}
void ui_update_frogpilot_params(UIState *s) {
@@ -373,13 +372,10 @@ void ui_update_frogpilot_params(UIState *s) {
scene.reverse_cruise_ui = scene.reverse_cruise && params.getBool("ReverseCruiseUI");
bool quality_of_life_visuals = params.getBool("QOLVisuals");
scene.big_map = quality_of_life_visuals && params.getBool("BigMap");
scene.full_map = scene.big_map && params.getBool("FullMap");
scene.camera_view = quality_of_life_visuals ? params.getInt("CameraView") : 0;
scene.driver_camera = quality_of_life_visuals && params.getBool("DriverCamera");
scene.hide_speed = quality_of_life_visuals && params.getBool("HideSpeed");
scene.hide_speed_ui = scene.hide_speed && params.getBool("HideSpeedUI");
scene.map_style = quality_of_life_visuals ? params.getInt("MapStyle") : 0;
scene.numerical_temp = quality_of_life_visuals && params.getBool("NumericalTemp");
scene.fahrenheit = scene.numerical_temp && params.getBool("Fahrenheit");
scene.wheel_speed = quality_of_life_visuals && params.getBool("WheelSpeed");
@@ -387,7 +383,6 @@ void ui_update_frogpilot_params(UIState *s) {
bool screen_management = params.getBool("ScreenManagement");
bool hide_ui_elements = screen_management && params.getBool("HideUIElements");
scene.hide_alerts = hide_ui_elements && params.getBool("HideAlerts");
scene.hide_map_icon = hide_ui_elements && params.getBool("HideMapIcon");
scene.hide_max_speed = hide_ui_elements && params.getBool("HideMaxSpeed");
scene.screen_brightness = screen_management ? params.getInt("ScreenBrightness") : 101;
scene.screen_brightness_onroad = screen_management ? params.getInt("ScreenBrightnessOnroad") : 101;
@@ -437,16 +432,12 @@ UIState::UIState(QObject *parent) : QObject(parent) {
sm = std::make_unique<SubMaster, const std::initializer_list<const char *>>({
"modelV2", "controlsState", "liveCalibration", "radarState", "deviceState",
"pandaStates", "carParams", "driverMonitoringState", "carState", "liveLocationKalman", "driverStateV2",
"wideRoadCameraState", "managerState", "navInstruction", "navRoute", "uiPlan", "liveTorqueParameters",
"wideRoadCameraState", "managerState", "uiPlan", "liveTorqueParameters",
"frogpilotCarControl", "frogpilotDeviceState", "frogpilotPlan",
});
Params params;
language = QString::fromStdString(params.get("LanguageSetting"));
auto prime_value = params.get("PrimeType");
if (!prime_value.empty()) {
prime_type = static_cast<PrimeType>(std::atoi(prime_value.c_str()));
}
// update timer
timer = new QTimer(this);
@@ -480,21 +471,6 @@ void UIState::update() {
scene.driver_camera_timer = (scene.driver_camera && scene.reverse) ? scene.driver_camera_timer + 1 : 0;
}
void UIState::setPrimeType(PrimeType type) {
if (type != prime_type) {
bool prev_prime = hasPrime();
prime_type = type;
Params().put("PrimeType", std::to_string(prime_type));
emit primeTypeChanged(prime_type);
bool prime = hasPrime();
if (prev_prime != prime) {
emit primeChanged(prime);
}
}
}
Device::Device(QObject *parent) : brightness_filter(BACKLIGHT_OFFROAD, BACKLIGHT_TS, BACKLIGHT_DT), QObject(parent) {
setAwake(true);
resetInteractiveTimeout();

View File

@@ -155,16 +155,6 @@ struct Alert {
}
};
enum PrimeType {
UNKNOWN = -1,
NONE = 0,
MAGENTA = 1,
LITE = 2,
BLUE = 3,
MAGENTA_NEW = 4,
PURPLE = 5,
};
typedef struct UIScene {
bool calibration_valid = false;
@@ -191,7 +181,6 @@ typedef struct UIScene {
float driver_pose_coss[3];
vec3 face_kpts_draw[std::size(default_face_kpts_3d)];
bool navigate_on_openpilot = false;
cereal::LongitudinalPersonality personality;
float light_sensor;
@@ -204,7 +193,6 @@ typedef struct UIScene {
bool adjacent_path;
bool adjacent_path_metrics;
bool always_on_lateral_active;
bool big_map;
bool blind_spot_left;
bool blind_spot_path;
bool blind_spot_right;
@@ -219,7 +207,6 @@ typedef struct UIScene {
bool experimental_mode_via_screen;
bool fahrenheit;
bool fps_counter;
bool full_map;
bool has_auto_tune;
bool hide_alerts;
bool hide_lead_marker;
@@ -230,7 +217,6 @@ typedef struct UIScene {
bool holiday_themes;
bool lead_info;
bool live_valid;
bool map_open;
bool model_ui;
bool numerical_temp;
bool online;
@@ -328,10 +314,6 @@ public:
return scene.started && (*sm)["controlsState"].getControlsState().getEnabled();
}
void setPrimeType(PrimeType type);
inline PrimeType primeType() const { return prime_type; }
inline bool hasPrime() const { return prime_type != PrimeType::UNKNOWN && prime_type != PrimeType::NONE; }
int fb_w = 0, fb_h = 0;
std::unique_ptr<SubMaster> sm;
@@ -349,8 +331,6 @@ public:
signals:
void uiUpdate(const UIState &s);
void offroadTransition(bool offroad);
void primeChanged(bool prime);
void primeTypeChanged(PrimeType prime_type);
private slots:
void update();
@@ -358,7 +338,6 @@ private slots:
private:
QTimer *timer;
bool started_prev = false;
PrimeType prime_type = PrimeType::UNKNOWN;
// FrogPilot variables
Params paramsMemory{"/dev/shm/params"};

View File

@@ -19,7 +19,6 @@ int main(int argc, char *argv[]) {
{
QHBoxLayout *hlayout = new QHBoxLayout();
layout->addLayout(hlayout);
hlayout->addWidget(new CameraWidget("navd", VISION_STREAM_MAP, false));
hlayout->addWidget(new CameraWidget("camerad", VISION_STREAM_ROAD, false));
}