Automatic updates scheduler

This commit is contained in:
FrogAi
2024-01-12 22:39:30 -07:00
parent 42b3636995
commit 9ded0c1c3a
4 changed files with 127 additions and 1 deletions

View File

@@ -103,4 +103,12 @@ private:
Params params;
ParamWatcher *fs_watch;
// FrogPilot variables
void automaticUpdate();
ButtonControl *updateTime;
int schedule;
int time;
};

View File

@@ -2,6 +2,8 @@
#include <cassert>
#include <cmath>
#include <iomanip>
#include <sstream>
#include <string>
#include <QDebug>
@@ -29,6 +31,40 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
versionLbl = new LabelControl(tr("Current Version"), "");
addItem(versionLbl);
// update scheduler
std::vector<QString> scheduleOptions{tr("Manually"), tr("Daily"), tr("Weekly")};
ButtonParamControl *preferredSchedule = new ButtonParamControl("UpdateSchedule", tr("Update Scheduler"),
tr("Choose the update frequency for FrogPilot's automatic updates.\n\n"
"This feature will handle the download, installation, and device reboot for a seamless 'Set and Forget' experience.\n\n"
"Weekly updates start at midnight every Sunday."),
"",
scheduleOptions);
schedule = params.getInt("UpdateSchedule");
addItem(preferredSchedule);
updateTime = new ButtonControl(tr("Update Time"), tr("SELECT"));
QStringList hours;
for (int h = 0; h < 24; h++) {
int displayHour = (h % 12 == 0) ? 12 : h % 12;
QString meridiem = (h < 12) ? "AM" : "PM";
hours << QString("%1:00 %2").arg(displayHour).arg(meridiem)
<< QString("%1:30 %2").arg(displayHour).arg(meridiem);
}
QObject::connect(updateTime, &ButtonControl::clicked, [=]() {
int currentHourIndex = params.getInt("UpdateTime");
QString currentHourLabel = hours[currentHourIndex];
QString selection = MultiOptionDialog::getSelection(tr("Select a time to automatically update"), hours, currentHourLabel, this);
if (!selection.isEmpty()) {
int selectedHourIndex = hours.indexOf(selection);
params.putInt("UpdateTime", selectedHourIndex);
updateTime->setValue(selection);
}
});
time = params.getInt("UpdateTime");
updateTime->setValue(hours[time]);
addItem(updateTime);
// download update btn
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
connect(downloadBtn, &ButtonControl::clicked, [=]() {
@@ -85,6 +121,8 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
fs_watch = new ParamWatcher(this);
QObject::connect(fs_watch, &ParamWatcher::paramChanged, [=](const QString &param_name, const QString &param_value) {
schedule = params.getInt("UpdateSchedule");
time = params.getInt("UpdateTime");
updateLabels();
});
@@ -93,6 +131,8 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
updateLabels();
});
QObject::connect(uiState(), &UIState::uiUpdate, this, &SoftwarePanel::automaticUpdate);
updateLabels();
}
@@ -110,6 +150,9 @@ void SoftwarePanel::updateLabels() {
fs_watch->addParam("UpdaterState");
fs_watch->addParam("UpdateAvailable");
fs_watch->addParam("UpdateSchedule");
fs_watch->addParam("UpdateTime");
if (!isVisible()) {
return;
}
@@ -152,5 +195,78 @@ void SoftwarePanel::updateLabels() {
installBtn->setValue(QString::fromStdString(params.get("UpdaterNewDescription")));
installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes")));
updateTime->setVisible(params.getInt("UpdateSchedule"));
update();
}
void SoftwarePanel::automaticUpdate() {
static int timer = 0;
static std::chrono::system_clock::time_point lastOffroadTime;
if (!is_onroad) {
timer = (timer == 0) ? 0 : std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - lastOffroadTime).count();
lastOffroadTime = std::chrono::system_clock::now();
} else {
timer = 0;
}
bool isWifiConnected = (*uiState()->sm)["deviceState"].getDeviceState().getNetworkType() == cereal::DeviceState::NetworkType::WIFI;
if (schedule == 0 || is_onroad || !isWifiConnected || isVisible()) return;
static bool isDownloadCompleted = false;
if (isDownloadCompleted && params.getBool("UpdateAvailable")) {
params.putBool(true);
return;
}
int updateHour = time / 2;
int updateMinute = (time % 2) * 30;
if (updateHour >= 1 && updateHour <= 11 && time >= 24) {
updateHour += 12;
} else if (updateHour == 12 && time < 24) {
updateHour = 0;
}
std::time_t currentTimeT = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::tm now = *std::localtime(&currentTimeT);
static bool updateCheckedToday = false;
static int lastCheckedDay = now.tm_yday;
if (lastCheckedDay != now.tm_yday) {
updateCheckedToday = false;
lastCheckedDay = now.tm_yday;
}
if (now.tm_hour != updateHour || now.tm_min < updateMinute) return;
std::string lastUpdateStr = params.get("Updated");
std::tm lastUpdate = {};
std::istringstream iss(lastUpdateStr);
if (iss >> std::get_time(&lastUpdate, "%Y-%m-%d %H:%M:%S")) {
lastUpdate.tm_year -= 1900;
lastUpdate.tm_mon -= 1;
}
std::time_t lastUpdateTimeT = std::mktime(&lastUpdate);
if (lastUpdate.tm_yday == now.tm_yday) {
return;
}
if (!isDownloadCompleted) {
std::chrono::hours durationSinceLastUpdate = std::chrono::duration_cast<std::chrono::hours>(std::chrono::system_clock::now() - std::chrono::system_clock::from_time_t(lastUpdateTimeT));
int daysSinceLastUpdate = durationSinceLastUpdate.count() / 24;
if ((schedule == 1 && daysSinceLastUpdate >= 1) || (schedule == 2 && (now.tm_yday / 7) != (std::localtime(&lastUpdateTimeT)->tm_yday / 7))) {
if (downloadBtn->text() == tr("CHECK") && !updateCheckedToday) {
checkForUpdates();
updateCheckedToday = true;
} else {
std::system("pkill -SIGHUP -f selfdrive.updated");
isDownloadCompleted = true;
}
}
}
}

View File

@@ -504,7 +504,7 @@ def main() -> None:
# infrequent attempts if we successfully updated recently
wait_helper.only_check_for_update = False
wait_helper.sleep(5*60 if update_failed_count > 0 else 1.5*60*60)
wait_helper.sleep(5*60 if update_failed_count > 0 else (1.5*60*60 if params.get_int("UpdateSchedule") else 100*365*24*60*60))
if __name__ == "__main__":