mirror of
https://github.com/dzid26/sunnypilot.git
synced 2026-06-08 07:44:55 +08:00
new reset (#37563)
* start new reset w navwidgets * full port * clean up * clean up * clean up * fixes * rm
This commit is contained in:
BIN
selfdrive/assets/icons_mici/setup/factory_reset.png
LFS
Normal file
BIN
selfdrive/assets/icons_mici/setup/factory_reset.png
LFS
Normal file
Binary file not shown.
BIN
selfdrive/assets/icons_mici/setup/reset_failed.png
LFS
Normal file
BIN
selfdrive/assets/icons_mici/setup/reset_failed.png
LFS
Normal file
Binary file not shown.
@@ -1,18 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from enum import IntEnum
|
||||
|
||||
import pyray as rl
|
||||
|
||||
from openpilot.system.hardware import PC
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets.slider import SmallSlider
|
||||
from openpilot.system.ui.widgets.button import SmallButton, FullRoundedButton
|
||||
from openpilot.system.ui.widgets.label import gui_label, gui_text_box
|
||||
from openpilot.system.hardware import HARDWARE, PC
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.widgets.scroller import Scroller
|
||||
from openpilot.system.ui.widgets.nav_widget import NavWidget
|
||||
from openpilot.system.ui.mici_setup import GreyBigButton, FailedPage
|
||||
from openpilot.selfdrive.ui.mici.widgets.dialog import BigConfirmationDialogV2
|
||||
from openpilot.selfdrive.ui.mici.widgets.button import BigCircleButton
|
||||
|
||||
USERDATA = "/dev/disk/by-partlabel/userdata"
|
||||
TIMEOUT = 3*60
|
||||
@@ -24,32 +24,87 @@ class ResetMode(IntEnum):
|
||||
FORMAT = 2 # finish up a factory reset from a tool that doesn't flash an empty partition to userdata
|
||||
|
||||
|
||||
class ResetState(IntEnum):
|
||||
NONE = 0
|
||||
RESETTING = 1
|
||||
FAILED = 2
|
||||
class ResetFailedPage(FailedPage):
|
||||
def __init__(self):
|
||||
super().__init__(None, "reset failed", "reboot to try again", icon="icons_mici/setup/reset_failed.png")
|
||||
|
||||
def show_event(self):
|
||||
super().show_event()
|
||||
self._nav_bar._alpha = 0.0 # not dismissable
|
||||
|
||||
def _back_enabled(self) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
class Reset(Widget):
|
||||
class ResettingPage(NavWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self._resetting_card = GreyBigButton("resetting device", "this may take up to\na minute...",
|
||||
gui_app.texture("icons_mici/setup/factory_reset.png", 64, 64))
|
||||
|
||||
def show_event(self):
|
||||
super().show_event()
|
||||
self._nav_bar._alpha = 0.0 # not dismissable
|
||||
|
||||
def _back_enabled(self) -> bool:
|
||||
return False
|
||||
|
||||
def _render(self, _):
|
||||
self._resetting_card.render(rl.Rectangle(
|
||||
self._rect.x + self._rect.width / 2 - self._resetting_card.rect.width / 2,
|
||||
self._rect.y + self._rect.height / 2 - self._resetting_card.rect.height / 2,
|
||||
self._resetting_card.rect.width,
|
||||
self._resetting_card.rect.height,
|
||||
))
|
||||
|
||||
|
||||
class Reset(Scroller):
|
||||
def __init__(self, mode):
|
||||
super().__init__()
|
||||
self._mode = mode
|
||||
self._previous_reset_state = None
|
||||
self._reset_state = ResetState.NONE
|
||||
self._previous_active_widget = None
|
||||
self._reset_failed = False
|
||||
self._timeout_st = time.monotonic()
|
||||
|
||||
self._cancel_button = SmallButton("cancel")
|
||||
self._cancel_button.set_click_callback(gui_app.request_close)
|
||||
self._resetting_page = ResettingPage()
|
||||
self._reset_failed_page = ResetFailedPage()
|
||||
|
||||
self._reboot_button = FullRoundedButton("reboot")
|
||||
self._reboot_button.set_click_callback(self._do_reboot)
|
||||
def show_confirm_dialog():
|
||||
dialog = BigConfirmationDialogV2("erase\ndevice", "icons_mici/settings/device/uninstall.png", red=True,
|
||||
confirm_callback=self.start_reset)
|
||||
gui_app.push_widget(dialog)
|
||||
|
||||
self._confirm_slider = SmallSlider("reset", self._confirm)
|
||||
def show_cancel_dialog():
|
||||
dialog = BigConfirmationDialogV2("normal\nstartup", "icons_mici/settings/device/reboot.png",
|
||||
exit_on_confirm=False, confirm_callback=gui_app.request_close)
|
||||
gui_app.push_widget(dialog)
|
||||
|
||||
def _do_reboot(self):
|
||||
if PC:
|
||||
return
|
||||
def show_reboot_dialog():
|
||||
dialog = BigConfirmationDialogV2("reboot\ndevice", "icons_mici/settings/device/reboot.png",
|
||||
exit_on_confirm=False, confirm_callback=HARDWARE.reboot)
|
||||
gui_app.push_widget(dialog)
|
||||
|
||||
os.system("sudo reboot")
|
||||
self._reset_button = BigCircleButton("icons_mici/settings/device/uninstall.png", red=True)
|
||||
self._reset_button.set_click_callback(show_confirm_dialog)
|
||||
|
||||
self._cancel_button = BigCircleButton("icons_mici/settings/device/reboot.png")
|
||||
self._cancel_button.set_click_callback(show_cancel_dialog)
|
||||
|
||||
main_card = GreyBigButton("factory reset", "all content and\nsettings will be erased",
|
||||
gui_app.texture("icons_mici/setup/factory_reset.png", 64, 64))
|
||||
|
||||
# cancel button becomes reboot button
|
||||
if mode == ResetMode.RECOVER:
|
||||
main_card.set_text("unable to mount\ndata partition")
|
||||
main_card.set_value("it may be corrupted")
|
||||
self._cancel_button.set_click_callback(show_reboot_dialog)
|
||||
|
||||
self._scroller.add_widgets([
|
||||
main_card,
|
||||
self._reset_button,
|
||||
self._cancel_button,
|
||||
])
|
||||
|
||||
def _do_erase(self):
|
||||
if PC:
|
||||
@@ -63,72 +118,26 @@ class Reset(Widget):
|
||||
if rm == 0 or fmt == 0:
|
||||
os.system("sudo reboot")
|
||||
else:
|
||||
self._reset_state = ResetState.FAILED
|
||||
self._reset_failed = True
|
||||
|
||||
def start_reset(self):
|
||||
self._reset_state = ResetState.RESETTING
|
||||
threading.Timer(0.1, self._do_erase).start()
|
||||
self._resetting_page.set_shown_callback(self._do_erase)
|
||||
gui_app.push_widget(self._resetting_page)
|
||||
|
||||
def _update_state(self):
|
||||
if self._reset_state != self._previous_reset_state:
|
||||
self._previous_reset_state = self._reset_state
|
||||
super()._update_state()
|
||||
|
||||
if self._reset_failed:
|
||||
self._reset_failed = False
|
||||
gui_app.pop_widgets_to(self, lambda: gui_app.push_widget(self._reset_failed_page))
|
||||
|
||||
active_widget = gui_app.get_active_widget()
|
||||
if active_widget != self._previous_active_widget:
|
||||
self._previous_active_widget = active_widget
|
||||
self._timeout_st = time.monotonic()
|
||||
elif self._mode != ResetMode.RECOVER and self._reset_state != ResetState.RESETTING and (time.monotonic() - self._timeout_st) > TIMEOUT:
|
||||
elif self._mode != ResetMode.RECOVER and active_widget != self._resetting_page and (time.monotonic() - self._timeout_st) > TIMEOUT:
|
||||
exit(0)
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
label_rect = rl.Rectangle(rect.x + 8, rect.y + 8, rect.width, 50)
|
||||
gui_label(label_rect, "factory reset", 48, font_weight=FontWeight.BOLD,
|
||||
color=rl.Color(255, 255, 255, int(255 * 0.9)))
|
||||
|
||||
text_rect = rl.Rectangle(rect.x + 8, rect.y + 56, rect.width - 8 * 2, rect.height - 80)
|
||||
gui_text_box(text_rect, self._get_body_text(), 36, font_weight=FontWeight.ROMAN, line_scale=0.9)
|
||||
|
||||
if self._reset_state != ResetState.RESETTING:
|
||||
# fade out cancel button as slider is moved, set visible to prevent pressing invisible cancel
|
||||
self._cancel_button.set_opacity(1.0 - self._confirm_slider.slider_percentage)
|
||||
self._cancel_button.set_visible(self._confirm_slider.slider_percentage < 0.8)
|
||||
|
||||
if self._mode == ResetMode.RECOVER:
|
||||
self._cancel_button.set_text("reboot")
|
||||
self._cancel_button.set_click_callback(self._do_reboot)
|
||||
self._cancel_button.render(rl.Rectangle(
|
||||
rect.x + 8,
|
||||
rect.y + rect.height - self._cancel_button.rect.height,
|
||||
self._cancel_button.rect.width,
|
||||
self._cancel_button.rect.height))
|
||||
elif self._mode == ResetMode.USER_RESET and self._reset_state != ResetState.FAILED:
|
||||
self._cancel_button.render(rl.Rectangle(
|
||||
rect.x + 8,
|
||||
rect.y + rect.height - self._cancel_button.rect.height,
|
||||
self._cancel_button.rect.width,
|
||||
self._cancel_button.rect.height))
|
||||
|
||||
if self._reset_state != ResetState.FAILED:
|
||||
self._confirm_slider.render(rl.Rectangle(
|
||||
rect.x + rect.width - self._confirm_slider.rect.width,
|
||||
rect.y + rect.height - self._confirm_slider.rect.height,
|
||||
self._confirm_slider.rect.width,
|
||||
self._confirm_slider.rect.height))
|
||||
else:
|
||||
self._reboot_button.render(rl.Rectangle(
|
||||
rect.x + 8,
|
||||
rect.y + rect.height - self._reboot_button.rect.height,
|
||||
self._reboot_button.rect.width,
|
||||
self._reboot_button.rect.height))
|
||||
|
||||
def _confirm(self):
|
||||
self.start_reset()
|
||||
|
||||
def _get_body_text(self):
|
||||
if self._reset_state == ResetState.RESETTING:
|
||||
return "Resetting device... This may take up to a minute."
|
||||
if self._reset_state == ResetState.FAILED:
|
||||
return "Reset failed. Reboot to try again."
|
||||
if self._mode == ResetMode.RECOVER:
|
||||
return "Unable to mount data partition. It may be corrupted."
|
||||
return "All content and settings will be erased."
|
||||
|
||||
|
||||
def main():
|
||||
mode = ResetMode.USER_RESET
|
||||
@@ -140,12 +149,11 @@ def main():
|
||||
|
||||
gui_app.init_window("System Reset")
|
||||
reset = Reset(mode)
|
||||
gui_app.push_widget(reset)
|
||||
|
||||
if mode == ResetMode.FORMAT:
|
||||
reset.start_reset()
|
||||
|
||||
gui_app.push_widget(reset)
|
||||
|
||||
for _ in gui_app.render():
|
||||
pass
|
||||
|
||||
|
||||
@@ -221,7 +221,8 @@ class DownloadingPage(NavWidget):
|
||||
|
||||
|
||||
class FailedPage(NavScroller):
|
||||
def __init__(self, retry_callback: Callable, title: str = "download failed", red_icon: bool = False):
|
||||
def __init__(self, retry_callback: Callable | None, title: str = "download failed",
|
||||
description: str | None = None, icon: str = "icons_mici/setup/warning.png"):
|
||||
super().__init__()
|
||||
self.set_back_callback(retry_callback)
|
||||
|
||||
@@ -236,11 +237,9 @@ class FailedPage(NavScroller):
|
||||
self._reason_card = GreyBigButton("", "")
|
||||
self._reason_card.set_visible(False)
|
||||
|
||||
warning_icon = "icons_mici/setup/red_warning.png" if red_icon else "icons_mici/setup/warning.png"
|
||||
|
||||
self._scroller.add_widgets([
|
||||
GreyBigButton(title, "swipe down to go\nback and try again",
|
||||
gui_app.texture(warning_icon, 64, 58)),
|
||||
GreyBigButton(title, description or "swipe down to go\nback and try again",
|
||||
gui_app.texture(icon, 64, 58)),
|
||||
self._reason_card,
|
||||
reboot_button,
|
||||
])
|
||||
@@ -459,7 +458,7 @@ class Setup(Widget):
|
||||
|
||||
self._software_selection_page = SoftwareSelectionPage(self._use_openpilot, lambda: gui_app.push_widget(self._custom_software_warning_page))
|
||||
|
||||
self._download_failed_page = FailedPage(self._pop_to_software_selection, red_icon=True)
|
||||
self._download_failed_page = FailedPage(self._pop_to_software_selection, icon="icons_mici/setup/red_warning.png")
|
||||
|
||||
self._custom_software_warning_page = CustomSoftwareWarningPage(lambda: self._push_network_setup(True), self._pop_to_software_selection)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user