Automatic updates
This commit is contained in:
@@ -71,3 +71,42 @@ def update_boot_logo(frogpilot=False, stock=False):
|
||||
run_cmd(["sudo", "mount", "-o", "remount,rw", "/"], "Successfully remounted / as read-write", "Failed to remount /")
|
||||
run_cmd(["sudo", "cp", target_logo, boot_logo_location], "Successfully replaced boot logo", "Failed to replace boot logo")
|
||||
run_cmd(["sudo", "mount", "-o", f"remount,{mount_options}", "/"], "Successfully restored / mount options", "Failed to restore / mount options")
|
||||
|
||||
|
||||
def update_openpilot(thread_manager, params):
|
||||
def update_available():
|
||||
run_cmd(["pkill", "-SIGUSR1", "-f", "system.updated.updated"], "Checking for updates...", "Failed to check for update...", report=False)
|
||||
|
||||
while params.get("UpdaterState") != "checking...":
|
||||
time.sleep(1)
|
||||
|
||||
while params.get("UpdaterState") == "checking...":
|
||||
time.sleep(1)
|
||||
|
||||
if not params.get_bool("UpdaterFetchAvailable"):
|
||||
return False
|
||||
|
||||
while params.get_bool("IsOnroad") or thread_manager.is_thread_alive("lock_doors"):
|
||||
time.sleep(60)
|
||||
|
||||
run_cmd(["pkill", "-SIGHUP", "-f", "system.updated.updated"], "Update available, downloading...", "Failed to download update...", report=False)
|
||||
|
||||
while not params.get_bool("UpdateAvailable"):
|
||||
time.sleep(60)
|
||||
|
||||
return True
|
||||
|
||||
if params.get("UpdaterState") != "idle":
|
||||
return
|
||||
|
||||
while params.get_bool("IsOnroad") or thread_manager.is_thread_alive("lock_doors"):
|
||||
time.sleep(60)
|
||||
|
||||
if not update_available():
|
||||
return
|
||||
|
||||
while True:
|
||||
if not update_available():
|
||||
break
|
||||
|
||||
HARDWARE.reboot()
|
||||
|
||||
@@ -9,6 +9,7 @@ from openpilot.common.realtime import DT_MDL, Priority, Ratekeeper, config_realt
|
||||
from openpilot.common.time_helpers import system_time_valid
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_backups import backup_toggles
|
||||
from openpilot.frogpilot.common.frogpilot_functions import update_openpilot
|
||||
from openpilot.frogpilot.common.frogpilot_utilities import ThreadManager, is_url_pingable
|
||||
from openpilot.frogpilot.common.frogpilot_variables import FrogPilotVariables
|
||||
from openpilot.frogpilot.controls.frogpilot_planner import FrogPilotPlanner
|
||||
@@ -31,6 +32,9 @@ def update_checks(now, thread_manager, params, params_memory, frogpilot_toggles,
|
||||
while not (is_url_pingable("https://github.com") or is_url_pingable("https://gitlab.com")):
|
||||
time.sleep(60)
|
||||
|
||||
if frogpilot_toggles.automatic_updates:
|
||||
thread_manager.run_with_lock(update_openpilot, (thread_manager, params))
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
def update_toggles(frogpilot_variables, started, thread_manager, time_validated, params):
|
||||
@@ -102,6 +106,7 @@ def frogpilot_thread():
|
||||
if params_memory.get_bool("FrogPilotTogglesUpdated"):
|
||||
frogpilot_toggles = update_toggles(frogpilot_variables, started, thread_manager, time_validated, params)
|
||||
|
||||
run_update_checks |= params_memory.get_bool("ManualUpdateInitiated")
|
||||
run_update_checks |= now.second == 0 and (now.minute % 60 == 0)
|
||||
run_update_checks &= time_validated
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
struct FrogPilotUIScene {
|
||||
bool always_on_lateral_active;
|
||||
bool downloading_update;
|
||||
bool enabled;
|
||||
bool frogpilot_panel_active;
|
||||
bool online;
|
||||
|
||||
@@ -21,7 +21,7 @@ void SoftwarePanel::checkForUpdates() {
|
||||
}
|
||||
|
||||
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
||||
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off."));
|
||||
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off or in park."));
|
||||
onroadLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;");
|
||||
addItem(onroadLbl);
|
||||
|
||||
@@ -29,6 +29,12 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
||||
versionLbl = new LabelControl(tr("Current Version"), "");
|
||||
addItem(versionLbl);
|
||||
|
||||
// automatic updates toggle
|
||||
ParamControl *automaticUpdatesToggle = new ParamControl("AutomaticUpdates", tr("Automatically Update FrogPilot"),
|
||||
tr("Automatically update FrogPilot when the vehicle is parked with an active internet connection."), "");
|
||||
automaticUpdatesToggle->setVisible(params.getBool("IsReleaseBranch"));
|
||||
addItem(automaticUpdatesToggle);
|
||||
|
||||
// download update btn
|
||||
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
|
||||
connect(downloadBtn, &ButtonControl::clicked, [=]() {
|
||||
@@ -38,6 +44,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
||||
} else {
|
||||
std::system("pkill -SIGHUP -f system.updated.updated");
|
||||
}
|
||||
frogpilotUIState()->params_memory.putBool("ManualUpdateInitiated", true);
|
||||
});
|
||||
addItem(downloadBtn);
|
||||
|
||||
@@ -110,6 +117,10 @@ void SoftwarePanel::showEvent(QShowEvent *event) {
|
||||
// FrogPilot variables
|
||||
FrogPilotUIState &fs = *frogpilotUIState();
|
||||
FrogPilotUIScene &frogpilot_scene = fs.frogpilot_scene;
|
||||
|
||||
if (frogpilot_scene.online && params.get("UpdaterState") == "idle") {
|
||||
checkForUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwarePanel::updateLabels() {
|
||||
@@ -117,6 +128,8 @@ void SoftwarePanel::updateLabels() {
|
||||
FrogPilotUIState &fs = *frogpilotUIState();
|
||||
FrogPilotUIScene &frogpilot_scene = fs.frogpilot_scene;
|
||||
|
||||
bool parked = frogpilot_scene.parked;
|
||||
|
||||
// add these back in case the files got removed
|
||||
fs_watch->addParam("LastUpdateTime");
|
||||
fs_watch->addParam("UpdateFailedCount");
|
||||
@@ -125,12 +138,13 @@ void SoftwarePanel::updateLabels() {
|
||||
|
||||
if (!isVisible()) {
|
||||
// FrogPilot variables
|
||||
frogpilot_scene.downloading_update = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// updater only runs offroad
|
||||
onroadLbl->setVisible(is_onroad);
|
||||
downloadBtn->setVisible(!is_onroad);
|
||||
// updater only runs offroad or when parked
|
||||
onroadLbl->setVisible(is_onroad && !parked);
|
||||
downloadBtn->setVisible(!is_onroad || parked);
|
||||
|
||||
// download update
|
||||
QString updater_state = QString::fromStdString(params.get("UpdaterState"));
|
||||
@@ -140,6 +154,7 @@ void SoftwarePanel::updateLabels() {
|
||||
downloadBtn->setValue(updater_state);
|
||||
|
||||
// FrogPilot variables
|
||||
frogpilot_scene.downloading_update = true;
|
||||
} else {
|
||||
if (failed) {
|
||||
downloadBtn->setText(tr("CHECK"));
|
||||
@@ -159,6 +174,7 @@ void SoftwarePanel::updateLabels() {
|
||||
downloadBtn->setEnabled(true);
|
||||
|
||||
// FrogPilot variables
|
||||
frogpilot_scene.downloading_update = false;
|
||||
}
|
||||
targetBranchBtn->setValue(QString::fromStdString(params.get("UpdaterTargetBranch")));
|
||||
|
||||
@@ -166,7 +182,7 @@ void SoftwarePanel::updateLabels() {
|
||||
versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription")));
|
||||
versionLbl->setDescription(QString::fromStdString(params.get("UpdaterCurrentReleaseNotes")));
|
||||
|
||||
installBtn->setVisible(!is_onroad && params.getBool("UpdateAvailable"));
|
||||
installBtn->setVisible((!is_onroad || parked) && params.getBool("UpdateAvailable"));
|
||||
installBtn->setValue(QString::fromStdString(params.get("UpdaterNewDescription")));
|
||||
installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes")));
|
||||
|
||||
|
||||
+1
-1
@@ -154,7 +154,7 @@ void UIState::update() {
|
||||
FrogPilotUIScene &frogpilot_scene = fs->frogpilot_scene;
|
||||
QJsonObject &frogpilot_toggles = frogpilot_scene.frogpilot_toggles;
|
||||
|
||||
if (frogpilot_scene.frogpilot_panel_active) {
|
||||
if (frogpilot_scene.downloading_update || frogpilot_scene.frogpilot_panel_active) {
|
||||
device()->resetInteractiveTimeout();
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ procs = [
|
||||
PythonProcess("radard", "selfdrive.controls.radard", only_onroad),
|
||||
PythonProcess("hardwared", "system.hardware.hardwared", always_run),
|
||||
PythonProcess("tombstoned", "system.tombstoned", always_run, enabled=not PC),
|
||||
PythonProcess("updated", "system.updated.updated", only_offroad, enabled=not PC),
|
||||
PythonProcess("updated", "system.updated.updated", always_run, enabled=not PC),
|
||||
PythonProcess("uploader", "system.loggerd.uploader", always_run),
|
||||
PythonProcess("statsd", "system.statsd", always_run),
|
||||
PythonProcess("feedbackd", "selfdrive.ui.feedback.feedbackd", only_onroad),
|
||||
|
||||
+20
-18
@@ -422,10 +422,6 @@ class Updater:
|
||||
def main() -> None:
|
||||
params = Params()
|
||||
|
||||
if params.get_bool("DisableUpdates"):
|
||||
cloudlog.warning("updates are disabled by the DisableUpdates param")
|
||||
exit(0)
|
||||
|
||||
with open(LOCK_FILE, 'w') as ov_lock_fd:
|
||||
try:
|
||||
fcntl.flock(ov_lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
@@ -467,6 +463,9 @@ def main() -> None:
|
||||
# FrogPilot variables
|
||||
frogpilot_toggles = get_frogpilot_toggles()
|
||||
|
||||
manual_update_requested = params_memory.get_bool("ManualUpdateInitiated")
|
||||
params_memory.remove("ManualUpdateInitiated")
|
||||
|
||||
# Attempt an update
|
||||
exception = None
|
||||
try:
|
||||
@@ -483,21 +482,24 @@ def main() -> None:
|
||||
|
||||
update_failed_count += 1
|
||||
|
||||
# check for update
|
||||
params.put("UpdaterState", "checking...")
|
||||
updater.check_for_update()
|
||||
if manual_update_requested or params.get_bool("IsOffroad"):
|
||||
# check for update
|
||||
params.put("UpdaterState", "checking...")
|
||||
updater.check_for_update()
|
||||
|
||||
# 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
|
||||
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")
|
||||
# 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
|
||||
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()
|
||||
write_time_to_param(params, "UpdaterLastFetchTime")
|
||||
else:
|
||||
updater.fetch_update()
|
||||
write_time_to_param(params, "UpdaterLastFetchTime")
|
||||
cloudlog.info("skipping fetch, vehicle is onroad")
|
||||
update_failed_count = 0
|
||||
except subprocess.CalledProcessError as e:
|
||||
cloudlog.event(
|
||||
@@ -522,7 +524,7 @@ def main() -> None:
|
||||
|
||||
# infrequent attempts if we successfully updated recently
|
||||
wait_helper.user_request = UserRequest.NONE
|
||||
wait_helper.sleep(5*60 if update_failed_count > 0 else 1.5*60*60)
|
||||
wait_helper.sleep(60*60*24*365*100)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user