diff --git a/selfdrive/thermald/thermald.py b/selfdrive/thermald/thermald.py index eb3e594269..db7f2ab56c 100755 --- a/selfdrive/thermald/thermald.py +++ b/selfdrive/thermald/thermald.py @@ -22,15 +22,13 @@ from selfdrive.hardware import EON, TICI, PC, HARDWARE from selfdrive.loggerd.config import get_available_percent from selfdrive.swaglog import cloudlog from selfdrive.thermald.power_monitoring import PowerMonitoring -from selfdrive.version import get_tested_branch, terms_version, training_version +from selfdrive.version import terms_version, training_version ThermalStatus = log.DeviceState.ThermalStatus NetworkType = log.DeviceState.NetworkType NetworkStrength = log.DeviceState.NetworkStrength CURRENT_TAU = 15. # 15s time constant TEMP_TAU = 5. # 5s time constant -DAYS_NO_CONNECTIVITY_MAX = 14 # do not allow to engage after this many days -DAYS_NO_CONNECTIVITY_PROMPT = 10 # send an offroad prompt after this many days DISCONNECT_TIMEOUT = 5. # wait 5 seconds before going offroad after disconnect so you get an alert ThermalBand = namedtuple("ThermalBand", ['min_temp', 'max_temp']) @@ -324,47 +322,11 @@ def thermald_thread(): # **** starting logic **** - # Check for last update time and display alerts if needed + # Ensure date/time are valid now = datetime.datetime.utcnow() - - # show invalid date/time alert startup_conditions["time_valid"] = (now.year > 2020) or (now.year == 2020 and now.month >= 10) set_offroad_alert_if_changed("Offroad_InvalidTime", (not startup_conditions["time_valid"])) - # Show update prompt - try: - last_update = datetime.datetime.fromisoformat(params.get("LastUpdateTime", encoding='utf8')) - except (TypeError, ValueError): - last_update = now - dt = now - last_update - - update_failed_count = params.get("UpdateFailedCount") - update_failed_count = 0 if update_failed_count is None else int(update_failed_count) - last_update_exception = params.get("LastUpdateException", encoding='utf8') - - if update_failed_count > 15 and last_update_exception is not None: - if get_tested_branch(): - extra_text = "Ensure the software is correctly installed" - else: - extra_text = last_update_exception - - set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False) - set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False) - set_offroad_alert_if_changed("Offroad_UpdateFailed", True, extra_text=extra_text) - elif dt.days > DAYS_NO_CONNECTIVITY_MAX and update_failed_count > 1: - set_offroad_alert_if_changed("Offroad_UpdateFailed", False) - set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False) - set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", True) - elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT: - remaining = max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 1) - set_offroad_alert_if_changed("Offroad_UpdateFailed", False) - set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False) - set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining} day{'' if remaining == 1 else 's'}.") - else: - set_offroad_alert_if_changed("Offroad_UpdateFailed", False) - set_offroad_alert_if_changed("Offroad_ConnectivityNeeded", False) - set_offroad_alert_if_changed("Offroad_ConnectivityNeededPrompt", False) - startup_conditions["up_to_date"] = params.get("Offroad_ConnectivityNeeded") is None or params.get_bool("DisableUpdates") or params.get_bool("SnoozeUpdate") startup_conditions["not_uninstalling"] = not params.get_bool("DoUninstall") startup_conditions["accepted_terms"] = params.get("HasAcceptedTerms") == terms_version diff --git a/selfdrive/updated.py b/selfdrive/updated.py index 78760bea44..0a203e3cd0 100755 --- a/selfdrive/updated.py +++ b/selfdrive/updated.py @@ -40,6 +40,7 @@ from common.params import Params from selfdrive.hardware import EON, TICI, HARDWARE from selfdrive.swaglog import cloudlog from selfdrive.controls.lib.alertmanager import set_offroad_alert +from selfdrive.version import get_tested_branch LOCK_FILE = os.getenv("UPDATER_LOCK_FILE", "/tmp/safe_staging_overlay.lock") STAGING_ROOT = os.getenv("UPDATER_STAGING_ROOT", "/data/safe_staging") @@ -51,6 +52,8 @@ OVERLAY_METADATA = os.path.join(STAGING_ROOT, "metadata") OVERLAY_MERGED = os.path.join(STAGING_ROOT, "merged") FINALIZED = os.path.join(STAGING_ROOT, "finalized") +DAYS_NO_CONNECTIVITY_MAX = 14 # do not allow to engage after this many days +DAYS_NO_CONNECTIVITY_PROMPT = 10 # send an offroad prompt after this many days class WaitTimeHelper: def __init__(self, proc): @@ -102,15 +105,24 @@ def set_params(new_version: bool, failed_count: int, exception: Optional[str]) - params = Params() params.put("UpdateFailedCount", str(failed_count)) + + last_update = datetime.datetime.utcnow() if failed_count == 0: - t = datetime.datetime.utcnow().isoformat() + t = last_update.isoformat() params.put("LastUpdateTime", t.encode('utf8')) + else: + try: + t = params.get("LastUpdateTime", encoding='utf8') + last_update = datetime.datetime.fromisoformat(t) + except (TypeError, ValueError): + pass if exception is None: params.delete("LastUpdateException") else: params.put("LastUpdateException", exception) + # Write out release notes for new versions if new_version: try: with open(os.path.join(FINALIZED, "RELEASES.md"), "rb") as f: @@ -123,6 +135,24 @@ def set_params(new_version: bool, failed_count: int, exception: Optional[str]) - params.put("ReleaseNotes", "") params.put_bool("UpdateAvailable", True) + # Handle user prompt + for alert in ("Offroad_UpdateFailed", "Offroad_ConnectivityNeeded", "Offroad_ConnectivityNeededPrompt"): + set_offroad_alert(alert, False) + + now = datetime.datetime.utcnow() + dt = now - last_update + if failed_count > 15 and exception is not None: + if get_tested_branch(): + extra_text = "Ensure the software is correctly installed" + else: + extra_text = exception + set_offroad_alert("Offroad_UpdateFailed", True, extra_text=extra_text) + elif dt.days > DAYS_NO_CONNECTIVITY_MAX and failed_count > 1: + set_offroad_alert("Offroad_ConnectivityNeeded", True) + elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT: + remaining = max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 1) + set_offroad_alert("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining} day{'' if remaining == 1 else 's'}.") + def setup_git_options(cwd: str) -> None: # We sync FS object atimes (which NEOS doesn't use) and mtimes, but ctimes @@ -344,16 +374,14 @@ def main(): params = Params() if params.get_bool("DisableUpdates"): - raise RuntimeError("updates are disabled by the DisableUpdates param") - - if EON and os.geteuid() != 0: - raise RuntimeError("updated must be launched as root!") + cloudlog.warning("updates are disabled by the DisableUpdates param") + exit(0) ov_lock_fd = open(LOCK_FILE, 'w') try: fcntl.flock(ov_lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError as e: - raise RuntimeError("couldn't get overlay lock; is another updated running?") from e + raise RuntimeError("couldn't get overlay lock; is another instance running?") from e # Set low io priority proc = psutil.Process() @@ -368,10 +396,6 @@ def main(): t = datetime.datetime.utcnow().isoformat() params.put("InstallDate", t.encode('utf8')) - # Wait for IsOffroad to be set before our first update attempt - wait_helper = WaitTimeHelper(proc) - wait_helper.sleep(30) - overlay_init = Path(os.path.join(BASEDIR, ".overlay_init")) overlay_init.unlink(missing_ok=True) @@ -379,6 +403,13 @@ def main(): last_fetch_time = 0 update_failed_count = 0 + # Set initial params for offroad alerts + set_params(False, 0, None) + + # Wait for IsOffroad to be set before our first update attempt + wait_helper = WaitTimeHelper(proc) + wait_helper.sleep(30) + # Run the update loop # * every 1m, do a lightweight internet/update check # * every 10m, do a full git fetch