Open Street Maps integration
Added OSM to openpilot to use speed limits and road names. Credit goes to Pfeiferj! https: //github.com/pfeiferj Co-Authored-By: Jacob Pfeifer <jacob@pfeifer.dev>
This commit is contained in:
@@ -288,6 +288,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"LaneDetection", PERSISTENT},
|
||||
{"LaneDetectionWidth", PERSISTENT},
|
||||
{"LaneLinesWidth", PERSISTENT},
|
||||
{"LastMapsUpdate", PERSISTENT},
|
||||
{"LateralTune", PERSISTENT},
|
||||
{"LeadDepartingAlert", PERSISTENT},
|
||||
{"LeadInfo", PERSISTENT},
|
||||
@@ -299,6 +300,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"ManualUpdateInitiated", CLEAR_ON_MANAGER_START},
|
||||
{"MapboxPublicKey", PERSISTENT},
|
||||
{"MapboxSecretKey", PERSISTENT},
|
||||
{"MapsSelected", PERSISTENT},
|
||||
{"MapTargetLatA", PERSISTENT},
|
||||
{"MapTargetVelocities", PERSISTENT},
|
||||
{"Model", PERSISTENT},
|
||||
@@ -319,8 +321,11 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"NumericalTemp", PERSISTENT},
|
||||
{"OfflineMode", PERSISTENT},
|
||||
{"OneLaneChange", PERSISTENT},
|
||||
{"OSMDownloadLocations", PERSISTENT},
|
||||
{"OSMDownloadProgress", CLEAR_ON_MANAGER_START},
|
||||
{"PathEdgeWidth", PERSISTENT},
|
||||
{"PathWidth", PERSISTENT},
|
||||
{"PreferredSchedule", PERSISTENT},
|
||||
{"PromptVolume", PERSISTENT},
|
||||
{"PromptDistractedVolume", PERSISTENT},
|
||||
{"QOLControls", PERSISTENT},
|
||||
@@ -331,6 +336,9 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"ReverseCruise", PERSISTENT},
|
||||
{"ReverseCruiseUI", PERSISTENT},
|
||||
{"RoadEdgesWidth", PERSISTENT},
|
||||
{"RoadName", PERSISTENT},
|
||||
{"RoadNameUI", PERSISTENT},
|
||||
{"SchedulePending", PERSISTENT},
|
||||
{"SearchInput", PERSISTENT},
|
||||
{"SilentMode", PERSISTENT},
|
||||
{"ShowCPU", PERSISTENT},
|
||||
|
||||
56
selfdrive/frogpilot/functions/mapd.py
Normal file
56
selfdrive/frogpilot/functions/mapd.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# PFEIFER - MAPD
|
||||
import os
|
||||
import subprocess
|
||||
import urllib.request
|
||||
from openpilot.common.realtime import Ratekeeper
|
||||
import stat
|
||||
|
||||
VERSION = 'v1.8.1'
|
||||
URL = f"https://github.com/pfeiferj/openpilot-mapd/releases/download/{VERSION}/mapd"
|
||||
MAPD_PATH = '/data/media/0/osm/mapd'
|
||||
VERSION_PATH = '/data/media/0/osm/mapd_version'
|
||||
|
||||
def download():
|
||||
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(VERSION)
|
||||
os.fsync(output)
|
||||
|
||||
def mapd_thread(sm=None, pm=None):
|
||||
rk = Ratekeeper(0.05, print_delay_threshold=None)
|
||||
|
||||
while True:
|
||||
try:
|
||||
if not os.path.exists(MAPD_PATH):
|
||||
download()
|
||||
continue
|
||||
if not os.path.exists(VERSION_PATH):
|
||||
download()
|
||||
continue
|
||||
with open(VERSION_PATH) as f:
|
||||
content = f.read()
|
||||
if content != VERSION:
|
||||
download()
|
||||
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()
|
||||
311
selfdrive/frogpilot/navigation/ui/navigation_functions.h
Normal file
311
selfdrive/frogpilot/navigation/ui/navigation_functions.h
Normal file
@@ -0,0 +1,311 @@
|
||||
#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";
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#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) {
|
||||
@@ -19,6 +20,423 @@ FrogPilotNavigationPanel::FrogPilotNavigationPanel(QWidget *parent) : QFrame(par
|
||||
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(¤tTime);
|
||||
|
||||
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("Are you sure you want to cancel the download?", parent)) {
|
||||
std::thread([&] {
|
||||
std::system("pkill mapd");
|
||||
}).detach();
|
||||
if (FrogPilotConfirmationDialog::toggle("Reboot required to re-enable map downloads", "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("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) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "selfdrive/frogpilot/ui/frogpilot_ui_functions.h"
|
||||
#include "selfdrive/ui/qt/network/wifi_manager.h"
|
||||
#include "selfdrive/ui/qt/offroad/settings.h"
|
||||
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
||||
@@ -44,6 +45,33 @@ 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
|
||||
|
||||
@@ -51,9 +79,42 @@ 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;
|
||||
|
||||
@@ -32,6 +32,7 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(SettingsWindow *parent) : FrogPilot
|
||||
{"BlindSpotPath", "Blind Spot Path", "Visualize your blind spots with a red path when another vehicle is detected nearby.", ""},
|
||||
{"FPSCounter", "FPS Counter", "Display the Frames Per Second (FPS) of your onroad UI for monitoring system performance.", ""},
|
||||
{"LeadInfo", "Lead Info and Logics", "Get detailed information about the vehicle ahead, including speed and distance, and the logic behind your following distance.", ""},
|
||||
{"RoadNameUI", "Road Name", "See the name of the road you're on at the bottom of your screen. Sourced from OpenStreetMap.", ""},
|
||||
|
||||
{"DriverCamera", "Driver Camera On Reverse", "Show the driver's camera feed when you shift to reverse.", "../assets/img_driver_face_static.png"},
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ private:
|
||||
|
||||
std::set<QString> alertVolumeControlKeys = {"EngageVolume", "DisengageVolume", "RefuseVolume", "PromptVolume", "PromptDistractedVolume", "WarningSoftVolume", "WarningImmediateVolume"};
|
||||
std::set<QString> customAlertsKeys = {"GreenLightAlert", "LeadDepartingAlert", "LoudBlindspotAlert"};
|
||||
std::set<QString> customOnroadUIKeys = {"AccelerationPath", "AdjacentPath", "BlindSpotPath", "FPSCounter", "LeadInfo"};
|
||||
std::set<QString> customOnroadUIKeys = {"AccelerationPath", "AdjacentPath", "BlindSpotPath", "FPSCounter", "LeadInfo", "RoadNameUI"};
|
||||
std::set<QString> customThemeKeys = {"HolidayThemes", "CustomColors", "CustomIcons", "CustomSignals", "CustomSounds"};
|
||||
std::set<QString> modelUIKeys = {"DynamicPathWidth", "LaneLinesWidth", "PathEdgeWidth", "PathWidth", "RoadEdgesWidth", "UnlimitedLength"};
|
||||
std::set<QString> qolKeys = {"DriveStats", "FullMap", "HideSpeed"};
|
||||
|
||||
@@ -14,6 +14,7 @@ from openpilot.selfdrive.locationd.models.car_kf import CarKalman, ObservationKi
|
||||
from openpilot.selfdrive.locationd.models.constants import GENERATED_DIR
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
params_memory = Params("/dev/shm/params")
|
||||
|
||||
MAX_ANGLE_OFFSET_DELTA = 20 * DT_MDL # Max 20 deg/s
|
||||
ROLL_MAX_DELTA = math.radians(20.0) * DT_MDL # 20deg in 1 second is well within curvature limits
|
||||
@@ -191,6 +192,12 @@ def main():
|
||||
learner.handle_log(t, which, sm[which])
|
||||
|
||||
if sm.updated['liveLocationKalman']:
|
||||
location = sm['liveLocationKalman']
|
||||
if (location.status == log.LiveLocationKalman.Status.valid) and location.positionGeodetic.valid and location.gpsOK:
|
||||
bearing = math.degrees(location.calibratedOrientationNED.value[2])
|
||||
lat = location.positionGeodetic.value[0]
|
||||
lon = location.positionGeodetic.value[1]
|
||||
params_memory.put("LastGPSPosition", json.dumps({ "latitude": lat, "longitude": lon, "bearing": bearing }))
|
||||
x = learner.kf.x
|
||||
P = np.sqrt(learner.kf.P.diagonal())
|
||||
if not all(map(math.isfinite, x)):
|
||||
|
||||
@@ -347,7 +347,7 @@ def manager_cleanup() -> None:
|
||||
|
||||
|
||||
def update_frogpilot_params(params, params_memory):
|
||||
keys = ["DisableOnroadUploads", "FireTheBabysitter", "NoLogging", "NoUploads"]
|
||||
keys = ["DisableOnroadUploads", "FireTheBabysitter", "NoLogging", "NoUploads", "RoadNameUI"]
|
||||
for key in keys:
|
||||
params_memory.put_bool(key, params.get_bool(key))
|
||||
|
||||
|
||||
@@ -51,6 +51,9 @@ def allow_logging(started, params, params_memory, CP: car.CarParams) -> bool:
|
||||
allow_logging = not (params_memory.get_bool("FireTheBabysitter") and params_memory.get_bool("NoLogging"))
|
||||
return allow_logging and logging(started, params, params_memory, CP)
|
||||
|
||||
def osm(started, params, params_memory, CP: car.CarParams) -> bool:
|
||||
return params_memory.get_bool("RoadNameUI")
|
||||
|
||||
procs = [
|
||||
DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"),
|
||||
|
||||
@@ -100,6 +103,7 @@ procs = [
|
||||
# FrogPilot processes
|
||||
PythonProcess("fleet_manager", "selfdrive.frogpilot.fleetmanager.fleet_manager", always_run),
|
||||
PythonProcess("frogpilot_process", "selfdrive.frogpilot.functions.frogpilot_process", always_run),
|
||||
PythonProcess("mapd", "selfdrive.frogpilot.functions.mapd", osm),
|
||||
]
|
||||
|
||||
managed_processes = {p.name: p for p in procs}
|
||||
|
||||
@@ -283,7 +283,7 @@ void OnroadAlerts::paintEvent(QPaintEvent *event) {
|
||||
|
||||
int margin = 40;
|
||||
int radius = 30;
|
||||
int offset = scene.always_on_lateral || scene.conditional_experimental ? 25 : 0;
|
||||
int offset = scene.always_on_lateral || scene.conditional_experimental || scene.road_name_ui ? 25 : 0;
|
||||
if (alert.size == cereal::ControlsState::AlertSize::FULL) {
|
||||
margin = 0;
|
||||
radius = 0;
|
||||
@@ -827,7 +827,7 @@ void AnnotatedCameraWidget::drawDriverState(QPainter &painter, const UIState *s)
|
||||
|
||||
// base icon
|
||||
int offset = UI_BORDER_SIZE + btn_size / 2;
|
||||
offset += alwaysOnLateral || conditionalExperimental ? 25 : 0;
|
||||
offset += alwaysOnLateral || conditionalExperimental || roadNameUI ? 25 : 0;
|
||||
int x = rightHandDM ? width() - offset : offset;
|
||||
int y = height() - offset;
|
||||
float opacity = dmActive ? 0.65 : 0.2;
|
||||
@@ -1137,6 +1137,8 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets(QPainter &p) {
|
||||
mapOpen = scene.map_open;
|
||||
fullMapOpen = mapOpen && scene.full_map;
|
||||
|
||||
roadNameUI = scene.road_name_ui;
|
||||
|
||||
showDriverCamera = scene.show_driver_camera;
|
||||
|
||||
turnSignalLeft = scene.turn_signal_left;
|
||||
@@ -1147,7 +1149,7 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets(QPainter &p) {
|
||||
drawLeadInfo(p);
|
||||
}
|
||||
|
||||
if (alwaysOnLateral || conditionalExperimental) {
|
||||
if (alwaysOnLateral || conditionalExperimental || roadNameUI) {
|
||||
drawStatusBar(p);
|
||||
}
|
||||
|
||||
@@ -1484,6 +1486,8 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
|
||||
{15, "Experimental Mode activated for stop" + (mapOpen ? "" : QString(" sign / stop light"))},
|
||||
};
|
||||
|
||||
QString roadName = roadNameUI ? QString::fromStdString(paramsMemory.get("RoadName")) : QString();
|
||||
|
||||
// Update status text
|
||||
if (alwaysOnLateralActive) {
|
||||
newStatus = QString("Always On Lateral active") + (mapOpen ? "" : ". Press the \"Cruise Control\" button to disable");
|
||||
@@ -1506,8 +1510,8 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if status has changed
|
||||
if (newStatus != lastShownStatus) {
|
||||
// Check if status has changed or if the road name is empty
|
||||
if (newStatus != lastShownStatus || roadName.isEmpty()) {
|
||||
displayStatusText = true;
|
||||
lastShownStatus = newStatus;
|
||||
timer.restart();
|
||||
@@ -1521,10 +1525,15 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
|
||||
p.setRenderHint(QPainter::TextAntialiasing);
|
||||
|
||||
// Calculate text opacity
|
||||
static qreal roadNameOpacity;
|
||||
static qreal statusTextOpacity;
|
||||
int elapsed = timer.elapsed();
|
||||
if (displayStatusText) {
|
||||
statusTextOpacity = qBound(0.0, 1.0 - (elapsed - textDuration) / fadeDuration, 1.0);
|
||||
roadNameOpacity = 1.0 - statusTextOpacity;
|
||||
} else {
|
||||
roadNameOpacity = qBound(0.0, elapsed / fadeDuration, 1.0);
|
||||
statusTextOpacity = 0.0;
|
||||
}
|
||||
|
||||
// Draw the status text
|
||||
@@ -1533,6 +1542,14 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
|
||||
textRect.moveBottom(statusBarRect.bottom() - 50);
|
||||
p.drawText(textRect, Qt::AlignCenter | Qt::TextWordWrap, newStatus);
|
||||
|
||||
// Draw the road name with the calculated opacity
|
||||
if (!roadName.isEmpty()) {
|
||||
p.setOpacity(roadNameOpacity);
|
||||
textRect = p.fontMetrics().boundingRect(statusBarRect, Qt::AlignCenter | Qt::TextWordWrap, roadName);
|
||||
textRect.moveBottom(statusBarRect.bottom() - 50);
|
||||
p.drawText(textRect, Qt::AlignCenter | Qt::TextWordWrap, roadName);
|
||||
}
|
||||
|
||||
p.restore();
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,7 @@ private:
|
||||
bool fullMapOpen;
|
||||
bool leadInfo;
|
||||
bool mapOpen;
|
||||
bool roadNameUI;
|
||||
bool showDriverCamera;
|
||||
bool turnSignalLeft;
|
||||
bool turnSignalRight;
|
||||
|
||||
@@ -308,6 +308,7 @@ void ui_update_frogpilot_params(UIState *s) {
|
||||
scene.fps_counter = custom_onroad_ui && params.getBool("FPSCounter");
|
||||
scene.lead_info = custom_onroad_ui && params.getBool("LeadInfo");
|
||||
scene.use_si = scene.lead_info && params.getBool("UseSI");
|
||||
scene.road_name_ui = custom_onroad_ui && params.getBool("RoadNameUI");
|
||||
|
||||
bool custom_theme = params.getBool("CustomTheme");
|
||||
scene.custom_colors = custom_theme ? params.getInt("CustomColors") : 0;
|
||||
|
||||
@@ -197,6 +197,7 @@ typedef struct UIScene {
|
||||
bool numerical_temp;
|
||||
bool reverse_cruise;
|
||||
bool reverse_cruise_ui;
|
||||
bool road_name_ui;
|
||||
bool show_driver_camera;
|
||||
bool turn_signal_left;
|
||||
bool turn_signal_right;
|
||||
|
||||
Reference in New Issue
Block a user