|  |  |  | @ -1,17 +1,179 @@ | 
			
		
	
		
			
				
					|  |  |  |  | #include <iostream> | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #include "common/timing.h" | 
			
		
	
		
			
				
					|  |  |  |  | #include "common/swaglog.h" | 
			
		
	
		
			
				
					|  |  |  |  | #include "selfdrive/common/timing.h" | 
			
		
	
		
			
				
					|  |  |  |  | #include "selfdrive/common/swaglog.h" | 
			
		
	
		
			
				
					|  |  |  |  | #include "selfdrive/ui/qt/onroad.h" | 
			
		
	
		
			
				
					|  |  |  |  | #include "selfdrive/ui/paint.h" | 
			
		
	
		
			
				
					|  |  |  |  | #include "selfdrive/ui/qt/qt_window.h" | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | #include "onroad.h" | 
			
		
	
		
			
				
					|  |  |  |  | #include "paint.h" | 
			
		
	
		
			
				
					|  |  |  |  | OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) { | 
			
		
	
		
			
				
					|  |  |  |  |   layout = new QStackedLayout(); | 
			
		
	
		
			
				
					|  |  |  |  |   layout->setStackingMode(QStackedLayout::StackAll); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | OnroadWindow::~OnroadWindow() { | 
			
		
	
		
			
				
					|  |  |  |  |   // 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::update); | 
			
		
	
		
			
				
					|  |  |  |  |   QObject::connect(this, &OnroadWindow::offroadTransition, alerts, &OnroadAlerts::offroadTransition); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // hack to align the onroad alerts, better way to do this?
 | 
			
		
	
		
			
				
					|  |  |  |  |   QVBoxLayout *alerts_container = new QVBoxLayout(this); | 
			
		
	
		
			
				
					|  |  |  |  |   alerts_container->setMargin(0); | 
			
		
	
		
			
				
					|  |  |  |  |   alerts_container->addStretch(1); | 
			
		
	
		
			
				
					|  |  |  |  |   alerts_container->addWidget(alerts, 0, Qt::AlignBottom); | 
			
		
	
		
			
				
					|  |  |  |  |   QWidget *w = new QWidget(this); | 
			
		
	
		
			
				
					|  |  |  |  |   w->setLayout(alerts_container); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   layout->addWidget(w); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // alerts on top
 | 
			
		
	
		
			
				
					|  |  |  |  |   layout->setCurrentWidget(w); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   setLayout(layout); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // ***** onroad widgets *****
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | OnroadAlerts::OnroadAlerts(QWidget *parent) : QFrame(parent) { | 
			
		
	
		
			
				
					|  |  |  |  |   layout = new QVBoxLayout(this); | 
			
		
	
		
			
				
					|  |  |  |  |   layout->setSpacing(40); | 
			
		
	
		
			
				
					|  |  |  |  |   layout->setMargin(20); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   title = new QLabel(); | 
			
		
	
		
			
				
					|  |  |  |  |   title->setWordWrap(true); | 
			
		
	
		
			
				
					|  |  |  |  |   title->setAlignment(Qt::AlignCenter); | 
			
		
	
		
			
				
					|  |  |  |  |   layout->addWidget(title); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   msg = new QLabel(); | 
			
		
	
		
			
				
					|  |  |  |  |   msg->setWordWrap(true); | 
			
		
	
		
			
				
					|  |  |  |  |   msg->setAlignment(Qt::AlignCenter); | 
			
		
	
		
			
				
					|  |  |  |  |   layout->addWidget(msg); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   layout->addStretch(1); | 
			
		
	
		
			
				
					|  |  |  |  |   layout->insertStretch(0, 1); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   setLayout(layout); | 
			
		
	
		
			
				
					|  |  |  |  |   setStyleSheet("color: white;"); | 
			
		
	
		
			
				
					|  |  |  |  |   setVisible(false); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // setup sounds
 | 
			
		
	
		
			
				
					|  |  |  |  |   for (auto &kv : sound_map) { | 
			
		
	
		
			
				
					|  |  |  |  |     auto path = QUrl::fromLocalFile(kv.second.first); | 
			
		
	
		
			
				
					|  |  |  |  |     sounds[kv.first].setSource(path); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void OnroadAlerts::update(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 (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 { | 
			
		
	
		
			
				
					|  |  |  |  |     // Handle controls timeout
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (s.scene.deviceState.getStarted() && (sm.frame - s.scene.started_frame) > 10 * UI_FREQ) { | 
			
		
	
		
			
				
					|  |  |  |  |       const uint64_t cs_frame = sm.rcv_frame("controlsState"); | 
			
		
	
		
			
				
					|  |  |  |  |       if (cs_frame < 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 - cs_frame) > 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; | 
			
		
	
		
			
				
					|  |  |  |  |       } | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (isVisible()) { | 
			
		
	
		
			
				
					|  |  |  |  |     auto c = bg_colors[s.status]; | 
			
		
	
		
			
				
					|  |  |  |  |     float alpha = 0.375 * cos((millis_since_boot() / 1000) * 2 * M_PI * blinking_rate) + 0.625; | 
			
		
	
		
			
				
					|  |  |  |  |     bg.setRgb(c.r*255, c.g*255, c.b*255, c.a*alpha*255); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void OnroadAlerts::offroadTransition(bool offroad) { | 
			
		
	
		
			
				
					|  |  |  |  |   stopSounds(); | 
			
		
	
		
			
				
					|  |  |  |  |   setVisible(false); | 
			
		
	
		
			
				
					|  |  |  |  |   alert_type = ""; | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void OnroadAlerts::updateAlert(const QString &text1, const QString &text2, float blink_rate, | 
			
		
	
		
			
				
					|  |  |  |  |                                const std::string &type, cereal::ControlsState::AlertSize size, AudibleAlert sound) { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (alert_type.compare(type) == 0) { | 
			
		
	
		
			
				
					|  |  |  |  |     return; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   stopSounds(); | 
			
		
	
		
			
				
					|  |  |  |  |   if (sound != AudibleAlert::NONE) { | 
			
		
	
		
			
				
					|  |  |  |  |     playSound(sound); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   alert_type = type; | 
			
		
	
		
			
				
					|  |  |  |  |   blinking_rate = blink_rate; | 
			
		
	
		
			
				
					|  |  |  |  |   title->setText(text1); | 
			
		
	
		
			
				
					|  |  |  |  |   msg->setText(text2); | 
			
		
	
		
			
				
					|  |  |  |  |   msg->setVisible(!msg->text().isEmpty()); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (size == cereal::ControlsState::AlertSize::SMALL) { | 
			
		
	
		
			
				
					|  |  |  |  |     setFixedHeight(241); | 
			
		
	
		
			
				
					|  |  |  |  |     title->setStyleSheet("font-size: 70px; font-weight: 500;"); | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (size == cereal::ControlsState::AlertSize::MID) { | 
			
		
	
		
			
				
					|  |  |  |  |     setFixedHeight(390); | 
			
		
	
		
			
				
					|  |  |  |  |     msg->setStyleSheet("font-size: 65px; font-weight: 400;"); | 
			
		
	
		
			
				
					|  |  |  |  |     title->setStyleSheet("font-size: 80px; font-weight: 500;"); | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (size == cereal::ControlsState::AlertSize::FULL) { | 
			
		
	
		
			
				
					|  |  |  |  |     setFixedHeight(vwp_h); | 
			
		
	
		
			
				
					|  |  |  |  |     int title_size = (title->text().size() > 15) ? 130 : 110; | 
			
		
	
		
			
				
					|  |  |  |  |     title->setStyleSheet(QString("font-size: %1px; font-weight: 500;").arg(title_size)); | 
			
		
	
		
			
				
					|  |  |  |  |     msg->setStyleSheet("font-size: 90px; font-weight: 400;"); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   setVisible(size != cereal::ControlsState::AlertSize::NONE); | 
			
		
	
		
			
				
					|  |  |  |  |   repaint(); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 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); | 
			
		
	
		
			
				
					|  |  |  |  |   p.setBrush(QBrush(bg)); | 
			
		
	
		
			
				
					|  |  |  |  |   p.setPen(Qt::NoPen); | 
			
		
	
		
			
				
					|  |  |  |  |   p.drawRect(rect()); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | NvgWindow::~NvgWindow() { | 
			
		
	
		
			
				
					|  |  |  |  |   makeCurrent(); | 
			
		
	
		
			
				
					|  |  |  |  |   doneCurrent(); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void OnroadWindow::initializeGL() { | 
			
		
	
		
			
				
					|  |  |  |  | void NvgWindow::initializeGL() { | 
			
		
	
		
			
				
					|  |  |  |  |   initializeOpenGLFunctions(); | 
			
		
	
		
			
				
					|  |  |  |  |   std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl; | 
			
		
	
		
			
				
					|  |  |  |  |   std::cout << "OpenGL vendor: " << glGetString(GL_VENDOR) << std::endl; | 
			
		
	
	
		
			
				
					|  |  |  | @ -22,22 +184,20 @@ void OnroadWindow::initializeGL() { | 
			
		
	
		
			
				
					|  |  |  |  |   prev_draw_t = millis_since_boot(); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void OnroadWindow::update(const UIState &s) { | 
			
		
	
		
			
				
					|  |  |  |  | void NvgWindow::update(const UIState &s) { | 
			
		
	
		
			
				
					|  |  |  |  |   // Connecting to visionIPC requires opengl to be current
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (s.vipc_client->connected){ | 
			
		
	
		
			
				
					|  |  |  |  |     makeCurrent(); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   repaint(); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void OnroadWindow::paintGL() { | 
			
		
	
		
			
				
					|  |  |  |  | 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; | 
			
		
	
		
			
				
					|  |  |  |  |   // TODO: check if onroad
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (dt > 66 && QUIState::ui_state.scene.started && !QUIState::ui_state.scene.driver_view) { | 
			
		
	
		
			
				
					|  |  |  |  |   if (dt > 66 && !QUIState::ui_state.scene.driver_view) { | 
			
		
	
		
			
				
					|  |  |  |  |     // warn on sub 15fps
 | 
			
		
	
		
			
				
					|  |  |  |  |     LOGW("slow frame time: %.2f", dt); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
	
		
			
				
					|  |  |  | 
 |