# 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/qt_window.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 : : 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 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 ;
}