diff --git a/common/params_keys.h b/common/params_keys.h index ba20acdaa..8456146aa 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -11,7 +11,6 @@ inline static std::unordered_map keys = { {"AlwaysAllowUploads", {PERSISTENT, BOOL, "0"}}, {"AlwaysOnDM", {PERSISTENT, BOOL}}, {"ApiCache_Device", {PERSISTENT, STRING}}, - {"ApiCache_FirehoseStats", {PERSISTENT, JSON}}, {"AssistNowToken", {PERSISTENT, STRING}}, {"AthenadPid", {PERSISTENT, INT}}, {"AthenadUploadQueue", {PERSISTENT, JSON}}, diff --git a/common/tests/test_params.py b/common/tests/test_params.py index 354997fed..4bacb8842 100644 --- a/common/tests/test_params.py +++ b/common/tests/test_params.py @@ -133,8 +133,8 @@ class TestParams: def test_params_get_type(self): # json - self.params.put("ApiCache_FirehoseStats", {"a": 0}) - assert self.params.get("ApiCache_FirehoseStats") == {"a": 0} + self.params.put("ApiCache_DriveStats", {"a": 0}) + assert self.params.get("ApiCache_DriveStats") == {"a": 0} # int self.params.put("BootCount", 1441) diff --git a/selfdrive/assets/images/StarPilotLogo.png b/selfdrive/assets/images/StarPilotLogo.png new file mode 100644 index 000000000..a0443b817 Binary files /dev/null and b/selfdrive/assets/images/StarPilotLogo.png differ diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index e51f6d161..3046f99e5 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -23,7 +23,7 @@ if arch == "Darwin": 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 = ["qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/prime_state.cc", +widgets_src = ["qt/widgets/input.cc", "qt/prime_state.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/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", @@ -39,7 +39,7 @@ qt_libs = [widgets, qt_util] + base_libs qt_src = ["main.cc", "ui.cc", "qt/sidebar.cc", "qt/body.cc", "qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", "qt/offroad/software_settings.cc", "qt/offroad/developer_panel.cc", "qt/offroad/onboarding.cc", - "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/offroad/firehose.cc", + "qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", "qt/onroad/model.cc", "qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"] diff --git a/selfdrive/ui/layouts/main.py b/selfdrive/ui/layouts/main.py index 4762c8c1f..f15ed3056 100644 --- a/selfdrive/ui/layouts/main.py +++ b/selfdrive/ui/layouts/main.py @@ -54,7 +54,6 @@ class MainLayout(Widget): self._sidebar.set_callbacks(on_settings=self._on_settings_clicked, on_flag=self._on_bookmark_clicked, open_settings=lambda: self.open_settings(PanelType.TOGGLES)) - self._layouts[MainState.HOME]._setup_widget.set_open_settings_callback(lambda: self.open_settings(PanelType.FIREHOSE)) self._layouts[MainState.HOME].set_settings_callback(lambda: self.open_settings(PanelType.TOGGLES)) self._layouts[MainState.SETTINGS].set_callbacks(on_close=self._set_mode_for_state) self._layouts[MainState.ONROAD].set_click_callback(self._on_onroad_clicked) diff --git a/selfdrive/ui/layouts/settings/firehose.py b/selfdrive/ui/layouts/settings/firehose.py deleted file mode 100644 index 0c1cfe870..000000000 --- a/selfdrive/ui/layouts/settings/firehose.py +++ /dev/null @@ -1,90 +0,0 @@ -import pyray as rl - -from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE -from openpilot.system.ui.lib.multilang import tr, trn, tr_noop -from openpilot.system.ui.lib.text_measure import measure_text_cached -from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel -from openpilot.system.ui.lib.wrap_text import wrap_text -from openpilot.selfdrive.ui.mici.layouts.settings.firehose import FirehoseLayoutBase - -TITLE = tr_noop("Firehose Mode") -DESCRIPTION = tr_noop( - "openpilot learns to drive by watching humans, like you, drive.\n\n" - + "Firehose Mode allows you to maximize your training data uploads to improve " - + "openpilot's driving models. More data means bigger models, which means better Experimental Mode." -) -INSTRUCTIONS = tr_noop( - "For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.\n\n" - + "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.\n\n\n" - + "Frequently Asked Questions\n\n" - + "Does it matter how or where I drive? Nope, just drive as you normally would.\n\n" - + "Do all of my segments get pulled in Firehose Mode? No, we selectively pull a subset of your segments.\n\n" - + "What's a good USB-C adapter? Any fast phone or laptop charger should be fine.\n\n" - + "Does it matter which software I run? Yes, only upstream openpilot (and particular forks) are able to be used for training." -) - - -class FirehoseLayout(FirehoseLayoutBase): - def __init__(self): - super().__init__() - self._scroll_panel = GuiScrollPanel() - - def _render(self, rect: rl.Rectangle): - # Calculate content dimensions - content_rect = rl.Rectangle(rect.x, rect.y, rect.width, self._content_height) - - # Handle scrolling and render with clipping - scroll_offset = self._scroll_panel.update(rect, content_rect) - rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height)) - self._content_height = self._render_content(rect, scroll_offset) - rl.end_scissor_mode() - - def _render_content(self, rect: rl.Rectangle, scroll_offset: float) -> int: - x = int(rect.x + 40) - y = int(rect.y + 40 + scroll_offset) - w = int(rect.width - 80) - - # Title (centered) - title_text = tr(TITLE) # live translate - title_font = gui_app.font(FontWeight.MEDIUM) - text_width = measure_text_cached(title_font, title_text, 100).x - title_x = rect.x + (rect.width - text_width) / 2 - rl.draw_text_ex(title_font, title_text, rl.Vector2(round(title_x), round(y)), 100, 0, rl.WHITE) - y += 200 - - # Description - y = self._draw_wrapped_text(x, y, w, tr(DESCRIPTION), gui_app.font(FontWeight.NORMAL), 45, rl.WHITE) - y += 40 + 20 - - # Separator - rl.draw_rectangle(x, y, w, 2, self.GRAY) - y += 30 + 20 - - # Status - status_text, status_color = self._get_status() - y = self._draw_wrapped_text(x, y, w, status_text, gui_app.font(FontWeight.BOLD), 60, status_color) - y += 20 + 20 - - # Contribution count (if available) - if self._segment_count > 0: - contrib_text = trn("{} segment of your driving is in the training dataset so far.", - "{} segments of your driving is in the training dataset so far.", self._segment_count).format(self._segment_count) - y = self._draw_wrapped_text(x, y, w, contrib_text, gui_app.font(FontWeight.BOLD), 52, rl.WHITE) - y += 20 + 20 - - # Separator - rl.draw_rectangle(x, y, w, 2, self.GRAY) - y += 30 + 20 - - # Instructions - y = self._draw_wrapped_text(x, y, w, tr(INSTRUCTIONS), gui_app.font(FontWeight.NORMAL), 40, self.LIGHT_GRAY) - - # bottom margin + remove effect of scroll offset - return int(round(y - self._scroll_panel.offset + 40)) - - def _draw_wrapped_text(self, x, y, width, text, font, font_size, color): - wrapped = wrap_text(font, text, font_size, width) - for line in wrapped: - rl.draw_text_ex(font, line, rl.Vector2(round(x), round(y)), font_size, 0, color) - y += font_size * FONT_SCALE - return round(y) diff --git a/selfdrive/ui/layouts/settings/settings.py b/selfdrive/ui/layouts/settings/settings.py index 626c5c8f3..e63358b57 100644 --- a/selfdrive/ui/layouts/settings/settings.py +++ b/selfdrive/ui/layouts/settings/settings.py @@ -4,7 +4,6 @@ from enum import IntEnum from collections.abc import Callable from openpilot.selfdrive.ui.layouts.settings.developer import DeveloperLayout from openpilot.selfdrive.ui.layouts.settings.device import DeviceLayout -from openpilot.selfdrive.ui.layouts.settings.firehose import FirehoseLayout from openpilot.selfdrive.ui.layouts.settings.starpilot.main_panel import StarPilotLayout from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout @@ -37,8 +36,7 @@ class PanelType(IntEnum): NETWORK = 2 TOGGLES = 3 SOFTWARE = 4 - FIREHOSE = 5 - DEVELOPER = 6 + DEVELOPER = 5 @dataclass @@ -67,7 +65,6 @@ class SettingsLayout(Widget): PanelType.NETWORK: PanelInfo(tr_noop("Network"), NetworkUI(wifi_manager)), PanelType.TOGGLES: PanelInfo(tr_noop("Toggles"), TogglesLayout()), PanelType.SOFTWARE: PanelInfo(tr_noop("Software"), SoftwareLayout()), - PanelType.FIREHOSE: PanelInfo(tr_noop("Firehose"), FirehoseLayout()), PanelType.DEVELOPER: PanelInfo(tr_noop("Developer"), DeveloperLayout()), } @@ -143,9 +140,6 @@ class SettingsLayout(Widget): # Navigation buttons y = rect.y + 300 for panel_type, panel_info in self._panels.items(): - if panel_type == PanelType.FIREHOSE: - continue - button_rect = rl.Rectangle(rect.x + 50, y, rect.width - 150, NAV_BTN_HEIGHT) # Button styling diff --git a/selfdrive/ui/mici/layouts/settings/firehose.py b/selfdrive/ui/mici/layouts/settings/firehose.py deleted file mode 100644 index 4c27a909f..000000000 --- a/selfdrive/ui/mici/layouts/settings/firehose.py +++ /dev/null @@ -1,222 +0,0 @@ -import requests -import threading -import time -import pyray as rl - -from openpilot.common.api import api_get -from openpilot.common.params import Params -from openpilot.common.swaglog import cloudlog -from openpilot.selfdrive.ui.lib.api_helpers import get_token -from openpilot.selfdrive.ui.ui_state import ui_state, device -from openpilot.system.athena.registration import UNREGISTERED_DONGLE_ID -from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE -from openpilot.system.ui.lib.wrap_text import wrap_text -from openpilot.system.ui.lib.scroll_panel2 import GuiScrollPanel2 -from openpilot.system.ui.lib.multilang import tr, trn, tr_noop -from openpilot.system.ui.widgets import Widget -from openpilot.system.ui.widgets.scroller import NavRawScrollPanel - -TITLE = tr_noop("Firehose Mode") -DESCRIPTION = tr_noop( - "openpilot learns to drive by watching humans, like you, drive.\n\n" - + "Firehose Mode allows you to maximize your training data uploads to improve " - + "openpilot's driving models. More data means bigger models, which means better Experimental Mode." -) -INSTRUCTIONS_INTRO = tr_noop( - "For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.\n\n" - + "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card." -) -FAQ_HEADER = tr_noop("Frequently Asked Questions") -FAQ_ITEMS = [ - (tr_noop("Does it matter how or where I drive?"), tr_noop("Nope, just drive as you normally would.")), - (tr_noop("Do all of my segments get pulled in Firehose Mode?"), tr_noop("No, we selectively pull a subset of your segments.")), - (tr_noop("What's a good USB-C adapter?"), tr_noop("Any fast phone or laptop charger should be fine.")), - (tr_noop("Does it matter which software I run?"), tr_noop("Yes, only upstream openpilot (and particular forks) are able to be used for training.")), -] - - -class FirehoseLayoutBase(Widget): - PARAM_KEY = "ApiCache_FirehoseStats" - GREEN = rl.Color(46, 204, 113, 255) - RED = rl.Color(231, 76, 60, 255) - GRAY = rl.Color(68, 68, 68, 255) - LIGHT_GRAY = rl.Color(228, 228, 228, 255) - UPDATE_INTERVAL = 30 # seconds - - def __init__(self): - super().__init__() - self._params = Params() - self._session = requests.Session() # reuse session to reduce SSL handshake overhead - self._segment_count = self._get_segment_count() - - self._scroll_panel = GuiScrollPanel2(horizontal=False) - self._content_height = 0 - - self._running = True - self._update_thread = threading.Thread(target=self._update_loop, daemon=True) - self._update_thread.start() - - def __del__(self): - self._running = False - try: - if self._update_thread and self._update_thread.is_alive(): - self._update_thread.join(timeout=1.0) - except Exception: - pass - - def show_event(self): - super().show_event() - self._scroll_panel.set_offset(0) - - def _get_segment_count(self) -> int: - stats = self._params.get(self.PARAM_KEY) - if not stats: - return 0 - try: - return int(stats.get("firehose", 0)) - except Exception: - cloudlog.exception(f"Failed to decode firehose stats: {stats}") - return 0 - - def _render(self, rect: rl.Rectangle): - # compute total content height for scrolling - content_height = self._measure_content_height(rect) - scroll_offset = self._scroll_panel.update(rect, content_height) - - # start drawing with offset - x = rect.x + 40 - y = rect.y + 40 + scroll_offset - w = rect.width - 80 - - # Title - title_text = tr(TITLE) - title_font = gui_app.font(FontWeight.BOLD) - title_size = 64 - rl.draw_text_ex(title_font, title_text, rl.Vector2(x, y), title_size, 0, rl.WHITE) - y += int(title_size * FONT_SCALE) + 20 - - # Description - y = self._draw_wrapped_text(x, y, w, tr(DESCRIPTION), gui_app.font(FontWeight.ROMAN), 36, rl.WHITE) - y += 20 - - # Separator - rl.draw_rectangle_rec(rl.Rectangle(x, y, w, 2), self.GRAY) - y += 20 - - # Status - status_text, status_color = self._get_status() - y = self._draw_wrapped_text(x, y, w, status_text, gui_app.font(FontWeight.BOLD), 48, status_color) - y += 20 - - # Contribution count (if available) - if self._segment_count > 0: - contrib_text = trn("{} segment of your driving is in the training dataset so far.", - "{} segments of your driving is in the training dataset so far.", self._segment_count).format(self._segment_count) - y = self._draw_wrapped_text(x, y, w, contrib_text, gui_app.font(FontWeight.BOLD), 42, rl.WHITE) - y += 20 - - # Separator - rl.draw_rectangle_rec(rl.Rectangle(x, y, w, 2), self.GRAY) - y += 20 - - # Instructions intro - y = self._draw_wrapped_text(x, y, w, tr(INSTRUCTIONS_INTRO), gui_app.font(FontWeight.ROMAN), 32, self.LIGHT_GRAY) - y += 20 - - # FAQ Header - y = self._draw_wrapped_text(x, y, w, tr(FAQ_HEADER), gui_app.font(FontWeight.BOLD), 44, rl.WHITE) - y += 20 - - # FAQ Items - for question, answer in FAQ_ITEMS: - y = self._draw_wrapped_text(x, y, w, tr(question), gui_app.font(FontWeight.BOLD), 32, self.LIGHT_GRAY) - y = self._draw_wrapped_text(x, y, w, tr(answer), gui_app.font(FontWeight.ROMAN), 32, self.LIGHT_GRAY) - y += 20 - - def _draw_wrapped_text(self, x, y, width, text, font, font_size, color): - wrapped = wrap_text(font, text, font_size, width) - for line in wrapped: - rl.draw_text_ex(font, line, rl.Vector2(x, y), font_size, 0, color) - y += int(font_size * FONT_SCALE) - return y - - def _measure_content_height(self, rect: rl.Rectangle) -> int: - # Rough measurement using the same wrapping as rendering - w = int(rect.width - 80) - y = 40 - - # Title - title_size = 72 - y += int(title_size * FONT_SCALE) + 20 - - # Description - desc_lines = wrap_text(gui_app.font(FontWeight.ROMAN), tr(DESCRIPTION), 36, w) - y += int(len(desc_lines) * 36 * FONT_SCALE) + 20 - - # Separator + Status - y += 2 + 20 - status_text, _ = self._get_status() - status_lines = wrap_text(gui_app.font(FontWeight.BOLD), status_text, 48, w) - y += int(len(status_lines) * 48 * FONT_SCALE) + 20 - - # Contribution count - if self._segment_count > 0: - contrib_text = trn("{} segment of your driving is in the training dataset so far.", - "{} segments of your driving is in the training dataset so far.", self._segment_count).format(self._segment_count) - contrib_lines = wrap_text(gui_app.font(FontWeight.BOLD), contrib_text, 42, w) - y += int(len(contrib_lines) * 42 * FONT_SCALE) + 20 - - # Separator + Instructions - y += 2 + 20 - - # Instructions intro - intro_lines = wrap_text(gui_app.font(FontWeight.ROMAN), tr(INSTRUCTIONS_INTRO), 32, w) - y += int(len(intro_lines) * 32 * FONT_SCALE) + 20 - - # FAQ Header - faq_header_lines = wrap_text(gui_app.font(FontWeight.BOLD), tr(FAQ_HEADER), 44, w) - y += int(len(faq_header_lines) * 44 * FONT_SCALE) + 20 - - # FAQ Items - for question, answer in FAQ_ITEMS: - q_lines = wrap_text(gui_app.font(FontWeight.BOLD), tr(question), 32, w) - y += int(len(q_lines) * 32 * FONT_SCALE) - a_lines = wrap_text(gui_app.font(FontWeight.ROMAN), tr(answer), 32, w) - y += int(len(a_lines) * 32 * FONT_SCALE) + 20 - - # bottom padding - y += 40 - return y - - def _get_status(self) -> tuple[str, rl.Color]: - network_type = ui_state.sm["deviceState"].networkType - network_metered = ui_state.sm["deviceState"].networkMetered - - if not network_metered and network_type != 0: # Not metered and connected - return tr("ACTIVE"), self.GREEN - else: - return tr("INACTIVE: connect to an unmetered network"), self.RED - - def _fetch_firehose_stats(self): - try: - dongle_id = self._params.get("DongleId") - if not dongle_id or dongle_id == UNREGISTERED_DONGLE_ID: - return - identity_token = get_token(dongle_id) - response = api_get(f"v1/devices/{dongle_id}/firehose_stats", access_token=identity_token, session=self._session) - if response.status_code == 200: - data = response.json() - self._segment_count = data.get("firehose", 0) - self._params.put(self.PARAM_KEY, data) - except Exception as e: - cloudlog.error(f"Failed to fetch firehose stats: {e}") - - def _update_loop(self): - while self._running: - if not ui_state.started and device._awake: - self._fetch_firehose_stats() - time.sleep(self.UPDATE_INTERVAL) - - -class FirehoseLayout(NavRawScrollPanel, FirehoseLayoutBase): - pass diff --git a/selfdrive/ui/qt/offroad/firehose.cc b/selfdrive/ui/qt/offroad/firehose.cc deleted file mode 100644 index fb96d8cb3..000000000 --- a/selfdrive/ui/qt/offroad/firehose.cc +++ /dev/null @@ -1,112 +0,0 @@ -#include "selfdrive/ui/qt/offroad/firehose.h" -#include "selfdrive/ui/ui.h" -#include "selfdrive/ui/qt/offroad/settings.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -FirehosePanel::FirehosePanel(SettingsWindow *parent) : QWidget((QWidget*)parent) { - layout = new QVBoxLayout(this); - layout->setContentsMargins(40, 40, 40, 40); - layout->setSpacing(20); - - // header - QLabel *title = new QLabel(tr("Firehose Mode")); - title->setStyleSheet("font-size: 100px; font-weight: 500; font-family: 'Noto Color Emoji';"); - layout->addWidget(title, 0, Qt::AlignCenter); - - // Create a container for the content - QFrame *content = new QFrame(); - content->setStyleSheet("background-color: #292929; border-radius: 15px; padding: 20px;"); - QVBoxLayout *content_layout = new QVBoxLayout(content); - content_layout->setSpacing(20); - - // Top description - QLabel *description = new QLabel(tr("openpilot learns to drive by watching humans, like you, drive.\n\nFirehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.")); - description->setStyleSheet("font-size: 45px; padding-bottom: 20px;"); - description->setWordWrap(true); - content_layout->addWidget(description); - - // Add a separator - QFrame *line = new QFrame(); - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - line->setStyleSheet("background-color: #444444; margin-top: 5px; margin-bottom: 5px;"); - content_layout->addWidget(line); - - toggle_label = new QLabel(tr("Firehose Mode: ACTIVE")); - toggle_label->setStyleSheet("font-size: 60px; font-weight: bold; color: white;"); - content_layout->addWidget(toggle_label); - - // Add contribution label - contribution_label = new QLabel(); - contribution_label->setStyleSheet("font-size: 52px; margin-top: 10px; margin-bottom: 10px;"); - contribution_label->setWordWrap(true); - contribution_label->hide(); - content_layout->addWidget(contribution_label); - - // Add a separator before detailed instructions - QFrame *line2 = new QFrame(); - line2->setFrameShape(QFrame::HLine); - line2->setFrameShadow(QFrame::Sunken); - line2->setStyleSheet("background-color: #444444; margin-top: 10px; margin-bottom: 10px;"); - content_layout->addWidget(line2); - - // Detailed instructions at the bottom - detailed_instructions = new QLabel(tr( - "For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.
" - "
" - "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.
" - "

" - "Frequently Asked Questions

" - "Does it matter how or where I drive? Nope, just drive as you normally would.

" - "Do all of my segments get pulled in Firehose Mode? No, we selectively pull a subset of your segments.

" - "What's a good USB-C adapter? Any fast phone or laptop charger should be fine.

" - "Does it matter which software I run? Yes, only upstream openpilot (and particular forks) are able to be used for training." - )); - detailed_instructions->setStyleSheet("font-size: 40px; color: #E4E4E4;"); - detailed_instructions->setWordWrap(true); - content_layout->addWidget(detailed_instructions); - - layout->addWidget(content, 1); - - // Set up the API request for firehose stats - const QString dongle_id = QString::fromStdString(Params().get("DongleId")); - firehose_stats = new RequestRepeater(this, CommaApi::BASE_URL + "/v1/devices/" + dongle_id + "/firehose_stats", - "ApiCache_FirehoseStats", 30, false); - QObject::connect(firehose_stats, &RequestRepeater::requestDone, [=](const QString &response, bool success) { - if (success) { - QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8()); - QJsonObject json = doc.object(); - int count = json["firehose"].toInt(); - contribution_label->setText(tr("%n segment(s) of your driving is in the training dataset so far.", "", count)); - contribution_label->show(); - } - }); - - QObject::connect(uiState(), &UIState::uiUpdate, this, &FirehosePanel::refresh); -} - -void FirehosePanel::refresh() { - auto deviceState = (*uiState()->sm)["deviceState"].getDeviceState(); - auto networkType = deviceState.getNetworkType(); - bool networkMetered = deviceState.getNetworkMetered(); - - bool is_active = !networkMetered && (networkType != cereal::DeviceState::NetworkType::NONE); - if (is_active) { - toggle_label->setText(tr("ACTIVE")); - toggle_label->setStyleSheet("font-size: 60px; font-weight: bold; color: #2ecc71;"); - } else { - toggle_label->setText(tr("INACTIVE: connect to an unmetered network")); - toggle_label->setStyleSheet("font-size: 60px;"); - } -} diff --git a/selfdrive/ui/qt/offroad/firehose.h b/selfdrive/ui/qt/offroad/firehose.h deleted file mode 100644 index 9082cb0f9..000000000 --- a/selfdrive/ui/qt/offroad/firehose.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include -#include -#include "selfdrive/ui/qt/request_repeater.h" - -// Forward declarations -class SettingsWindow; - -class FirehosePanel : public QWidget { - Q_OBJECT -public: - explicit FirehosePanel(SettingsWindow *parent); - -private: - QVBoxLayout *layout; - - QLabel *detailed_instructions; - QLabel *contribution_label; - QLabel *toggle_label; - - RequestRepeater *firehose_stats; - -private slots: - void refresh(); -}; diff --git a/selfdrive/ui/qt/offroad/moc_firehose.cc b/selfdrive/ui/qt/offroad/moc_firehose.cc deleted file mode 100644 index cc08c4885..000000000 --- a/selfdrive/ui/qt/offroad/moc_firehose.cc +++ /dev/null @@ -1,118 +0,0 @@ -/**************************************************************************** -** Meta object code from reading C++ file 'firehose.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 "firehose.h" -#include -#include -#if !defined(Q_MOC_OUTPUT_REVISION) -#error "The header file 'firehose.h' doesn't include ." -#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_FirehosePanel_t { - QByteArrayData data[3]; - char stringdata0[23]; -}; -#define QT_MOC_LITERAL(idx, ofs, len) \ - Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ - qptrdiff(offsetof(qt_meta_stringdata_FirehosePanel_t, stringdata0) + ofs \ - - idx * sizeof(QByteArrayData)) \ - ) -static const qt_meta_stringdata_FirehosePanel_t qt_meta_stringdata_FirehosePanel = { - { -QT_MOC_LITERAL(0, 0, 13), // "FirehosePanel" -QT_MOC_LITERAL(1, 14, 7), // "refresh" -QT_MOC_LITERAL(2, 22, 0) // "" - - }, - "FirehosePanel\0refresh\0" -}; -#undef QT_MOC_LITERAL - -static const uint qt_meta_data_FirehosePanel[] = { - - // content: - 8, // revision - 0, // classname - 0, 0, // classinfo - 1, 14, // methods - 0, 0, // properties - 0, 0, // enums/sets - 0, 0, // constructors - 0, // flags - 0, // signalCount - - // slots: name, argc, parameters, tag, flags - 1, 0, 19, 2, 0x08 /* Private */, - - // slots: parameters - QMetaType::Void, - - 0 // eod -}; - -void FirehosePanel::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) -{ - if (_c == QMetaObject::InvokeMetaMethod) { - auto *_t = static_cast(_o); - Q_UNUSED(_t) - switch (_id) { - case 0: _t->refresh(); break; - default: ; - } - } - Q_UNUSED(_a); -} - -QT_INIT_METAOBJECT const QMetaObject FirehosePanel::staticMetaObject = { { - &QWidget::staticMetaObject, - qt_meta_stringdata_FirehosePanel.data, - qt_meta_data_FirehosePanel, - qt_static_metacall, - nullptr, - nullptr -} }; - - -const QMetaObject *FirehosePanel::metaObject() const -{ - return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; -} - -void *FirehosePanel::qt_metacast(const char *_clname) -{ - if (!_clname) return nullptr; - if (!strcmp(_clname, qt_meta_stringdata_FirehosePanel.stringdata0)) - return static_cast(this); - return QWidget::qt_metacast(_clname); -} - -int FirehosePanel::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(_a[0]) = -1; - _id -= 1; - } - return _id; -} -QT_WARNING_POP -QT_END_MOC_NAMESPACE diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 0d4272e52..3d550b0c4 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -20,7 +20,6 @@ #include "selfdrive/ui/qt/widgets/prime.h" #include "selfdrive/ui/qt/widgets/scrollview.h" #include "selfdrive/ui/qt/offroad/developer_panel.h" -#include "selfdrive/ui/qt/offroad/firehose.h" #include "starpilot/ui/qt/offroad/starpilot_settings.h" diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h index 14952e448..7630c296e 100644 --- a/selfdrive/ui/qt/offroad/settings.h +++ b/selfdrive/ui/qt/offroad/settings.h @@ -141,6 +141,3 @@ private: Params params; ParamWatcher *fs_watch; }; - -// Forward declaration -class FirehosePanel; diff --git a/selfdrive/ui/qt/widgets/moc_wifi.cc b/selfdrive/ui/qt/widgets/moc_wifi.cc deleted file mode 100644 index f04945bcf..000000000 --- a/selfdrive/ui/qt/widgets/moc_wifi.cc +++ /dev/null @@ -1,142 +0,0 @@ -/**************************************************************************** -** Meta object code from reading C++ file 'wifi.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 "wifi.h" -#include -#include -#if !defined(Q_MOC_OUTPUT_REVISION) -#error "The header file 'wifi.h' doesn't include ." -#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_WiFiPromptWidget_t { - QByteArrayData data[5]; - char stringdata0[43]; -}; -#define QT_MOC_LITERAL(idx, ofs, len) \ - Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ - qptrdiff(offsetof(qt_meta_stringdata_WiFiPromptWidget_t, stringdata0) + ofs \ - - idx * sizeof(QByteArrayData)) \ - ) -static const qt_meta_stringdata_WiFiPromptWidget_t qt_meta_stringdata_WiFiPromptWidget = { - { -QT_MOC_LITERAL(0, 0, 16), // "WiFiPromptWidget" -QT_MOC_LITERAL(1, 17, 12), // "openSettings" -QT_MOC_LITERAL(2, 30, 0), // "" -QT_MOC_LITERAL(3, 31, 5), // "index" -QT_MOC_LITERAL(4, 37, 5) // "param" - - }, - "WiFiPromptWidget\0openSettings\0\0index\0" - "param" -}; -#undef QT_MOC_LITERAL - -static const uint qt_meta_data_WiFiPromptWidget[] = { - - // content: - 8, // revision - 0, // classname - 0, 0, // classinfo - 3, 14, // methods - 0, 0, // properties - 0, 0, // enums/sets - 0, 0, // constructors - 0, // flags - 3, // signalCount - - // signals: name, argc, parameters, tag, flags - 1, 2, 29, 2, 0x06 /* Public */, - 1, 1, 34, 2, 0x26 /* Public | MethodCloned */, - 1, 0, 37, 2, 0x26 /* Public | MethodCloned */, - - // signals: parameters - QMetaType::Void, QMetaType::Int, QMetaType::QString, 3, 4, - QMetaType::Void, QMetaType::Int, 3, - QMetaType::Void, - - 0 // eod -}; - -void WiFiPromptWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) -{ - if (_c == QMetaObject::InvokeMetaMethod) { - auto *_t = static_cast(_o); - Q_UNUSED(_t) - switch (_id) { - case 0: _t->openSettings((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< const QString(*)>(_a[2]))); break; - case 1: _t->openSettings((*reinterpret_cast< int(*)>(_a[1]))); break; - case 2: _t->openSettings(); break; - default: ; - } - } else if (_c == QMetaObject::IndexOfMethod) { - int *result = reinterpret_cast(_a[0]); - { - using _t = void (WiFiPromptWidget::*)(int , const QString & ); - if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&WiFiPromptWidget::openSettings)) { - *result = 0; - return; - } - } - } -} - -QT_INIT_METAOBJECT const QMetaObject WiFiPromptWidget::staticMetaObject = { { - &QFrame::staticMetaObject, - qt_meta_stringdata_WiFiPromptWidget.data, - qt_meta_data_WiFiPromptWidget, - qt_static_metacall, - nullptr, - nullptr -} }; - - -const QMetaObject *WiFiPromptWidget::metaObject() const -{ - return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; -} - -void *WiFiPromptWidget::qt_metacast(const char *_clname) -{ - if (!_clname) return nullptr; - if (!strcmp(_clname, qt_meta_stringdata_WiFiPromptWidget.stringdata0)) - return static_cast(this); - return QFrame::qt_metacast(_clname); -} - -int WiFiPromptWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a) -{ - _id = QFrame::qt_metacall(_c, _id, _a); - if (_id < 0) - return _id; - if (_c == QMetaObject::InvokeMetaMethod) { - if (_id < 3) - qt_static_metacall(this, _c, _id, _a); - _id -= 3; - } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { - if (_id < 3) - *reinterpret_cast(_a[0]) = -1; - _id -= 3; - } - return _id; -} - -// SIGNAL 0 -void WiFiPromptWidget::openSettings(int _t1, const QString & _t2) -{ - void *_a[] = { nullptr, const_cast(reinterpret_cast(&_t1)), const_cast(reinterpret_cast(&_t2)) }; - QMetaObject::activate(this, &staticMetaObject, 0, _a); -} -QT_WARNING_POP -QT_END_MOC_NAMESPACE diff --git a/selfdrive/ui/qt/widgets/prime.cc b/selfdrive/ui/qt/widgets/prime.cc index 1a28ea69f..bfac96e84 100644 --- a/selfdrive/ui/qt/widgets/prime.cc +++ b/selfdrive/ui/qt/widgets/prime.cc @@ -14,7 +14,6 @@ #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; @@ -232,10 +231,6 @@ SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) { QVBoxLayout *content_layout = new QVBoxLayout(content); content_layout->setContentsMargins(0, 0, 0, 0); content_layout->setSpacing(30); - - 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); diff --git a/selfdrive/ui/qt/widgets/wifi.cc b/selfdrive/ui/qt/widgets/wifi.cc deleted file mode 100644 index d7eb8beeb..000000000 --- a/selfdrive/ui/qt/widgets/wifi.cc +++ /dev/null @@ -1,45 +0,0 @@ -#include "selfdrive/ui/qt/widgets/wifi.h" - -#include -#include -#include -#include - -WiFiPromptWidget::WiFiPromptWidget(QWidget *parent) : QFrame(parent) { - // Setup Firehose Mode - QVBoxLayout *main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(56, 40, 56, 40); - main_layout->setSpacing(42); - - QLabel *title = new QLabel(tr("🔥 Firehose Mode 🔥")); - title->setStyleSheet("font-size: 64px; font-weight: 500;"); - main_layout->addWidget(title); - - QLabel *desc = new QLabel(tr("Maximize your training data uploads to improve openpilot's driving models.")); - desc->setStyleSheet("font-size: 40px; font-weight: 400;"); - desc->setWordWrap(true); - main_layout->addWidget(desc); - - QPushButton *settings_btn = new QPushButton(tr("Open")); - connect(settings_btn, &QPushButton::clicked, [=]() { emit openSettings(1, "FirehosePanel"); }); - settings_btn->setStyleSheet(R"( - QPushButton { - font-size: 48px; - font-weight: 500; - border-radius: 10px; - background-color: #465BEA; - padding: 32px; - } - QPushButton:pressed { - background-color: #3049F4; - } - )"); - main_layout->addWidget(settings_btn); - - setStyleSheet(R"( - WiFiPromptWidget { - background-color: #333333; - border-radius: 10px; - } - )"); -} \ No newline at end of file diff --git a/selfdrive/ui/qt/widgets/wifi.h b/selfdrive/ui/qt/widgets/wifi.h deleted file mode 100644 index 3e68a15b7..000000000 --- a/selfdrive/ui/qt/widgets/wifi.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include - -class WiFiPromptWidget : public QFrame { - Q_OBJECT - -public: - explicit WiFiPromptWidget(QWidget* parent = 0); - -signals: - void openSettings(int index = 0, const QString ¶m = ""); -}; diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 481ac111b..c9003cd6a 100644 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -103,11 +103,6 @@ def setup_settings_software_branch_switcher(click, pm: PubMaster): click(1984, 449) -def setup_settings_firehose(click, pm: PubMaster): - setup_settings(click, pm) - click(278, 845) - - def setup_settings_developer(click, pm: PubMaster): CP = car.CarParams() CP.alphaLongitudinalAvailable = True # show alpha long control toggle @@ -226,7 +221,6 @@ CASES = { "settings_software_download": setup_settings_software_download, "settings_software_release_notes": setup_settings_software_release_notes, "settings_software_branch_switcher": setup_settings_software_branch_switcher, - "settings_firehose": setup_settings_firehose, "settings_developer": setup_settings_developer, "keyboard": setup_keyboard, "pair_device": setup_pair_device, diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py index 3c9406688..42565bd61 100644 --- a/selfdrive/ui/widgets/setup.py +++ b/selfdrive/ui/widgets/setup.py @@ -8,27 +8,24 @@ from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.confirm_dialog import alert_dialog from openpilot.system.ui.widgets.button import Button, ButtonStyle -from openpilot.system.ui.widgets.label import Label + + +LOGO_WIDTH = 750 +LOGO_HEIGHT = 770 class SetupWidget(Widget): def __init__(self): super().__init__() - self._open_settings_callback = None self._pairing_dialog: PairingDialog | None = None self._pair_device_btn = Button(lambda: tr("Pair device"), self._show_pairing, button_style=ButtonStyle.PRIMARY) - self._open_settings_btn = Button(lambda: tr("Open"), lambda: self._open_settings_callback() if self._open_settings_callback else None, - button_style=ButtonStyle.PRIMARY) - self._firehose_label = Label(lambda: tr("🔥 Firehose Mode 🔥"), font_weight=FontWeight.MEDIUM, font_size=64) - - def set_open_settings_callback(self, callback): - self._open_settings_callback = callback + self._logo_texture = gui_app.texture("images/StarPilotLogo.png", LOGO_WIDTH, LOGO_HEIGHT) def _render(self, rect: rl.Rectangle): if not ui_state.prime_state.is_paired(): self._render_registration(rect) else: - self._render_firehose_prompt(rect) + self._render_logo(rect) def _render_registration(self, rect: rl.Rectangle): """Render registration prompt.""" @@ -55,36 +52,12 @@ class SetupWidget(Widget): button_rect = rl.Rectangle(x, y + 30, w, 200) self._pair_device_btn.render(button_rect) - def _render_firehose_prompt(self, rect: rl.Rectangle): - """Render firehose prompt widget.""" - - rl.draw_rectangle_rounded(rl.Rectangle(rect.x, rect.y, rect.width, 500), 0.04, 20, rl.Color(51, 51, 51, 255)) - - # Content margins (56, 40, 56, 40) - x = rect.x + 56 - y = rect.y + 40 - w = rect.width - 112 - spacing = 42 - - # Title with fire emojis - self._firehose_label.render(rl.Rectangle(rect.x, y, rect.width, 64)) - y += 64 + spacing - - # Description - desc_font = gui_app.font(FontWeight.NORMAL) - desc_text = tr("Maximize your training data uploads to improve openpilot's driving models.") - wrapped_desc = wrap_text(desc_font, desc_text, 40, int(w)) - - for line in wrapped_desc: - rl.draw_text_ex(desc_font, line, rl.Vector2(x, y), 40, 0, rl.WHITE) - y += 40 * FONT_SCALE - - y += spacing - - # Open button - button_height = 48 + 64 # font size + padding - button_rect = rl.Rectangle(x, y, w, button_height) - self._open_settings_btn.render(button_rect) + def _render_logo(self, rect: rl.Rectangle): + tex_w = self._logo_texture.width + tex_h = self._logo_texture.height + x = rect.x + (rect.width - tex_w) / 2 + y = rect.y + (rect.height - tex_h) / 2 + rl.draw_texture(self._logo_texture, int(x), int(y), rl.WHITE) def _show_pairing(self): if not system_time_valid():