diff --git a/selfdrive/ui/.gitignore b/selfdrive/ui/.gitignore index 2dbc325231..3f5116f24b 100644 --- a/selfdrive/ui/.gitignore +++ b/selfdrive/ui/.gitignore @@ -7,4 +7,4 @@ qt/spinner qt/setup/setup qt/setup/reset qt/setup/wifi -qt/setup/installer* +qt/setup/installer_* diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 8233144b19..df607ec7fc 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -54,6 +54,8 @@ if arch != 'aarch64' and GetOption('setup'): qt_env.Program("qt/setup/setup", ["qt/setup/setup.cc"], LIBS=qt_libs + ['curl', 'common', 'json11']) qt_env.Program("qt/setup/wifi", ["qt/setup/wifi.cc"], LIBS=qt_libs + ['common', 'json11']) + senv = qt_env.Clone() + senv['LINKFLAGS'].append('-Wl,-strip-debug') installers = [ ("openpilot", "release3-staging"), ("openpilot_test", "release3-staging"), @@ -70,8 +72,11 @@ if arch != 'aarch64' and GetOption('setup'): r = requests.get("https://github.com/commaci2.keys") r.raise_for_status() d['SSH_KEYS'] = f'\\"{r.text.strip()}\\"' - obj = qt_env.Object(f"qt/setup/installer_{name}.o", ["qt/setup/installer.cc"], CPPDEFINES=d) - qt_env.Program(f"qt/setup/installer_{name}", obj, LIBS=qt_libs, CPPDEFINES=d) + obj = senv.Object(f"qt/setup/installer_{name}.o", ["qt/setup/installer.cc"], CPPDEFINES=d) + f = senv.Program(f"qt/setup/installer_{name}", obj, LIBS=qt_libs) + # keep installers small + assert f[0].get_size() < 300*1e3 + # build headless replay if arch == 'x86_64' and os.path.exists(Dir("#tools/").get_abspath()): diff --git a/selfdrive/ui/qt/setup/installer.cc b/selfdrive/ui/qt/setup/installer.cc index 14bb598fa6..e1e90d4555 100644 --- a/selfdrive/ui/qt/setup/installer.cc +++ b/selfdrive/ui/qt/setup/installer.cc @@ -3,45 +3,137 @@ #include #include -#include #include -#ifndef BRANCH -#define BRANCH "master" -#endif +#include +#include +#include + +#include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/qt_window.h" +#include "selfdrive/ui/qt/setup/installer.h" #define GIT_URL "https://github.com/commaai/openpilot.git" #define GIT_SSH_URL "git@github.com:commaai/openpilot.git" #define CONTINUE_PATH "/data/continue.sh" + bool time_valid() { time_t rawtime; time(&rawtime); struct tm * sys_time = gmtime(&rawtime); - return (1900 + sys_time->tm_year) >= 2019; + return (1900 + sys_time->tm_year) >= 2020; } -int fresh_clone() { - int err; - // Cleanup - err = std::system("rm -rf /data/tmppilot /data/openpilot"); - if (err) return 1; +Installer::Installer(QWidget *parent) : QWidget(parent) { + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setContentsMargins(150, 290, 150, 150); + layout->setSpacing(0); + + QLabel *title = new QLabel("Installing..."); + title->setStyleSheet("font-size: 90px; font-weight: 600;"); + layout->addWidget(title, 0, Qt::AlignTop); + + layout->addSpacing(170); + + bar = new QProgressBar(); + bar->setRange(0, 100); + bar->setTextVisible(false); + bar->setFixedHeight(72); + layout->addWidget(bar, 0, Qt::AlignTop); + + layout->addSpacing(30); + + val = new QLabel("0%"); + val->setStyleSheet("font-size: 70px; font-weight: 300;"); + layout->addWidget(val, 0, Qt::AlignTop); + + layout->addStretch(); + + QTimer::singleShot(100, this, &Installer::doInstall); + + setStyleSheet(R"( + * { + font-family: Inter; + color: white; + background-color: black; + } + QProgressBar { + border: none; + background-color: #292929; + } + QProgressBar::chunk { + background-color: #364DEF; + } + )"); +} + +void Installer::updateProgress(int percent) { + bar->setValue(percent); + val->setText(QString("%1%").arg(percent)); +} + +void Installer::doInstall() { + // wait for valid time + while (!time_valid()) { + usleep(500 * 1000); + qDebug() << "Waiting for valid time"; + } + + // cleanup + int err = std::system("rm -rf /data/tmppilot /data/openpilot"); + assert(err == 0); - // Clone - err = std::system("git clone " GIT_URL " -b " BRANCH " --depth=1 --recurse-submodules /data/tmppilot"); - if (err) return 1; - err = std::system("cd /data/tmppilot && git remote set-url origin --push " GIT_SSH_URL); - if (err) return 1; + // TODO: support using the dashcam cache + // do install + freshClone(); +} + +void Installer::freshClone() { + qDebug() << "Doing fresh clone\n"; + QObject::connect(&proc, QOverload::of(&QProcess::finished), this, &Installer::cloneFinished); + QObject::connect(&proc, &QProcess::readyReadStandardError, this, &Installer::readProgress); + QStringList args = {"clone", "--progress", GIT_URL, "-b", BRANCH, "--depth=1", "--recurse-submodules", "/data/tmppilot"}; + proc.start("git", args); +} + +void Installer::readProgress() { + const QVector> stages = { + // prefix, weight in percentage + {"Receiving objects: ", 91}, + {"Resolving deltas: ", 2}, + {"Updating files: ", 7}, + }; + + auto line = QString(proc.readAllStandardError()); + + int base = 0; + for (const QPair kv : stages) { + if (line.startsWith(kv.first)) { + auto perc = line.split(kv.first)[1].split("%")[0]; + int p = base + int(perc.toFloat() / 100. * kv.second); + updateProgress(p); + break; + } + base += kv.second; + } +} + +void Installer::cloneFinished(int exitCode, QProcess::ExitStatus exitStatus) { + qDebug() << "finished " << exitCode; + assert(exitCode == 0); + + int err; + // move into place err = std::system("mv /data/tmppilot /data/openpilot"); - if (err) return 1; + assert(err == 0); #ifdef INTERNAL - err = std::system("mkdir -p /data/params/d/"); - if (err) return 1; + std::system("mkdir -p /data/params/d/"); std::map params = { {"SshEnabled", "1"}, @@ -54,32 +146,20 @@ int fresh_clone() { param << value; param.close(); } + std::system("cd /data/tmppilot && git remote set-url origin --push " GIT_SSH_URL); #endif - return 0; -} - -int install() { - int err; - - // Wait for valid time - while (!time_valid()) { - usleep(500 * 1000); - std::cout << "Waiting for valid time\n"; - } - - std::cout << "Doing fresh clone\n"; - err = fresh_clone(); - if (err) return 1; - - // Write continue.sh + // write continue.sh err = std::system("cp /data/openpilot/installer/continue_openpilot.sh " CONTINUE_PATH); - if (err == -1) return 1; + assert(err == 0); - return 0; + QCoreApplication::exit(0); } int main(int argc, char *argv[]) { - // TODO: make a small installation UI - return install(); + initApp(); + QApplication a(argc, argv); + Installer installer; + setMainWindow(&installer); + return a.exec(); } diff --git a/selfdrive/ui/qt/setup/installer.h b/selfdrive/ui/qt/setup/installer.h new file mode 100644 index 0000000000..cadac6807c --- /dev/null +++ b/selfdrive/ui/qt/setup/installer.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include + +class Installer : public QWidget { + Q_OBJECT + +public: + explicit Installer(QWidget *parent = 0); + +private slots: + void updateProgress(int percent); + + void readProgress(); + void cloneFinished(int exitCode, QProcess::ExitStatus exitStatus); + +private: + QLabel *val; + QProgressBar *bar; + QProcess proc; + + void doInstall(); + void freshClone(); +}; diff --git a/selfdrive/ui/qt/setup/setup.h b/selfdrive/ui/qt/setup/setup.h index c6764ec688..92a8d98180 100644 --- a/selfdrive/ui/qt/setup/setup.h +++ b/selfdrive/ui/qt/setup/setup.h @@ -1,3 +1,5 @@ +#pragma once + #include #include #include