parent
							
								
									a029d13398
								
							
						
					
					
						commit
						a9b9e0bb54
					
				
				 1 changed files with 197 additions and 0 deletions
			
			
		| @ -0,0 +1,197 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  | import sys | ||||||
|  | import subprocess | ||||||
|  | import threading | ||||||
|  | import pyray as rl | ||||||
|  | from enum import IntEnum | ||||||
|  | 
 | ||||||
|  | from openpilot.system.hardware import HARDWARE | ||||||
|  | from openpilot.system.ui.lib.application import gui_app, FontWeight | ||||||
|  | from openpilot.system.ui.lib.button import gui_button, ButtonStyle | ||||||
|  | from openpilot.system.ui.lib.label import gui_text_box, gui_label | ||||||
|  | 
 | ||||||
|  | # Constants | ||||||
|  | MARGIN = 50 | ||||||
|  | BUTTON_HEIGHT = 160 | ||||||
|  | BUTTON_WIDTH = 400 | ||||||
|  | PROGRESS_BAR_HEIGHT = 72 | ||||||
|  | TITLE_FONT_SIZE = 80 | ||||||
|  | BODY_FONT_SIZE = 65 | ||||||
|  | BACKGROUND_COLOR = rl.BLACK | ||||||
|  | PROGRESS_BG_COLOR = rl.Color(41, 41, 41, 255) | ||||||
|  | PROGRESS_COLOR = rl.Color(54, 77, 239, 255) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Screen(IntEnum): | ||||||
|  |   PROMPT = 0 | ||||||
|  |   WIFI = 1 | ||||||
|  |   PROGRESS = 2 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Updater: | ||||||
|  |   def __init__(self, updater_path, manifest_path): | ||||||
|  |     self.updater = updater_path | ||||||
|  |     self.manifest = manifest_path | ||||||
|  |     self.current_screen = Screen.PROMPT | ||||||
|  | 
 | ||||||
|  |     self.progress_value = 0 | ||||||
|  |     self.progress_text = "Loading..." | ||||||
|  |     self.show_reboot_button = False | ||||||
|  |     self.process = None | ||||||
|  |     self.update_thread = None | ||||||
|  | 
 | ||||||
|  |   def install_update(self): | ||||||
|  |     self.current_screen = Screen.PROGRESS | ||||||
|  |     self.progress_value = 0 | ||||||
|  |     self.progress_text = "Downloading..." | ||||||
|  |     self.show_reboot_button = False | ||||||
|  | 
 | ||||||
|  |     # Start the update process in a separate thread | ||||||
|  |     self.update_thread = threading.Thread(target=self._run_update_process) | ||||||
|  |     self.update_thread.daemon = True | ||||||
|  |     self.update_thread.start() | ||||||
|  | 
 | ||||||
|  |   def _run_update_process(self): | ||||||
|  |     # TODO: just import it and run in a thread without a subprocess | ||||||
|  |     cmd = [self.updater, "--swap", self.manifest] | ||||||
|  |     self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, | ||||||
|  |                                    text=True, bufsize=1, universal_newlines=True) | ||||||
|  | 
 | ||||||
|  |     for line in self.process.stdout: | ||||||
|  |       parts = line.strip().split(":") | ||||||
|  |       if len(parts) == 2: | ||||||
|  |         self.progress_text = parts[0] | ||||||
|  |         try: | ||||||
|  |           self.progress_value = int(float(parts[1])) | ||||||
|  |         except ValueError: | ||||||
|  |           pass | ||||||
|  | 
 | ||||||
|  |     exit_code = self.process.wait() | ||||||
|  |     if exit_code == 0: | ||||||
|  |       HARDWARE.reboot() | ||||||
|  |     else: | ||||||
|  |       self.progress_text = "Update failed" | ||||||
|  |       self.show_reboot_button = True | ||||||
|  | 
 | ||||||
