diff --git a/common/params.cc b/common/params.cc index db2d44f..9069650 100644 --- a/common/params.cc +++ b/common/params.cc @@ -220,6 +220,8 @@ std::unordered_map keys = { {"AggressiveJerk", PERSISTENT}, {"AlwaysOnLateral", PERSISTENT}, {"AlwaysOnLateralMain", PERSISTENT}, + {"AMapKey1", PERSISTENT}, + {"AMapKey2", PERSISTENT}, {"ApiCache_DriveStats", PERSISTENT}, {"AverageCurvature", PERSISTENT}, {"BlindSpotPath", PERSISTENT}, @@ -252,6 +254,7 @@ std::unordered_map keys = { {"FireTheBabysitter", PERSISTENT}, {"FrogPilotTogglesUpdated", PERSISTENT}, {"GasRegenCmd", PERSISTENT}, + {"GMapKey", PERSISTENT}, {"GoatScream", PERSISTENT}, {"GreenLightAlert", PERSISTENT}, {"HideSpeed", PERSISTENT}, @@ -262,6 +265,8 @@ std::unordered_map keys = { {"LongitudinalTune", PERSISTENT}, {"LongPitch", PERSISTENT}, {"LowerVolt", PERSISTENT}, + {"MapboxPublicKey", PERSISTENT}, + {"MapboxSecretKey", PERSISTENT}, {"MapTargetVelocities", PERSISTENT}, {"Model", PERSISTENT}, {"ModelList", PERSISTENT}, @@ -272,6 +277,7 @@ std::unordered_map keys = { {"MuteDoor", PERSISTENT}, {"MuteOverheated", PERSISTENT}, {"MuteSeatbelt", PERSISTENT}, + {"NavEnable", PERSISTENT}, {"NoLogging", PERSISTENT}, {"OfflineMode", PERSISTENT}, {"PathEdgeWidth", PERSISTENT}, @@ -281,6 +287,7 @@ std::unordered_map keys = { {"ReverseCruise", PERSISTENT}, {"RoadEdgesWidth", PERSISTENT}, {"ScreenBrightness", PERSISTENT}, + {"SearchInput", PERSISTENT}, {"ShowCPU", PERSISTENT}, {"ShowFPS", PERSISTENT}, {"ShowGPU", PERSISTENT}, diff --git a/selfdrive/frogpilot/navigation/navigation_training/both_keys_set.png b/selfdrive/frogpilot/navigation/navigation_training/both_keys_set.png new file mode 100644 index 0000000..41cf320 Binary files /dev/null and b/selfdrive/frogpilot/navigation/navigation_training/both_keys_set.png differ diff --git a/selfdrive/frogpilot/navigation/navigation_training/no_keys_set.png b/selfdrive/frogpilot/navigation/navigation_training/no_keys_set.png new file mode 100644 index 0000000..7fc89a8 Binary files /dev/null and b/selfdrive/frogpilot/navigation/navigation_training/no_keys_set.png differ diff --git a/selfdrive/frogpilot/navigation/navigation_training/public_key_set.png b/selfdrive/frogpilot/navigation/navigation_training/public_key_set.png new file mode 100644 index 0000000..ac3e6d1 Binary files /dev/null and b/selfdrive/frogpilot/navigation/navigation_training/public_key_set.png differ diff --git a/selfdrive/frogpilot/navigation/navigation_training/setup_completed.png b/selfdrive/frogpilot/navigation/navigation_training/setup_completed.png new file mode 100644 index 0000000..afd64fb Binary files /dev/null and b/selfdrive/frogpilot/navigation/navigation_training/setup_completed.png differ diff --git a/selfdrive/frogpilot/navigation/ui/navigation_settings.cc b/selfdrive/frogpilot/navigation/ui/navigation_settings.cc new file mode 100644 index 0000000..01f4d3b --- /dev/null +++ b/selfdrive/frogpilot/navigation/ui/navigation_settings.cc @@ -0,0 +1,156 @@ +#include + +#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 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()); +} + +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 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("Mapbox 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 ¶mKey, 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)); +} diff --git a/selfdrive/frogpilot/navigation/ui/navigation_settings.h b/selfdrive/frogpilot/navigation/ui/navigation_settings.h new file mode 100644 index 0000000..2bf9ede --- /dev/null +++ b/selfdrive/frogpilot/navigation/ui/navigation_settings.h @@ -0,0 +1,60 @@ +#pragma once + +#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 ¶mKey, 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 FrogPilotNavigationPanel : public QFrame { + Q_OBJECT + +public: + explicit FrogPilotNavigationPanel(QWidget *parent = 0); + +private: + QStackedLayout *mainLayout; + QWidget *navigationWidget; + + Params params; + Params paramsMemory{"/dev/shm/params"}; + UIScene &scene; +}; diff --git a/selfdrive/navd/navd.py b/selfdrive/navd/navd.py index 7688580..83e9b81 100755 --- a/selfdrive/navd/navd.py +++ b/selfdrive/navd/navd.py @@ -53,6 +53,9 @@ class RouteEngine: if "MAPBOX_TOKEN" in os.environ: self.mapbox_token = os.environ["MAPBOX_TOKEN"] self.mapbox_host = "https://api.mapbox.com" + elif self.params.get_int("PrimeType") == 0: + self.mapbox_token = self.params.get("MapboxPublicKey", encoding='utf8') + self.mapbox_host = "https://api.mapbox.com" else: try: self.mapbox_token = Api(self.params.get("DongleId", encoding='utf8')).get_token(expiry_hours=4 * 7 * 24) diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index c68a581..ca90dbe 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -25,7 +25,7 @@ widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/ "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.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/frogpilot_functions.cc", + "../frogpilot/ui/frogpilot_functions.cc", "../frogpilot/navigation/ui/navigation_settings.cc", "../frogpilot/ui/control_settings.cc", "../frogpilot/ui/vehicle_settings.cc", "../frogpilot/ui/visual_settings.cc"] diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc index 64ad371..aff2e4b 100644 --- a/selfdrive/ui/qt/maps/map.cc +++ b/selfdrive/ui/qt/maps/map.cc @@ -112,6 +112,50 @@ void MapWindow::initLayers() { // 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(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) { @@ -124,7 +168,8 @@ void MapWindow::updateState(const UIState &s) { 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()); + (sm["controlsState"].getControlsState().getEnabled() || sm["frogpilotCarControl"].getFrogpilotCarControl().getAlwaysOnLateral()) && + (!params.get("NavDestination").empty() || params.getInt("PrimeType") != 0); if (nav_enabled != uiState()->scene.navigate_on_openpilot) { if (loaded_once) { m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(nav_enabled)); diff --git a/selfdrive/ui/qt/maps/map_helpers.h b/selfdrive/ui/qt/maps/map_helpers.h index 3d045cb..04e7686 100644 --- a/selfdrive/ui/qt/maps/map_helpers.h +++ b/selfdrive/ui/qt/maps/map_helpers.h @@ -13,7 +13,7 @@ #include "common/transformations/orientation.hpp" #include "cereal/messaging/messaging.h" -const QString MAPBOX_TOKEN = util::getenv("MAPBOX_TOKEN").c_str(); +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"; diff --git a/selfdrive/ui/qt/maps/map_settings.cc b/selfdrive/ui/qt/maps/map_settings.cc index 4d655be..69a1406 100644 --- a/selfdrive/ui/qt/maps/map_settings.cc +++ b/selfdrive/ui/qt/maps/map_settings.cc @@ -62,7 +62,13 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) { title->setStyleSheet("color: #FFFFFF; font-size: 54px; font-weight: 600;"); heading->addWidget(title); - auto *subtitle = new QLabel(tr("Manage at connect.comma.ai"), this); + // NOO without Prime IP extraction + if (notPrime) { + ipAddress = QString("%1:8082").arg(wifi->getIp4Address()); + subtitle = new QLabel(tr("Manage at %1").arg(ipAddress), this); + } else { + subtitle = new QLabel(tr("Manage at connect.comma.ai"), this); + } subtitle->setStyleSheet("color: #A0A0A0; font-size: 40px; font-weight: 300;"); heading->addWidget(subtitle); } @@ -138,6 +144,12 @@ void MapSettings::refresh() { for (; n < widgets.size(); ++n) widgets[n]->setVisible(false); setUpdatesEnabled(true); + + // NOO without Prime IP update + if (notPrime) { + ipAddress = QString("%1:8082").arg(wifi->getIp4Address()); + subtitle->setText(tr("Manage at %1").arg(ipAddress)); + } } void MapSettings::navigateTo(const QJsonObject &place) { diff --git a/selfdrive/ui/qt/maps/map_settings.h b/selfdrive/ui/qt/maps/map_settings.h index 0e151df..6a87a4d 100644 --- a/selfdrive/ui/qt/maps/map_settings.h +++ b/selfdrive/ui/qt/maps/map_settings.h @@ -12,6 +12,7 @@ #include #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" @@ -64,6 +65,12 @@ private: DestinationWidget *work_widget; std::vector widgets; + // FrogPilot variables + bool notPrime = Params().getInt("PrimeType") == 0; + QLabel *subtitle; + QString ipAddress; + WifiManager *wifi = new WifiManager(this); + signals: void closeSettings(); }; diff --git a/selfdrive/ui/qt/network/wifi_manager.h b/selfdrive/ui/qt/network/wifi_manager.h index 7debffa..51d1175 100644 --- a/selfdrive/ui/qt/network/wifi_manager.h +++ b/selfdrive/ui/qt/network/wifi_manager.h @@ -58,6 +58,7 @@ public: void setTetheringEnabled(bool enabled); bool isTetheringEnabled(); void changeTetheringPassword(const QString &newPassword); + QString getIp4Address(); QString getTetheringPassword(); private: @@ -72,7 +73,6 @@ private: QString getAdapter(const uint = NM_DEVICE_TYPE_WIFI); uint getAdapterType(const QDBusObjectPath &path); - QString getIp4Address(); void deactivateConnectionBySsid(const QString &ssid); void deactivateConnection(const QDBusObjectPath &path); QVector getActiveConnections(); diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index a630054..a39451f 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -24,6 +24,7 @@ #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/control_settings.h" #include "selfdrive/frogpilot/ui/vehicle_settings.h" #include "selfdrive/frogpilot/ui/visual_settings.h" @@ -421,6 +422,7 @@ 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}, };