# pragma once
# include <map>
# include <memory>
# include <string>
# include <optional>
# include <QObject>
# include <QTimer>
# include <QColor>
# include <QTransform>
# include "nanovg.h"
# include "cereal/messaging/messaging.h"
# include "common/transformations/orientation.hpp"
# include "selfdrive/camerad/cameras/camera_common.h"
# include "selfdrive/common/mat.h"
# include "selfdrive/common/modeldata.h"
# include "selfdrive/common/params.h"
# include "selfdrive/common/util.h"
# define COLOR_BLACK nvgRGBA(0, 0, 0, 255)
# define COLOR_BLACK_ALPHA(x) nvgRGBA(0, 0, 0, x)
# define COLOR_WHITE nvgRGBA(255, 255, 255, 255)
# define COLOR_WHITE_ALPHA(x) nvgRGBA(255, 255, 255, x)
# define COLOR_RED_ALPHA(x) nvgRGBA(201, 34, 49, x)
# define COLOR_YELLOW nvgRGBA(218, 202, 37, 255)
# define COLOR_RED nvgRGBA(201, 34, 49, 255)
const int bdr_s = 30 ;
const int header_h = 420 ;
const int footer_h = 280 ;
const int UI_FREQ = 20 ; // Hz
typedef cereal : : CarControl : : HUDControl : : AudibleAlert AudibleAlert ;
// TODO: this is also hardcoded in common/transformations/camera.py
// TODO: choose based on frame input size
const float y_offset = Hardware : : EON ( ) ? 0.0 : 150.0 ;
const float ZOOM = Hardware : : EON ( ) ? 2138.5 : 2912.8 ;
typedef struct Rect {
int x , y , w , h ;
int centerX ( ) const { return x + w / 2 ; }
int centerY ( ) const { return y + h / 2 ; }
int right ( ) const { return x + w ; }
int bottom ( ) const { return y + h ; }
bool ptInRect ( int px , int py ) const {
return px > = x & & px < ( x + w ) & & py > = y & & py < ( y + h ) ;
}
} Rect ;
struct Alert {
QString text1 ;
QString text2 ;
QString type ;
cereal : : ControlsState : : AlertSize size ;
AudibleAlert sound ;
bool equal ( const Alert & a2 ) {
return text1 = = a2 . text1 & & text2 = = a2 . text2 & & type = = a2 . type & & sound = = a2 . sound ;
}
static Alert get ( const SubMaster & sm , uint64_t started_frame ) {
if ( sm . updated ( " controlsState " ) ) {
const cereal : : ControlsState : : Reader & cs = sm [ " controlsState " ] . getControlsState ( ) ;
return { cs . getAlertText1 ( ) . cStr ( ) , cs . getAlertText2 ( ) . cStr ( ) ,
cs . getAlertType ( ) . cStr ( ) , cs . getAlertSize ( ) ,
cs . getAlertSound ( ) } ;
} else if ( ( sm . frame - started_frame ) > 5 * UI_FREQ ) {
const int CONTROLS_TIMEOUT = 5 ;
// Handle controls timeout
if ( sm . rcv_frame ( " controlsState " ) < started_frame ) {
// car is started, but controlsState hasn't been seen at all
return { " openpilot Unavailable " , " Waiting for controls to start " ,
" controlsWaiting " , cereal : : ControlsState : : AlertSize : : MID ,
AudibleAlert : : NONE } ;
} else if ( ( nanos_since_boot ( ) - sm . rcv_time ( " controlsState " ) ) / 1e9 > CONTROLS_TIMEOUT ) {
// car is started, but controls is lagging or died
return { " TAKE CONTROL IMMEDIATELY " , " Controls Unresponsive " ,
" controlsUnresponsive " , cereal : : ControlsState : : AlertSize : : FULL ,
AudibleAlert : : CHIME_WARNING_REPEAT } ;
}
}
return { } ;
}
} ;
typedef enum UIStatus {
STATUS_DISENGAGED ,
STATUS_ENGAGED ,
STATUS_WARNING ,
STATUS_ALERT ,
} UIStatus ;
const QColor bg_colors [ ] = {
[ STATUS_DISENGAGED ] = QColor ( 0x17 , 0x33 , 0x49 , 0xc8 ) ,
[ STATUS_ENGAGED ] = QColor ( 0x17 , 0x86 , 0x44 , 0xf1 ) ,
[ STATUS_WARNING ] = QColor ( 0xDA , 0x6F , 0x25 , 0xf1 ) ,
[ STATUS_ALERT ] = QColor ( 0xC9 , 0x22 , 0x31 , 0xf1 ) ,
} ;
typedef struct {
float x , y ;
} vertex_data ;
typedef struct {
vertex_data v [ TRAJECTORY_SIZE * 2 ] ;
int cnt ;
} line_vertices_data ;
typedef struct UIScene {
mat3 view_from_calib ;
bool world_objects_visible ;
cereal : : PandaState : : PandaType pandaType ;
// modelV2
float lane_line_probs [ 4 ] ;
float road_edge_stds [ 2 ] ;
line_vertices_data track_vertices ;
line_vertices_data lane_line_vertices [ 4 ] ;
line_vertices_data road_edge_vertices [ 2 ] ;
bool dm_active , engageable ;
// lead
vertex_data lead_vertices [ 2 ] ;
float light_sensor , accel_sensor , gyro_sensor ;
bool started , ignition , is_metric , longitudinal_control , end_to_end ;
uint64_t started_frame ;
} UIScene ;
typedef struct UIState {
int fb_w = 0 , fb_h = 0 ;
NVGcontext * vg ;
// images
std : : map < std : : string , int > images ;
std : : unique_ptr < SubMaster > sm ;
UIStatus status ;
UIScene scene = { } ;
bool awake ;
bool has_prime = false ;
QTransform car_space_transform ;
bool wide_camera ;
float running_time ;
} UIState ;
class QUIState : public QObject {
Q_OBJECT
public :
QUIState ( QObject * parent = 0 ) ;
// TODO: get rid of this, only use signal
inline static UIState ui_state = { 0 } ;
signals :
void uiUpdate ( const UIState & s ) ;
void offroadTransition ( bool offroad ) ;
private slots :
void update ( ) ;
private :
QTimer * timer ;
bool started_prev = true ;
} ;
// device management class
class Device : public QObject {
Q_OBJECT
public :
Device ( QObject * parent = 0 ) ;
private :
// auto brightness
const float accel_samples = 5 * UI_FREQ ;
bool awake = false ;
int awake_timeout = 0 ;
float accel_prev = 0 ;
float gyro_prev = 0 ;
float last_brightness = 0 ;
FirstOrderFilter brightness_filter ;
QTimer * timer ;
void updateBrightness ( const UIState & s ) ;
void updateWakefulness ( const UIState & s ) ;
signals :
void displayPowerChanged ( bool on ) ;
public slots :
void setAwake ( bool on , bool reset ) ;
void update ( const UIState & s ) ;
} ;