agnos updater UI (#21776)

* start agnos updater UI

* wifi

* progress

* sometimes things fail

* fix wifi

* in launch script

* fwd

* fwd stderr

* update that

* release files

Co-authored-by: Comma Device <device@comma.ai>
old-commit-hash: 14d26d6d89
commatwo_master
Adeeb Shihadeh 4 years ago committed by GitHub
parent 5d94b9978f
commit f4708c153e
  1. 5
      launch_chffrplus.sh
  2. 1
      release/files_tici
  3. 28
      selfdrive/hardware/tici/agnos.py
  4. 3
      selfdrive/hardware/tici/updater
  5. 1
      selfdrive/ui/.gitignore
  6. 9
      selfdrive/ui/SConscript
  7. 175
      selfdrive/ui/qt/setup/updater.cc
  8. 30
      selfdrive/ui/qt/setup/updater.h

@ -109,10 +109,7 @@ function tici_init {
# Check if AGNOS update is required
if [ $(< /VERSION) != "$AGNOS_VERSION" ]; then
MANIFEST="$DIR/selfdrive/hardware/tici/agnos.json"
$DIR/selfdrive/hardware/tici/agnos.py --swap $MANIFEST
sleep 1
sudo reboot
$DIR/selfdrive/hardware/tici/updater $DIR/selfdrive/hardware/tici/agnos.py $MANIFEST
fi
}

@ -19,6 +19,7 @@ selfdrive/hardware/tici/pins.py
selfdrive/hardware/tici/agnos.py
selfdrive/hardware/tici/agnos.json
selfdrive/hardware/tici/amplifier.py
selfdrive/hardware/tici/updater
selfdrive/ui/qt/spinner_larch64
selfdrive/ui/qt/text_larch64

@ -5,10 +5,9 @@ import hashlib
import requests
import struct
import subprocess
import sys
import os
from typing import Generator, Optional
from common.spinner import Spinner
from typing import Generator
SPARSE_CHUNK_FMT = struct.Struct('H2xI4x')
@ -127,7 +126,7 @@ def clear_partition_hash(target_slot_number: int, partition: dict) -> None:
os.sync()
def flash_partition(target_slot_number: int, partition: dict, cloudlog, spinner: Optional[Spinner] = None):
def flash_partition(target_slot_number: int, partition: dict, cloudlog):
cloudlog.info(f"Downloading and writing {partition['name']}")
if verify_partition(target_slot_number, partition):
@ -151,9 +150,7 @@ def flash_partition(target_slot_number: int, partition: dict, cloudlog, spinner:
for chunk in unsparsify(downloader):
raw_hash.update(chunk)
out.write(chunk)
if spinner is not None:
spinner.update_progress(out.tell(), partition_size)
print(f"Installing {partition['name']}: {out.tell() / partition_size * 100}", file=sys.stderr)
if raw_hash.hexdigest().lower() != partition['hash_raw'].lower():
raise Exception(f"Unsparse hash mismatch '{raw_hash.hexdigest().lower()}'")
@ -161,9 +158,6 @@ def flash_partition(target_slot_number: int, partition: dict, cloudlog, spinner:
while not downloader.eof:
out.write(downloader.read(1024 * 1024))
if spinner is not None:
spinner.update_progress(out.tell(), partition_size)
if downloader.sha256.hexdigest().lower() != partition['hash'].lower():
raise Exception("Uncompressed hash mismatch")
@ -191,7 +185,7 @@ def swap(manifest_path: str, target_slot_number: int, cloudlog) -> None:
cloudlog.error(f"Swap failed {out}")
def flash_agnos_update(manifest_path: str, target_slot_number: int, cloudlog, spinner: Optional[Spinner] = None) -> None:
def flash_agnos_update(manifest_path: str, target_slot_number: int, cloudlog) -> None:
update = json.load(open(manifest_path))
cloudlog.info(f"Target slot {target_slot_number}")
@ -204,14 +198,12 @@ def flash_agnos_update(manifest_path: str, target_slot_number: int, cloudlog, sp
for retries in range(10):
try:
flash_partition(target_slot_number, partition, cloudlog, spinner)
flash_partition(target_slot_number, partition, cloudlog)
success = True
break
except requests.exceptions.RequestException:
cloudlog.exception("Failed")
if spinner is not None:
spinner.update("Waiting for internet...")
cloudlog.info(f"Failed to download {partition['name']}, retrying ({retries})")
time.sleep(10)
@ -239,19 +231,15 @@ if __name__ == "__main__":
parser.add_argument("manifest", help="Manifest json")
args = parser.parse_args()
spinner = Spinner()
spinner.update("Updating AGNOS")
time.sleep(5)
logging.basicConfig(level=logging.INFO)
target_slot_number = get_target_slot_number()
if args.swap:
while not verify_agnos_update(args.manifest, target_slot_number):
logging.error("Verification failed. Flashing AGNOS")
flash_agnos_update(args.manifest, target_slot_number, logging, spinner)
flash_agnos_update(args.manifest, target_slot_number, logging)
logging.warning(f"Verification succeeded. Swapping to slot {target_slot_number}")
swap(args.manifest, target_slot_number, logging)
else:
flash_agnos_update(args.manifest, target_slot_number, logging, spinner)
flash_agnos_update(args.manifest, target_slot_number, logging)

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a242a48497a398be53dee2cbe7f1f11f37c0b3ad28c5bee51b5ab16ef0de1019
size 7820552

@ -7,4 +7,5 @@ qt/spinner
qt/setup/setup
qt/setup/reset
qt/setup/wifi
qt/setup/updater
qt/setup/installer_*

@ -54,15 +54,18 @@ qt_env.Program("_ui", qt_src, LIBS=qt_libs)
# setup, factory resetter, and installer
if arch != 'aarch64' and GetOption('setup'):
qt_env.Program("qt/setup/reset", ["qt/setup/reset.cc"], LIBS=qt_libs)
qt_env.Program("qt/setup/wifi", ["qt/setup/wifi.cc"], LIBS=qt_libs + ['common', 'json11'])
# TODO: do this for all resources once NEOS has rcc
assets = "#selfdrive/assets/assets.cc"
assets_src = "#selfdrive/assets/assets.qrc"
qt_env.Command(assets, assets_src, f"rcc $SOURCES -o $TARGET")
qt_env.Depends(assets, Glob('#selfdrive/assets/*', exclude=[assets, assets_src, "#selfdrive/assets/assets.o"]))
qt_env.Program("qt/setup/setup", ["qt/setup/setup.cc", assets],
asset_obj = qt_env.Object("assets", assets)
qt_env.Program("qt/setup/reset", ["qt/setup/reset.cc"], LIBS=qt_libs)
qt_env.Program("qt/setup/wifi", ["qt/setup/wifi.cc"], LIBS=qt_libs + ['common', 'json11'])
qt_env.Program("qt/setup/updater", ["qt/setup/updater.cc", asset_obj], LIBS=qt_libs)
qt_env.Program("qt/setup/setup", ["qt/setup/setup.cc", asset_obj],
LIBS=qt_libs + ['curl', 'common', 'json11'])
senv = qt_env.Clone()

@ -0,0 +1,175 @@
#include <QDebug>
#include <QTimer>
#include <QVBoxLayout>
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/offroad/networking.h"
#include "selfdrive/ui/qt/setup/updater.h"
Updater::Updater(const QString &updater_path, const QString &manifest_path, QWidget *parent)
: updater(updater_path), manifest(manifest_path), QStackedWidget(parent) {
assert(updater.size());
assert(manifest.size());
// initial prompt screen
prompt = new QWidget;
{
QVBoxLayout *layout = new QVBoxLayout(prompt);
layout->setContentsMargins(100, 250, 100, 100);
QLabel *title = new QLabel("Update Required");
title->setStyleSheet("font-size: 80px; font-weight: bold;");
layout->addWidget(title);
layout->addSpacing(75);
QLabel *desc = new QLabel("An operating system update is required. Connect your device to WiFi for the fastest update experience. The download size is approximately 1GB.");
desc->setWordWrap(true);
desc->setStyleSheet("font-size: 65px;");
layout->addWidget(desc);
layout->addStretch();
QHBoxLayout *hlayout = new QHBoxLayout;
hlayout->setSpacing(30);
layout->addLayout(hlayout);
QPushButton *connect = new QPushButton("Connect to WiFi");
connect->setObjectName("navBtn");
QObject::connect(connect, &QPushButton::clicked, [=]() {
setCurrentWidget(wifi);
});
hlayout->addWidget(connect);
QPushButton *install = new QPushButton("Install");
install->setObjectName("navBtn");
install->setStyleSheet("background-color: #465BEA;");
QObject::connect(install, &QPushButton::clicked, this, &Updater::installUpdate);
hlayout->addWidget(install);
}
// wifi connection screen
wifi = new QWidget;
{
QVBoxLayout *layout = new QVBoxLayout(wifi);
layout->setContentsMargins(100, 100, 100, 100);
Networking *networking = new Networking(this, false);
networking->setStyleSheet("Networking { background-color: #292929; border-radius: 13px; }");
layout->addWidget(networking, 1);
QPushButton *back = new QPushButton("Back");
back->setObjectName("navBtn");
back->setStyleSheet("padding-left: 60px; padding-right: 60px;");
QObject::connect(back, &QPushButton::clicked, [=]() {
setCurrentWidget(prompt);
});
layout->addWidget(back, 0, Qt::AlignLeft);
}
// progress screen
progress = new QWidget;
{
QVBoxLayout *layout = new QVBoxLayout(progress);
layout->setContentsMargins(150, 330, 150, 150);
layout->setSpacing(0);
text = new QLabel("Installing...");
text->setStyleSheet("font-size: 90px; font-weight: 600;");
layout->addWidget(text, 0, Qt::AlignTop);
layout->addSpacing(100);
bar = new QProgressBar();
bar->setRange(0, 100);
bar->setTextVisible(false);
bar->setFixedHeight(72);
layout->addWidget(bar, 0, Qt::AlignTop);
layout->addStretch();
reboot = new QPushButton("Reboot");
reboot->setObjectName("navBtn");
reboot->setStyleSheet("padding-left: 60px; padding-right: 60px;");
QObject::connect(reboot, &QPushButton::clicked, [=]() {
Hardware::reboot();
});
layout->addWidget(reboot, 0, Qt::AlignLeft);
reboot->hide();
layout->addStretch();
}
addWidget(prompt);
addWidget(wifi);
addWidget(progress);
setStyleSheet(R"(
* {
color: white;
font-family: Inter;
}
Updater {
color: white;
background-color: black;
}
QPushButton#navBtn {
height: 160;
font-size: 55px;
font-weight: 400;
border-radius: 10px;
background-color: #333333;
}
QProgressBar {
border: none;
background-color: #292929;
}
QProgressBar::chunk {
background-color: #364DEF;
}
)");
}
void Updater::installUpdate() {
setCurrentWidget(progress);
QObject::connect(&proc, &QProcess::readyReadStandardError, this, &Updater::readProgress);
QObject::connect(&proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &Updater::updateFinished);
proc.setProcessChannelMode(QProcess::ForwardedOutputChannel);
proc.start(updater, {"--swap", manifest});
}
void Updater::readProgress() {
auto lines = QString(proc.readAllStandardError());
for (const QString &line : lines.trimmed().split("\n")) {
auto parts = line.split(":");
if (parts.size() == 2) {
text->setText(parts[0]);
bar->setValue((int)parts[1].toDouble());
repaint();
} else {
qDebug() << line;
}
}
}
void Updater::updateFinished(int exitCode, QProcess::ExitStatus exitStatus) {
qDebug() << "finished with " << exitCode;
if (exitCode == 0) {
Hardware::reboot();
} else {
text->setText("Update failed");
reboot->show();
}
}
int main(int argc, char *argv[]) {
initApp();
QApplication a(argc, argv);
Updater updater(argv[1], argv[2]);
setMainWindow(&updater);
return a.exec();
}

@ -0,0 +1,30 @@
#pragma once
#include <QLabel>
#include <QProcess>
#include <QPushButton>
#include <QProgressBar>
#include <QStackedWidget>
#include <QWidget>
class Updater : public QStackedWidget {
Q_OBJECT
public:
explicit Updater(const QString &updater_path, const QString &manifest_path, QWidget *parent = 0);
private slots:
void installUpdate();
void readProgress();
void updateFinished(int exitCode, QProcess::ExitStatus exitStatus);
private:
QString updater, manifest;
QLabel *text;
QProgressBar *bar;
QPushButton *reboot;
QProcess proc;
QWidget *prompt, *wifi, *progress;
};
Loading…
Cancel
Save