mirror of
https://github.com/infiniteCable2/openpilot.git
synced 2026-08-05 00:05:57 +08:00
Merge pull request #7 from infiniteCable2/updater_mici
branch switching for MICI, force update buttons for TICI/MICI
This commit is contained in:
@@ -58,6 +58,7 @@ class SoftwareLayout(Widget):
|
||||
self._onroad_label = ListItem(lambda: tr("Updates are only downloaded while the car is off."))
|
||||
self._version_item = text_item(lambda: tr("Current Version"), ui_state.params.get("UpdaterCurrentDescription") or "")
|
||||
self._download_btn = button_item(lambda: tr("Download"), lambda: tr("CHECK"), callback=self._on_download_update)
|
||||
self._force_download_btn = button_item(lambda: tr("Force Download"), lambda: tr("FORCE"), callback=self._on_force_download)
|
||||
|
||||
# Install button is initially hidden
|
||||
self._install_btn = button_item(lambda: tr("Install Update"), lambda: tr("INSTALL"), callback=self._on_install_update)
|
||||
@@ -76,6 +77,7 @@ class SoftwareLayout(Widget):
|
||||
self._onroad_label,
|
||||
self._version_item,
|
||||
self._download_btn,
|
||||
self._force_download_btn,
|
||||
self._install_btn,
|
||||
self._branch_btn,
|
||||
button_item(lambda: tr("Uninstall"), lambda: tr("UNINSTALL"), callback=self._on_uninstall),
|
||||
@@ -100,6 +102,7 @@ class SoftwareLayout(Widget):
|
||||
|
||||
# Update download button visibility and state
|
||||
self._download_btn.set_visible(ui_state.is_offroad())
|
||||
self._force_download_btn.set_visible(ui_state.is_offroad())
|
||||
|
||||
updater_state = ui_state.params.get("UpdaterState") or "idle"
|
||||
failed_count = ui_state.params.get("UpdateFailedCount") or 0
|
||||
@@ -110,6 +113,7 @@ class SoftwareLayout(Widget):
|
||||
# Updater responded
|
||||
self._waiting_for_updater = False
|
||||
self._download_btn.action_item.set_enabled(False)
|
||||
self._force_download_btn.action_item.set_enabled(False)
|
||||
# Use the mapping, with a fallback to the original state string
|
||||
display_text = STATE_TO_DISPLAY_TEXT.get(updater_state, updater_state)
|
||||
self._download_btn.action_item.set_value(display_text)
|
||||
@@ -135,6 +139,7 @@ class SoftwareLayout(Widget):
|
||||
|
||||
# Only enable if we're not waiting for updater to flip out of idle
|
||||
self._download_btn.action_item.set_enabled(not self._waiting_for_updater)
|
||||
self._force_download_btn.action_item.set_enabled(not self._waiting_for_updater)
|
||||
|
||||
# Update target branch button value
|
||||
current_branch = ui_state.params.get("UpdaterTargetBranch") or ""
|
||||
@@ -180,6 +185,12 @@ class SoftwareLayout(Widget):
|
||||
self._install_btn.action_item.set_enabled(False)
|
||||
ui_state.params.put_bool("DoReboot", True)
|
||||
|
||||
def _on_force_download(self):
|
||||
self._force_download_btn.action_item.set_enabled(False)
|
||||
self._waiting_for_updater = True
|
||||
self._waiting_start_ts = time.monotonic()
|
||||
os.system("pkill -SIGUSR2 -f system.updated.updated")
|
||||
|
||||
def _on_select_branch(self):
|
||||
# Get available branches and order
|
||||
current_git_branch = ui_state.params.get("GitBranch") or ""
|
||||
|
||||
@@ -15,7 +15,8 @@ from openpilot.selfdrive.ui.mici.onroad.driver_camera_dialog import DriverCamera
|
||||
from openpilot.selfdrive.ui.mici.layouts.onboarding import TrainingGuide, TermsPage
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos
|
||||
from openpilot.system.ui.lib.multilang import tr
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets import Widget, DialogResult
|
||||
from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog
|
||||
from openpilot.selfdrive.ui.ui_state import device, ui_state
|
||||
from openpilot.system.ui.widgets.label import UnifiedLabel
|
||||
from openpilot.system.ui.widgets.html_render import HtmlModal, HtmlRenderer
|
||||
@@ -167,6 +168,42 @@ class PairBigButton(BigButton):
|
||||
UPDATER_TIMEOUT = 10.0 # seconds to wait for updater to respond
|
||||
|
||||
|
||||
class TargetBranchBigButton(BigButton):
|
||||
def __init__(self):
|
||||
super().__init__("target branch", "", gui_app.texture("icons_mici/settings/device/update.png", 64, 75), scroll=True)
|
||||
self._branch_dialog: MultiOptionDialog | None = None
|
||||
self.set_click_callback(self._on_select_branch)
|
||||
|
||||
def _on_select_branch(self):
|
||||
current_git_branch = ui_state.params.get("GitBranch") or ""
|
||||
branches_str = ui_state.params.get("UpdaterAvailableBranches") or ""
|
||||
branches = [b for b in branches_str.split(",") if b]
|
||||
|
||||
for b in [current_git_branch, "devel-staging", "devel", "nightly", "nightly-dev", "master"]:
|
||||
if b in branches:
|
||||
branches.remove(b)
|
||||
branches.insert(0, b)
|
||||
|
||||
current_target = ui_state.params.get("UpdaterTargetBranch") or ""
|
||||
|
||||
def handle_selection(result: DialogResult):
|
||||
if result == DialogResult.CONFIRM and self._branch_dialog is not None and self._branch_dialog.selection:
|
||||
selection = self._branch_dialog.selection
|
||||
ui_state.params.put("UpdaterTargetBranch", selection)
|
||||
os.system("pkill -SIGUSR1 -f system.updated.updated")
|
||||
self.set_value(selection)
|
||||
self._branch_dialog = None
|
||||
|
||||
self._branch_dialog = MultiOptionDialog(tr("Select a branch"), branches, current_target, callback=handle_selection)
|
||||
gui_app.push_widget(self._branch_dialog)
|
||||
|
||||
def _update_state(self):
|
||||
super()._update_state()
|
||||
current_branch = ui_state.params.get("UpdaterTargetBranch") or ui_state.params.get("GitBranch") or ""
|
||||
if self.get_value() != current_branch:
|
||||
self.set_value(current_branch)
|
||||
|
||||
|
||||
class UpdateOpenpilotBigButton(BigButton):
|
||||
def __init__(self):
|
||||
self._txt_update_icon = gui_app.texture("icons_mici/settings/device/update.png", 64, 75)
|
||||
@@ -285,6 +322,84 @@ class UpdateOpenpilotBigButton(BigButton):
|
||||
self._waiting_for_updater_t = None
|
||||
|
||||
|
||||
class ForceDownloadBigButton(BigButton):
|
||||
def __init__(self):
|
||||
self._txt_update_icon = gui_app.texture("icons_mici/settings/device/update.png", 64, 75)
|
||||
super().__init__("force download", "", self._txt_update_icon)
|
||||
|
||||
self._waiting_for_updater_t: float | None = None
|
||||
self._hide_value_t: float | None = None
|
||||
self._state: UpdaterState = UpdaterState.IDLE
|
||||
|
||||
ui_state.add_offroad_transition_callback(self.offroad_transition)
|
||||
|
||||
def offroad_transition(self):
|
||||
if ui_state.is_offroad():
|
||||
self.set_enabled(True)
|
||||
|
||||
def _handle_mouse_release(self, mouse_pos: MousePos):
|
||||
super()._handle_mouse_release(mouse_pos)
|
||||
|
||||
if not system_time_valid():
|
||||
dlg = BigDialog("", tr("Please connect to Wi-Fi to update."))
|
||||
gui_app.push_widget(dlg)
|
||||
return
|
||||
|
||||
self.set_enabled(False)
|
||||
self._state = UpdaterState.WAITING_FOR_UPDATER
|
||||
self.set_icon(self._txt_update_icon)
|
||||
|
||||
def run():
|
||||
os.system("pkill -SIGUSR2 -f system.updated.updated")
|
||||
|
||||
threading.Thread(target=run, daemon=True).start()
|
||||
|
||||
def _update_state(self):
|
||||
super()._update_state()
|
||||
|
||||
if ui_state.started:
|
||||
self.set_enabled(False)
|
||||
return
|
||||
|
||||
updater_state = ui_state.params.get("UpdaterState") or ""
|
||||
|
||||
if self._state == UpdaterState.WAITING_FOR_UPDATER:
|
||||
self.set_rotate_icon(True)
|
||||
if updater_state != "idle":
|
||||
self._state = UpdaterState.UPDATER_RESPONDING
|
||||
|
||||
if self._waiting_for_updater_t is None:
|
||||
self._waiting_for_updater_t = rl.get_time()
|
||||
|
||||
if self._waiting_for_updater_t is not None and rl.get_time() - self._waiting_for_updater_t > UPDATER_TIMEOUT:
|
||||
self.set_rotate_icon(False)
|
||||
self.set_value("updater failed\nto respond")
|
||||
self._state = UpdaterState.IDLE
|
||||
self._hide_value_t = rl.get_time()
|
||||
|
||||
elif self._state == UpdaterState.UPDATER_RESPONDING:
|
||||
if updater_state == "idle":
|
||||
self.set_rotate_icon(False)
|
||||
self._state = UpdaterState.IDLE
|
||||
self._hide_value_t = rl.get_time()
|
||||
elif self.get_value() != updater_state:
|
||||
self.set_value(updater_state)
|
||||
|
||||
elif self._state == UpdaterState.IDLE:
|
||||
self.set_rotate_icon(False)
|
||||
self.set_enabled(True)
|
||||
|
||||
if self._hide_value_t is not None:
|
||||
if rl.get_time() - self._hide_value_t > 3.0:
|
||||
self._hide_value_t = None
|
||||
self.set_value("")
|
||||
elif self.get_value() != "":
|
||||
self.set_value("")
|
||||
|
||||
if self._state != UpdaterState.WAITING_FOR_UPDATER:
|
||||
self._waiting_for_updater_t = None
|
||||
|
||||
|
||||
class DeviceLayoutMici(NavScroller):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -339,7 +454,9 @@ class DeviceLayoutMici(NavScroller):
|
||||
|
||||
self._scroller.add_widgets([
|
||||
DeviceInfoLayoutMici(),
|
||||
TargetBranchBigButton(),
|
||||
UpdateOpenpilotBigButton(),
|
||||
ForceDownloadBigButton(),
|
||||
PairBigButton(),
|
||||
review_training_guide_btn,
|
||||
driver_cam_btn,
|
||||
|
||||
@@ -42,6 +42,7 @@ class UserRequest:
|
||||
NONE = 0
|
||||
CHECK = 1
|
||||
FETCH = 2
|
||||
FORCE_FETCH = 3
|
||||
|
||||
class WaitTimeHelper:
|
||||
def __init__(self):
|
||||
@@ -49,6 +50,7 @@ class WaitTimeHelper:
|
||||
self.user_request = UserRequest.NONE
|
||||
signal.signal(signal.SIGHUP, self.update_now)
|
||||
signal.signal(signal.SIGUSR1, self.check_now)
|
||||
signal.signal(signal.SIGUSR2, self.force_update_now)
|
||||
|
||||
def update_now(self, signum: int, frame) -> None:
|
||||
cloudlog.info("caught SIGHUP, attempting to downloading update")
|
||||
@@ -60,6 +62,11 @@ class WaitTimeHelper:
|
||||
self.user_request = UserRequest.CHECK
|
||||
self.ready_event.set()
|
||||
|
||||
def force_update_now(self, signum: int, frame) -> None:
|
||||
cloudlog.info("caught SIGUSR2, attempting forced update download")
|
||||
self.user_request = UserRequest.FORCE_FETCH
|
||||
self.ready_event.set()
|
||||
|
||||
def sleep(self, t: float) -> None:
|
||||
self.ready_event.wait(timeout=t)
|
||||
|
||||
@@ -359,7 +366,7 @@ class Updater:
|
||||
else:
|
||||
cloudlog.info(f"up to date on {cur_branch} ({str(cur_commit)[:7]})")
|
||||
|
||||
def fetch_update(self) -> None:
|
||||
def fetch_update(self, force: bool = False) -> None:
|
||||
cloudlog.info("attempting git fetch inside staging overlay")
|
||||
|
||||
self.params.put("UpdaterState", "downloading...")
|
||||
@@ -373,7 +380,10 @@ class Updater:
|
||||
run(["git", "config", "--replace-all", "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"], OVERLAY_MERGED)
|
||||
|
||||
branch = self.target_branch
|
||||
git_fetch_output = run(["git", "fetch", "origin", branch], OVERLAY_MERGED)
|
||||
fetch_cmd = ["git", "fetch", "origin", branch]
|
||||
if force:
|
||||
fetch_cmd = ["git", "fetch", "--prune", "origin", branch]
|
||||
git_fetch_output = run(fetch_cmd, OVERLAY_MERGED)
|
||||
cloudlog.info("git fetch success: %s", git_fetch_output)
|
||||
|
||||
cloudlog.info("git reset in progress")
|
||||
@@ -382,10 +392,22 @@ class Updater:
|
||||
["git", "branch", "--set-upstream-to", f"origin/{branch}"],
|
||||
["git", "reset", "--hard"],
|
||||
["git", "clean", "-xdff"],
|
||||
["git", "submodule", "sync"],
|
||||
["git", "submodule", "update", "--init", "--recursive"],
|
||||
["git", "submodule", "foreach", "--recursive", "git", "reset", "--hard"],
|
||||
]
|
||||
if force:
|
||||
cmds += [
|
||||
["git", "submodule", "sync", "--recursive"],
|
||||
["git", "submodule", "deinit", "--force", "--all"],
|
||||
["git", "clean", "-xdff"],
|
||||
["git", "submodule", "update", "--init", "--force", "--recursive"],
|
||||
["git", "submodule", "foreach", "--recursive", "git", "reset", "--hard"],
|
||||
["git", "submodule", "foreach", "--recursive", "git", "clean", "-xdff"],
|
||||
]
|
||||
else:
|
||||
cmds += [
|
||||
["git", "submodule", "sync"],
|
||||
["git", "submodule", "update", "--init", "--recursive"],
|
||||
["git", "submodule", "foreach", "--recursive", "git", "reset", "--hard"],
|
||||
]
|
||||
r = [run(cmd, OVERLAY_MERGED) for cmd in cmds]
|
||||
cloudlog.info("git reset success: %s", '\n'.join(r))
|
||||
|
||||
@@ -463,13 +485,13 @@ def main() -> None:
|
||||
# download update
|
||||
last_fetch = params.get("UpdaterLastFetchTime")
|
||||
timed_out = last_fetch is None or (datetime.datetime.now(datetime.UTC).replace(tzinfo=None) - last_fetch > datetime.timedelta(days=3))
|
||||
user_requested_fetch = wait_helper.user_request == UserRequest.FETCH
|
||||
user_requested_fetch = wait_helper.user_request in (UserRequest.FETCH, UserRequest.FORCE_FETCH)
|
||||
if params.get_bool("NetworkMetered") and not timed_out and not user_requested_fetch:
|
||||
cloudlog.info("skipping fetch, connection metered")
|
||||
elif wait_helper.user_request == UserRequest.CHECK:
|
||||
cloudlog.info("skipping fetch, only checking")
|
||||
else:
|
||||
updater.fetch_update()
|
||||
updater.fetch_update(force=(wait_helper.user_request == UserRequest.FORCE_FETCH))
|
||||
write_time_to_param(params, "UpdaterLastFetchTime")
|
||||
update_failed_count = 0
|
||||
except subprocess.CalledProcessError as e:
|
||||
|
||||
Reference in New Issue
Block a user