|  |   def render_prompt_screen(self): | ||||||
|  |     # Title | ||||||
|  |     title_rect = rl.Rectangle(MARGIN + 50, 250, gui_app.width - MARGIN * 2 - 100, TITLE_FONT_SIZE) | ||||||
|  |     gui_label(title_rect, "Update Required", TITLE_FONT_SIZE, font_weight=FontWeight.BOLD) | ||||||
|  | 
 | ||||||
|  |     # Description | ||||||
|  |     desc_text = "An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. \ | ||||||
|  |                  The download size is approximately 1GB." | ||||||
|  |     desc_rect = rl.Rectangle(MARGIN + 50, 250 + TITLE_FONT_SIZE + 75, gui_app.width - MARGIN * 2 - 100, BODY_FONT_SIZE * 3) | ||||||
|  |     gui_text_box(desc_rect, desc_text, BODY_FONT_SIZE) | ||||||
|  | 
 | ||||||
|  |     # Buttons at the bottom | ||||||
|  |     button_y = gui_app.height - MARGIN - BUTTON_HEIGHT | ||||||
|  |     button_width = (gui_app.width - MARGIN * 3) // 2 | ||||||
|  | 
 | ||||||
|  |     # WiFi button | ||||||
|  |     wifi_button_rect = rl.Rectangle(MARGIN, button_y, button_width, BUTTON_HEIGHT) | ||||||
|  |     if gui_button(wifi_button_rect, "Connect to Wi-Fi"): | ||||||
|  |       self.current_screen = Screen.WIFI | ||||||
|  |       return  # Return to avoid processing other buttons after screen change | ||||||
|  | 
 | ||||||
|  |     # Install button | ||||||
|  |     install_button_rect = rl.Rectangle(MARGIN * 2 + button_width, button_y, button_width, BUTTON_HEIGHT) | ||||||
|  |     if gui_button(install_button_rect, "Install", button_style=ButtonStyle.PRIMARY): | ||||||
|  |       self.install_update() | ||||||
|  |       return  # Return to avoid further processing after action | ||||||
|  | 
 | ||||||
|  |   def render_wifi_screen(self): | ||||||
|  |     # Title and back button | ||||||
|  |     title_rect = rl.Rectangle(MARGIN + 50, MARGIN, gui_app.width - MARGIN * 2 - 100, 60) | ||||||
|  |     gui_label(title_rect, "Wi-Fi Networks", 60, font_weight=FontWeight.BOLD) | ||||||
|  | 
 | ||||||
|  |     back_button_rect = rl.Rectangle(MARGIN, gui_app.height - MARGIN - BUTTON_HEIGHT, BUTTON_WIDTH, BUTTON_HEIGHT) | ||||||
|  |     if gui_button(back_button_rect, "Back"): | ||||||
|  |       self.current_screen = Screen.PROMPT | ||||||
|  |       return  # Return to avoid processing other interactions after screen change | ||||||
|  | 
 | ||||||
