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.

204 lines
6.8 KiB

#include "selfdrive/ui/qt/onroad.h"
#include <iostream>
#include "selfdrive/common/swaglog.h"
#include "selfdrive/common/timing.h"
#include "selfdrive/ui/paint.h"
#include "selfdrive/ui/qt/util.h"
OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
layout = new QStackedLayout();
layout->setStackingMode(QStackedLayout::StackAll);
// old UI on bottom
nvg = new NvgWindow(this);
layout->addWidget(nvg);
QObject::connect(this, &OnroadWindow::update, nvg, &NvgWindow::update);
alerts = new OnroadAlerts(this);
QObject::connect(this, &OnroadWindow::update, alerts, &OnroadAlerts::updateState);
QObject::connect(this, &OnroadWindow::offroadTransition, alerts, &OnroadAlerts::offroadTransition);
layout->addWidget(alerts);
// setup stacking order
alerts->raise();
setLayout(layout);
}
// ***** onroad widgets *****
OnroadAlerts::OnroadAlerts(QWidget *parent) : QWidget(parent) {
for (auto &kv : sound_map) {
auto path = QUrl::fromLocalFile(kv.second.first);
sounds[kv.first].setSource(path);
}
}
void OnroadAlerts::updateState(const UIState &s) {
SubMaster &sm = *(s.sm);
if (sm.updated("carState")) {
// scale volume with speed
volume = util::map_val(sm["carState"].getCarState().getVEgo(), 0.f, 20.f,
Hardware::MIN_VOLUME, Hardware::MAX_VOLUME);
}
if (s.scene.deviceState.getStarted()) {
if (sm.updated("controlsState")) {
const cereal::ControlsState::Reader &cs = sm["controlsState"].getControlsState();
updateAlert(QString::fromStdString(cs.getAlertText1()), QString::fromStdString(cs.getAlertText2()),
cs.getAlertBlinkingRate(), cs.getAlertType(), cs.getAlertSize(), cs.getAlertSound());
} else if ((sm.frame - s.scene.started_frame) > 10 * 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
updateAlert("openpilot Unavailable", "Waiting for controls to start", 0,
"controlsWaiting", cereal::ControlsState::AlertSize::MID, AudibleAlert::NONE);
} else if ((sm.frame - sm.rcv_frame("controlsState")) > 5 * UI_FREQ) {
// car is started, but controls is lagging or died
updateAlert("TAKE CONTROL IMMEDIATELY", "Controls Unresponsive", 0,
"controlsUnresponsive", cereal::ControlsState::AlertSize::FULL, AudibleAlert::CHIME_WARNING_REPEAT);
// TODO: clean this up once Qt handles the border
QUIState::ui_state.status = STATUS_ALERT;
}
}
}
// TODO: add blinking back if performant
//float alpha = 0.375 * cos((millis_since_boot() / 1000) * 2 * M_PI * blinking_rate) + 0.625;
auto c = bg_colors[s.status];
bg.setRgbF(c.r, c.g, c.b, c.a);
}
void OnroadAlerts::offroadTransition(bool offroad) {
updateAlert("", "", 0, "", cereal::ControlsState::AlertSize::NONE, AudibleAlert::NONE);
}
void OnroadAlerts::updateAlert(const QString &t1, const QString &t2, float blink_rate,
const std::string &type, cereal::ControlsState::AlertSize size, AudibleAlert sound) {
if (alert_type.compare(type) == 0 && text1.compare(t1) == 0) {
return;
}
stopSounds();
if (sound != AudibleAlert::NONE) {
playSound(sound);
}
text1 = t1;
text2 = t2;
alert_type = type;
alert_size = size;
blinking_rate = blink_rate;
update();
}
void OnroadAlerts::playSound(AudibleAlert alert) {
int loops = sound_map[alert].second ? QSoundEffect::Infinite : 0;
sounds[alert].setLoopCount(loops);
sounds[alert].setVolume(volume);
sounds[alert].play();
}
void OnroadAlerts::stopSounds() {
for (auto &kv : sounds) {
// Only stop repeating sounds
if (kv.second.loopsRemaining() == QSoundEffect::Infinite) {
kv.second.stop();
}
}
}
void OnroadAlerts::paintEvent(QPaintEvent *event) {
QPainter p(this);
static std::map<cereal::ControlsState::AlertSize, const int> alert_sizes = {
{cereal::ControlsState::AlertSize::NONE, 0},
{cereal::ControlsState::AlertSize::SMALL, 271},
{cereal::ControlsState::AlertSize::MID, 420},
{cereal::ControlsState::AlertSize::FULL, height()},
};
int h = alert_sizes[alert_size];
if (h == 0) {
return;
}
QRect r = QRect(0, height() - h, width(), h);
// draw background + gradient
p.setPen(Qt::NoPen);
p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
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.setBrush(QBrush(g));
p.fillRect(r, g);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
// remove bottom border
r = QRect(0, height() - h, width(), h - 30);
// text
const QPoint c = r.center();
p.setPen(QColor(0xff, 0xff, 0xff));
p.setRenderHint(QPainter::TextAntialiasing);
if (alert_size == cereal::ControlsState::AlertSize::SMALL) {
configFont(p, "Open Sans", 74, "SemiBold");
p.drawText(r, Qt::AlignCenter, text1);
} else if (alert_size == cereal::ControlsState::AlertSize::MID) {
configFont(p, "Open Sans", 88, "Bold");
p.drawText(QRect(0, c.y() - 125, width(), 150), Qt::AlignHCenter | Qt::AlignTop, text1);
configFont(p, "Open Sans", 66, "Regular");
p.drawText(QRect(0, c.y() + 21, width(), 90), Qt::AlignHCenter, text2);
} else if (alert_size == cereal::ControlsState::AlertSize::FULL) {
// TODO: offset from center to match old NVG UI, but why was it this way?
bool l = text1.length() > 15;
configFont(p, "Open Sans", l ? 132 : 177, "Bold");
p.drawText(QRect(0, r.y() + (l ? 240 : 270), width() - 60, 350), Qt::AlignHCenter | Qt::TextWordWrap, text1);
configFont(p, "Open Sans", 88, "Regular");
p.drawText(QRect(0, r.height() - (l ? 361 : 420), width() - 60, 300), Qt::AlignHCenter | Qt::TextWordWrap, text2);
}
}
NvgWindow::~NvgWindow() {
makeCurrent();
doneCurrent();
}
void NvgWindow::initializeGL() {
initializeOpenGLFunctions();
std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl;
std::cout << "OpenGL vendor: " << glGetString(GL_VENDOR) << std::endl;
std::cout << "OpenGL renderer: " << glGetString(GL_RENDERER) << std::endl;
std::cout << "OpenGL language version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
ui_nvg_init(&QUIState::ui_state);
prev_draw_t = millis_since_boot();
}
void NvgWindow::update(const UIState &s) {
// Connecting to visionIPC requires opengl to be current
if (s.vipc_client->connected){
makeCurrent();
}
repaint();
}
void NvgWindow::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 && !QUIState::ui_state.scene.driver_view) {
// warn on sub 15fps
LOGW("slow frame time: %.2f", dt);
}
prev_draw_t = cur_draw_t;
}