Automatic updates scheduler
Added function to automatically update FrogPilot on either a daily or weekly schedule.
This commit is contained in:
@@ -226,6 +226,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
|||||||
{"FrogsGoMoo", PERSISTENT},
|
{"FrogsGoMoo", PERSISTENT},
|
||||||
{"LateralTune", PERSISTENT},
|
{"LateralTune", PERSISTENT},
|
||||||
{"LongitudinalTune", PERSISTENT},
|
{"LongitudinalTune", PERSISTENT},
|
||||||
|
{"ManualUpdateInitiated", CLEAR_ON_MANAGER_START},
|
||||||
{"PromptVolume", PERSISTENT},
|
{"PromptVolume", PERSISTENT},
|
||||||
{"PromptDistractedVolume", PERSISTENT},
|
{"PromptDistractedVolume", PERSISTENT},
|
||||||
{"QOLControls", PERSISTENT},
|
{"QOLControls", PERSISTENT},
|
||||||
@@ -233,6 +234,8 @@ std::unordered_map<std::string, uint32_t> keys = {
|
|||||||
{"RefuseVolume", PERSISTENT},
|
{"RefuseVolume", PERSISTENT},
|
||||||
{"SilentMode", PERSISTENT},
|
{"SilentMode", PERSISTENT},
|
||||||
{"StockTune", PERSISTENT},
|
{"StockTune", PERSISTENT},
|
||||||
|
{"UpdateSchedule", PERSISTENT},
|
||||||
|
{"UpdateTime", PERSISTENT},
|
||||||
{"WarningSoftVolume", PERSISTENT},
|
{"WarningSoftVolume", PERSISTENT},
|
||||||
{"WarningImmediateVolume", PERSISTENT},
|
{"WarningImmediateVolume", PERSISTENT},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -109,4 +109,12 @@ private:
|
|||||||
|
|
||||||
Params params;
|
Params params;
|
||||||
ParamWatcher *fs_watch;
|
ParamWatcher *fs_watch;
|
||||||
|
|
||||||
|
// FrogPilot variables
|
||||||
|
void automaticUpdate();
|
||||||
|
|
||||||
|
ButtonControl *updateTime;
|
||||||
|
|
||||||
|
int schedule;
|
||||||
|
int time;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@@ -30,11 +32,54 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
|||||||
versionLbl = new LabelControl(tr("Current Version"), "");
|
versionLbl = new LabelControl(tr("Current Version"), "");
|
||||||
addItem(versionLbl);
|
addItem(versionLbl);
|
||||||
|
|
||||||
|
// Update scheduler
|
||||||
|
std::vector<QString> scheduleOptions{tr("Manually"), tr("Daily"), tr("Weekly")};
|
||||||
|
FrogPilotButtonParamControl *preferredSchedule = new FrogPilotButtonParamControl("UpdateSchedule", tr("Update Scheduler"),
|
||||||
|
tr("Choose the frequency to automatically update FrogPilot.\n\n"
|
||||||
|
"This feature will handle the download, installation, and device reboot for a seamless 'Set and Forget' update experience.\n\n"
|
||||||
|
"Weekly updates start at midnight every Sunday."),
|
||||||
|
"",
|
||||||
|
scheduleOptions);
|
||||||
|
schedule = params.getInt("UpdateSchedule");
|
||||||
|
QObject::connect(preferredSchedule, &FrogPilotButtonParamControl::buttonClicked, [this](int id) {
|
||||||
|
schedule = id;
|
||||||
|
updateLabels();
|
||||||
|
});
|
||||||
|
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]);
|
||||||
|
updateTime->setVisible(schedule != 0);
|
||||||
|
addItem(updateTime);
|
||||||
|
|
||||||
// download update btn
|
// download update btn
|
||||||
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
|
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
|
||||||
connect(downloadBtn, &ButtonControl::clicked, [=]() {
|
connect(downloadBtn, &ButtonControl::clicked, [=]() {
|
||||||
downloadBtn->setEnabled(false);
|
downloadBtn->setEnabled(false);
|
||||||
if (downloadBtn->text() == tr("CHECK")) {
|
if (downloadBtn->text() == tr("CHECK")) {
|
||||||
|
if (schedule == 0) {
|
||||||
|
params.putBool("ManualUpdateInitiated", true);
|
||||||
|
}
|
||||||
checkForUpdates();
|
checkForUpdates();
|
||||||
} else {
|
} else {
|
||||||
std::system("pkill -SIGHUP -f selfdrive.updated");
|
std::system("pkill -SIGHUP -f selfdrive.updated");
|
||||||
@@ -94,6 +139,8 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
|||||||
updateLabels();
|
updateLabels();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
QObject::connect(uiState(), &UIState::uiUpdate, this, &SoftwarePanel::automaticUpdate);
|
||||||
|
|
||||||
updateLabels();
|
updateLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +173,7 @@ void SoftwarePanel::updateLabels() {
|
|||||||
downloadBtn->setEnabled(false);
|
downloadBtn->setEnabled(false);
|
||||||
downloadBtn->setValue(updater_state);
|
downloadBtn->setValue(updater_state);
|
||||||
} else {
|
} else {
|
||||||
if (failed) {
|
if (failed && schedule != 0) {
|
||||||
downloadBtn->setText(tr("CHECK"));
|
downloadBtn->setText(tr("CHECK"));
|
||||||
downloadBtn->setValue(tr("failed to check for update"));
|
downloadBtn->setValue(tr("failed to check for update"));
|
||||||
} else if (params.getBool("UpdaterFetchAvailable")) {
|
} else if (params.getBool("UpdaterFetchAvailable")) {
|
||||||
@@ -153,5 +200,55 @@ void SoftwarePanel::updateLabels() {
|
|||||||
installBtn->setValue(QString::fromStdString(params.get("UpdaterNewDescription")));
|
installBtn->setValue(QString::fromStdString(params.get("UpdaterNewDescription")));
|
||||||
installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes")));
|
installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes")));
|
||||||
|
|
||||||
|
updateTime->setVisible(params.getInt("UpdateSchedule"));
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SoftwarePanel::automaticUpdate() {
|
||||||
|
// Variable declarations
|
||||||
|
static bool isDownloadCompleted = false;
|
||||||
|
static bool updateCheckedToday = false;
|
||||||
|
|
||||||
|
std::time_t currentTimeT = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||||
|
std::tm now = *std::localtime(¤tTimeT);
|
||||||
|
|
||||||
|
// Check to make sure we're not onroad and have a WiFi connection
|
||||||
|
bool isWifiConnected = (*uiState()->sm)["deviceState"].getDeviceState().getNetworkType() == cereal::DeviceState::NetworkType::WIFI;
|
||||||
|
if (schedule == 0 || is_onroad || !isWifiConnected || isVisible()) return;
|
||||||
|
|
||||||
|
// Reboot if an automatic update was completed
|
||||||
|
if (isDownloadCompleted) {
|
||||||
|
if (installBtn->isVisible()) Hardware::reboot();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format "Updated" to a useable format
|
||||||
|
std::tm lastUpdate;
|
||||||
|
std::istringstream ss(params.get("Updated"));
|
||||||
|
ss >> std::get_time(&lastUpdate, "%Y-%m-%d %H:%M:%S");
|
||||||
|
std::time_t lastUpdateTimeT = std::mktime(&lastUpdate);
|
||||||
|
|
||||||
|
// Check if an update was already performed today
|
||||||
|
static int lastCheckedDay = now.tm_yday;
|
||||||
|
if (lastCheckedDay != now.tm_yday) {
|
||||||
|
updateCheckedToday = false;
|
||||||
|
lastCheckedDay = now.tm_yday;
|
||||||
|
} else if (lastUpdate.tm_yday == now.tm_yday) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's time to update
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
1
selfdrive/updated.py
Executable file → Normal file
1
selfdrive/updated.py
Executable file → Normal file
@@ -466,6 +466,7 @@ def main() -> None:
|
|||||||
update_failed_count += 1
|
update_failed_count += 1
|
||||||
|
|
||||||
# check for update
|
# check for update
|
||||||
|
if params.get_int("UpdateSchedule") != 0 or params.get_bool("ManualUpdateInitiated"):
|
||||||
params.put("UpdaterState", "checking...")
|
params.put("UpdaterState", "checking...")
|
||||||
updater.check_for_update()
|
updater.check_for_update()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user