|  |     # Draw placeholder for WiFi implementation | ||||||
|  |     placeholder_rect = rl.Rectangle( | ||||||
|  |       MARGIN, | ||||||
|  |       title_rect.y + title_rect.height + MARGIN, | ||||||
|  |       gui_app.width - MARGIN * 2, | ||||||
|  |       gui_app.height - title_rect.height - MARGIN * 3 - BUTTON_HEIGHT | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     # Draw rounded rectangle background | ||||||
|  |     rl.draw_rectangle_rounded( | ||||||
|  |       placeholder_rect, | ||||||
|  |       0.1, | ||||||
|  |       10, | ||||||
|  |       rl.Color(41, 41, 41, 255) | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     # Draw placeholder text | ||||||
|  |     placeholder_text = "WiFi Implementation Placeholder" | ||||||
|  |     text_size = rl.measure_text_ex(gui_app.font(), placeholder_text, 80, 1) | ||||||
|  |     text_pos = rl.Vector2( | ||||||
|  |       placeholder_rect.x + (placeholder_rect.width - text_size.x) / 2, | ||||||
|  |       placeholder_rect.y + (placeholder_rect.height - text_size.y) / 2 | ||||||
|  |     ) | ||||||
|  |     rl.draw_text_ex(gui_app.font(), placeholder_text, text_pos, 80, 1, rl.WHITE) | ||||||
|  | 
 | ||||||
|  |     # Draw instructions | ||||||
|  |     instructions_text = "Real WiFi functionality would be implemented here" | ||||||
|  |     instructions_size = rl.measure_text_ex(gui_app.font(), instructions_text, 40, 1) | ||||||
|  |     instructions_pos = rl.Vector2( | ||||||
|  |       placeholder_rect.x + (placeholder_rect.width - instructions_size.x) / 2, | ||||||
|  |       text_pos.y + text_size.y + 20 | ||||||
|  |     ) | ||||||
|  |     rl.draw_text_ex(gui_app.font(), instructions_text, instructions_pos, 40, 1, rl.GRAY) | ||||||
|  | 
 | ||||||
|  |   def render_progress_screen(self): | ||||||
|  |     title_rect = rl.Rectangle(MARGIN + 100, 330, gui_app.width - MARGIN * 2 - 200, 100) | ||||||
|  |     gui_label(title_rect, self.progress_text, 90, font_weight=FontWeight.SEMI_BOLD) | ||||||
|  | 
 | ||||||
|  |     # Progress bar | ||||||
|  |     bar_rect = rl.Rectangle(MARGIN + 100, 330 + 100 + 100, gui_app.width - MARGIN * 2 - 200, PROGRESS_BAR_HEIGHT) | ||||||
|  |     rl.draw_rectangle_rounded(bar_rect, 0.5, 10, PROGRESS_BG_COLOR) | ||||||
|  | 
 | ||||||
|  |     # Calculate the width of the progress chunk | ||||||
|  |     progress_width = (bar_rect.width * self.progress_value) / 100 | ||||||
|  |     if progress_width > 0: | ||||||
|  |       progress_rect = rl.Rectangle(bar_rect.x, bar_rect.y, progress_width, bar_rect.height) | ||||||
|  |       rl.draw_rectangle_rounded(progress_rect, 0.5, 10, PROGRESS_COLOR) | ||||||
|  | 
 | ||||||
|  |     # Show reboot button if needed | ||||||
|  |     if self.show_reboot_button: | ||||||
|  |       reboot_rect = rl.Rectangle(MARGIN + 100, gui_app.height - MARGIN - BUTTON_HEIGHT, BUTTON_WIDTH, BUTTON_HEIGHT) | ||||||
|  |       if gui_button(reboot_rect, "Reboot"): | ||||||
|  |         # Return True to signal main loop to exit before rebooting | ||||||
|  |         HARDWARE.reboot() | ||||||
|  |         return | ||||||
|  | 
 | ||||||
|  |   def render(self): | ||||||
|  |     if self.current_screen == Screen.PROMPT: | ||||||
|  |       self.render_prompt_screen() | ||||||
|  |     elif self.current_screen == Screen.WIFI: | ||||||
|  |       self.render_wifi_screen() | ||||||
|  |     elif self.current_screen == Screen.PROGRESS: | ||||||
|  |       self.render_progress_screen() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def main(): | ||||||
|  |   if len(sys.argv) < 3: | ||||||
|  |     print("Usage: updater.py <updater_path> <manifest_path>") | ||||||
|  |     sys.exit(1) | ||||||
|  | 
 | ||||||
|  |   updater_path = sys.argv[1] | ||||||
|  |   manifest_path = sys.argv[2] | ||||||
|  | 
 | ||||||
|  |   try: | ||||||
|  |     gui_app.init_window("System Update") | ||||||
|  |     updater = Updater(updater_path, manifest_path) | ||||||
|  |     for _ in gui_app.render(): | ||||||
|  |       updater.render() | ||||||
|  |   finally: | ||||||
|  |     # Make sure we clean up even if there's an error | ||||||
|  |     gui_app.close() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == "__main__": | ||||||
|  |   main() | ||||||
					Loading…
					
					
				
		Reference in new issue