openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

246 lines
7.7 KiB

#include "selfdrive/ui/qt/onroad.h"
#include <QDebug>
#include "selfdrive/common/timing.h"
#include "selfdrive/ui/paint.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/api.h"
#ifdef ENABLE_MAPS
#include "selfdrive/ui/qt/maps/map.h"
#endif
OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setMargin(bdr_s);
QStackedLayout *stacked_layout = new QStackedLayout;
stacked_layout->setStackingMode(QStackedLayout::StackAll);
main_layout->addLayout(stacked_layout);
nvg = new NvgWindow(VISION_STREAM_RGB_BACK, this);
QWidget * split_wrapper = new QWidget;
split = new QHBoxLayout(split_wrapper);
split->setContentsMargins(0, 0, 0, 0);
split->setSpacing(0);
split->addWidget(nvg);
stacked_layout->addWidget(split_wrapper);
alerts = new OnroadAlerts(this);
alerts->setAttribute(Qt::WA_TransparentForMouseEvents, true);
stacked_layout->addWidget(alerts);
// setup stacking order
alerts->raise();
setAttribute(Qt::WA_OpaquePaintEvent);
QObject::connect(this, &OnroadWindow::updateStateSignal, this, &OnroadWindow::updateState);
QObject::connect(this, &OnroadWindow::offroadTransitionSignal, this, &OnroadWindow::offroadTransition);
}
void OnroadWindow::updateState(const UIState &s) {
SubMaster &sm = *(s.sm);
QColor bgColor = bg_colors[s.status];
if (sm.updated("controlsState")) {
const cereal::ControlsState::Reader &cs = sm["controlsState"].getControlsState();
alerts->updateAlert({QString::fromStdString(cs.getAlertText1()),
QString::fromStdString(cs.getAlertText2()),
QString::fromStdString(cs.getAlertType()),
cs.getAlertSize(), cs.getAlertSound()}, bgColor);
} else if ((sm.frame - s.scene.started_frame) > 5 * UI_FREQ) {
// Handle controls timeout
if (sm.rcv_frame("controlsState") < s.scene.started_frame) {
// car is started, but controlsState hasn't been seen at all
alerts->updateAlert(CONTROLS_WAITING_ALERT, bgColor);
} else if ((nanos_since_boot() - sm.rcv_time("controlsState")) / 1e9 > CONTROLS_TIMEOUT) {
// car is started, but controls is lagging or died
bgColor = bg_colors[STATUS_ALERT];
alerts->updateAlert(CONTROLS_UNRESPONSIVE_ALERT, bgColor);
}
}
if (bg != bgColor) {
// repaint border
bg = bgColor;
update();
}
}
void OnroadWindow::mousePressEvent(QMouseEvent* e) {
if (map != nullptr) {
bool sidebarVisible = geometry().x() > 0;
map->setVisible(!sidebarVisible && !map->isVisible());
}
// propagation event to parent(HomeWindow)
QWidget::mousePressEvent(e);
}
void OnroadWindow::offroadTransition(bool offroad) {
#ifdef ENABLE_MAPS
if (!offroad) {
if (map == nullptr && (QUIState::ui_state.has_prime || !MAPBOX_TOKEN.isEmpty())) {
QMapboxGLSettings settings;
// Valid for 4 weeks since we can't swap tokens on the fly
QString token = MAPBOX_TOKEN.isEmpty() ? CommaApi::create_jwt({}, 4 * 7 * 24 * 3600) : MAPBOX_TOKEN;
if (!Hardware::PC()) {
settings.setCacheDatabasePath("/data/mbgl-cache.db");
}
settings.setApiBaseUrl(MAPS_HOST);
settings.setCacheDatabaseMaximumSize(20 * 1024 * 1024);
settings.setAccessToken(token.trimmed());
MapWindow * m = new MapWindow(settings);
m->setFixedWidth(topWidget(this)->width() / 2);
QObject::connect(this, &OnroadWindow::offroadTransitionSignal, m, &MapWindow::offroadTransition);
split->addWidget(m, 0, Qt::AlignRight);
map = m;
}
}
#endif
alerts->updateAlert({}, bg);
// update stream type
bool wide_cam = Hardware::TICI() && Params().getBool("EnableWideCamera");
nvg->setStreamType(wide_cam ? VISION_STREAM_RGB_WIDE : VISION_STREAM_RGB_BACK);
}
void OnroadWindow::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.fillRect(rect(), QColor(bg.red(), bg.green(), bg.blue(), 255));
}
// ***** onroad widgets *****
void OnroadAlerts::updateAlert(const Alert &a, const QColor &color) {
if (!alert.equal(a) || color != bg) {
alert = a;
bg = color;
update();
}
}
struct Rects
{
int x, y, w, h; // bounding box for total text area
int x1, y1, w1, h1; // bounding box for alert text 1
int x2, y2, w2, h2; // bounding box for alert text 2
Rects()
{
memset(this, 0, sizeof(Rects));
}
};
template<typename T>
void getRects(T t, Rects &r, Alert alert) {
// returns bounding boxes for text area, alert1, alert2
// w - width, h - height, windows starting position x, y
const int heightMax = 1080; // adjust to change text box scale
const int width = t->width();
const int height = t->height();
switch (alert.size) {
case cereal::ControlsState::AlertSize::SMALL:
r.h = height * (271.0 / heightMax);
break;
case cereal::ControlsState::AlertSize::MID:
r.h = height * (420.0 / heightMax);
break;
case cereal::ControlsState::AlertSize::FULL:
r.h = height;
break;
case cereal::ControlsState::AlertSize::NONE:
break;
}
r.w = width;
r.w1 = r.w;
r.w2 = r.w1;
r.h1 = r.h / 2.0;
r.h2 = r.h1;
r.y = height - r.h;
r.y1 = r.y;
r.y2 = r.y1 + r.h1;
}
void OnroadAlerts::paintEvent(QPaintEvent *event) {
if (alert.size == cereal::ControlsState::AlertSize::NONE) {
return;
}
Rects R;
getRects(this, R, alert);
QRect r = QRect(R.x, R.y, R.w, R.h);
QPainter p(this);
// draw background + gradient
p.setPen(Qt::NoPen);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
p.setBrush(QBrush(bg));
p.drawRect(r);
QLinearGradient g(0, r.y(), 0, r.bottom());
g.setColorAt(0, QColor::fromRgbF(0, 0, 0, 0.05));
g.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0.35));
p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
p.setBrush(QBrush(g));
p.fillRect(r, g);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
// text
p.setPen(QColor(0xff, 0xff, 0xff));
p.setRenderHint(QPainter::TextAntialiasing);
const QRect a1 = QRect(R.x1, R.y1, R.w1, R.h1);
const QRect a2 = QRect(R.x2, R.y2, R.w2, R.h2);
const int textBoxFlag = Qt::AlignCenter;
if (alert.size == cereal::ControlsState::AlertSize::SMALL) {
configFont(p, r, "Open Sans", 74, "SemiBold", alert.text1);
p.drawText(r, textBoxFlag, alert.text1);
} else if (alert.size == cereal::ControlsState::AlertSize::MID) {
configFont(p, a1, "Open Sans", 88, "Bold", alert.text1);
p.drawText(a1, textBoxFlag, alert.text1);
configFont(p, a2, "Open Sans", 66, "Regular", alert.text2);
p.drawText(a2, textBoxFlag, alert.text2);
} else if (alert.size == cereal::ControlsState::AlertSize::FULL) {
bool l = alert.text1.length() > 15;
configFont(p, a1, "Open Sans", l ? 132 : 177, "Bold", alert.text1);
p.drawText(a1, textBoxFlag, alert.text1);
configFont(p, a2, "Open Sans", 88, "Regular", alert.text2);
p.drawText(a2, textBoxFlag, alert.text2);
}
}
void NvgWindow::initializeGL() {
CameraViewWidget::initializeGL();
qInfo() << "OpenGL version:" << QString((const char*)glGetString(GL_VERSION));
qInfo() << "OpenGL vendor:" << QString((const char*)glGetString(GL_VENDOR));
qInfo() << "OpenGL renderer:" << QString((const char*)glGetString(GL_RENDERER));
qInfo() << "OpenGL language version:" << QString((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
ui_nvg_init(&QUIState::ui_state);
prev_draw_t = millis_since_boot();
setBackgroundColor(bg_colors[STATUS_DISENGAGED]);
}
void NvgWindow::paintGL() {
CameraViewWidget::paintGL();
ui_draw(&QUIState::ui_state, width(), height());
double cur_draw_t = millis_since_boot();
double dt = cur_draw_t - prev_draw_t;
if (dt > 66) {
// warn on sub 15fps
LOGW("slow frame time: %.2f", dt);
}
prev_draw_t = cur_draw_t;
}