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.

1149 lines
34 KiB

5 years ago
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <czmq.h>
#include "common/util.h"
#include "common/timing.h"
#include "common/swaglog.h"
#include "common/touch.h"
#include "common/visionimg.h"
#include "common/params.h"
#include "ui.hpp"
#include "sound.hpp"
static int last_brightness = -1;
static void set_brightness(UIState *s, int brightness) {
if (last_brightness != brightness && (s->awake || brightness == 0)) {
FILE *f = fopen("/sys/class/leds/lcd-backlight/brightness", "wb");
if (f != NULL) {
fprintf(f, "%d", brightness);
fclose(f);
last_brightness = brightness;
}
}
}
int event_processing_enabled = -1;
static void enable_event_processing(bool yes) {
if (event_processing_enabled != 1 && yes) {
system("service call window 18 i32 1"); // enable event processing
event_processing_enabled = 1;
} else if (event_processing_enabled != 0 && !yes) {
system("service call window 18 i32 0"); // disable event processing
event_processing_enabled = 0;
}
}
5 years ago
static void set_awake(UIState *s, bool awake) {
#ifdef QCOM
if (awake) {
// 30 second timeout at 30 fps
s->awake_timeout = 30*30;
}
if (s->awake != awake) {
s->awake = awake;
// TODO: replace command_awake and command_sleep with direct calls to android
if (awake) {
LOGW("awake normal");
framebuffer_set_power(s->fb, HWC_POWER_MODE_NORMAL);
enable_event_processing(true);
5 years ago
} else {
LOGW("awake off");
set_brightness(s, 0);
framebuffer_set_power(s->fb, HWC_POWER_MODE_OFF);
enable_event_processing(false);
5 years ago
}
}
#else
// computer UI doesn't sleep
s->awake = true;
#endif
}
static void update_offroad_layout_state(UIState *s) {
struct capn rc;
capn_init_malloc(&rc);
struct capn_segment *cs = capn_root(&rc).seg;
cereal_UiLayoutState_ptr layoutp = cereal_new_UiLayoutState(cs);
struct cereal_UiLayoutState layoutd = {
.activeApp = (cereal_UiLayoutState_App)s->active_app,
.sidebarCollapsed = s->scene.uilayout_sidebarcollapsed,
};
cereal_write_UiLayoutState(&layoutd, layoutp);
LOGD("setting active app to %d with sidebar %d", layoutd.activeApp, layoutd.sidebarCollapsed);
cereal_Event_ptr eventp = cereal_new_Event(cs);
struct cereal_Event event = {
.logMonoTime = nanos_since_boot(),
.which = cereal_Event_uiLayoutState,
.uiLayoutState = layoutp,
};
cereal_write_Event(&event, eventp);
capn_setp(capn_root(&rc), 0, eventp.p);
uint8_t buf[4096];
ssize_t rs = capn_write_mem(&rc, buf, sizeof(buf), 0);
s->offroad_sock->send((char*)buf, rs);
capn_free(&rc);
}
static void navigate_to_settings(UIState *s) {
#ifdef QCOM
s->active_app = cereal_UiLayoutState_App_settings;
update_offroad_layout_state(s);
#else
// computer UI doesn't have offroad settings
#endif
}
static void navigate_to_home(UIState *s) {
#ifdef QCOM
if (s->started) {
s->active_app = cereal_UiLayoutState_App_none;
} else {
s->active_app = cereal_UiLayoutState_App_home;
}
update_offroad_layout_state(s);
#else
// computer UI doesn't have offroad home
#endif
}
static void handle_sidebar_touch(UIState *s, int touch_x, int touch_y) {
if (!s->scene.uilayout_sidebarcollapsed && touch_x <= sbr_w) {
if (touch_x >= settings_btn_x && touch_x < (settings_btn_x + settings_btn_w)
&& touch_y >= settings_btn_y && touch_y < (settings_btn_y + settings_btn_h)) {
navigate_to_settings(s);
}
if (touch_x >= home_btn_x && touch_x < (home_btn_x + home_btn_w)
&& touch_y >= home_btn_y && touch_y < (home_btn_y + home_btn_h)) {
navigate_to_home(s);
if (s->started) {
s->scene.uilayout_sidebarcollapsed = true;
update_offroad_layout_state(s);
}
}
}
}
static void handle_driver_view_touch(UIState *s, int touch_x, int touch_y) {
int err = write_db_value(NULL, "IsDriverViewEnabled", "0", 1);
}
static void handle_vision_touch(UIState *s, int touch_x, int touch_y) {
if (s->started && (touch_x >= s->scene.ui_viz_rx - bdr_s)
&& (s->active_app != cereal_UiLayoutState_App_settings)) {
if (!s->scene.frontview) {
s->scene.uilayout_sidebarcollapsed = !s->scene.uilayout_sidebarcollapsed;
} else {
handle_driver_view_touch(s, touch_x, touch_y);
}
update_offroad_layout_state(s);
}
}
5 years ago
volatile sig_atomic_t do_exit = 0;
static void set_do_exit(int sig) {
do_exit = 1;
}
static void read_param_bool(bool* param, const char* param_name) {
char *s;
const int result = read_db_value(NULL, param_name, &s, NULL);
if (result == 0) {
*param = s[0] == '1';
free(s);
}
}
static void read_param_float(float* param, const char* param_name) {
char *s;
const int result = read_db_value(NULL, param_name, &s, NULL);
if (result == 0) {
*param = strtod(s, NULL);
free(s);
}
}
static int read_param_uint64(uint64_t* dest, const char* param_name) {
char *s;
const int result = read_db_value(NULL, param_name, &s, NULL);
if (result == 0) {
*dest = strtoull(s, NULL, 0);
free(s);
}
return result;
}
5 years ago
static void read_param_bool_timeout(bool* param, const char* param_name, int* timeout) {
if (*timeout > 0){
(*timeout)--;
} else {
read_param_bool(param, param_name);
*timeout = 2 * UI_FREQ; // 0.5Hz
}
}
static void read_param_float_timeout(float* param, const char* param_name, int* timeout) {
if (*timeout > 0){
(*timeout)--;
} else {
read_param_float(param, param_name);
*timeout = 2 * UI_FREQ; // 0.5Hz
}
}
static int read_param_uint64_timeout(uint64_t* dest, const char* param_name, int* timeout) {
if (*timeout > 0){
(*timeout)--;
return 0;
} else {
return read_param_uint64(dest, param_name);
*timeout = 2 * UI_FREQ; // 0.5Hz
}
}
static void update_offroad_layout_timeout(UIState *s, int* timeout) {
if (*timeout > 0) {
(*timeout)--;
} else {
update_offroad_layout_state(s);
*timeout = 2 * UI_FREQ;
}
}
5 years ago
static void ui_init(UIState *s) {
memset(s, 0, sizeof(UIState));
pthread_mutex_init(&s->lock, NULL);
s->ctx = Context::create();
s->model_sock = SubSocket::create(s->ctx, "model");
s->controlsstate_sock = SubSocket::create(s->ctx, "controlsState");
s->uilayout_sock = SubSocket::create(s->ctx, "uiLayoutState");
s->livecalibration_sock = SubSocket::create(s->ctx, "liveCalibration");
s->radarstate_sock = SubSocket::create(s->ctx, "radarState");
s->thermal_sock = SubSocket::create(s->ctx, "thermal");
s->health_sock = SubSocket::create(s->ctx, "health");
s->ubloxgnss_sock = SubSocket::create(s->ctx, "ubloxGnss");
s->driverstate_sock = SubSocket::create(s->ctx, "driverState");
s->dmonitoring_sock = SubSocket::create(s->ctx, "dMonitoringState");
s->offroad_sock = PubSocket::create(s->ctx, "offroadLayout");
5 years ago
assert(s->model_sock != NULL);
assert(s->controlsstate_sock != NULL);
assert(s->uilayout_sock != NULL);
assert(s->livecalibration_sock != NULL);
assert(s->radarstate_sock != NULL);
assert(s->thermal_sock != NULL);
assert(s->health_sock != NULL);
assert(s->ubloxgnss_sock != NULL);
assert(s->driverstate_sock != NULL);
assert(s->dmonitoring_sock != NULL);
assert(s->offroad_sock != NULL);
5 years ago
s->poller = Poller::create({
s->model_sock,
s->controlsstate_sock,
s->uilayout_sock,
s->livecalibration_sock,
s->radarstate_sock,
s->thermal_sock,
s->health_sock,
s->ubloxgnss_sock,
s->driverstate_sock,
s->dmonitoring_sock
5 years ago
});
#ifdef SHOW_SPEEDLIMIT
s->map_data_sock = SubSocket::create(s->ctx, "liveMapData");
5 years ago
assert(s->map_data_sock != NULL);
s->poller->registerSocket(s->map_data_sock);
5 years ago
#endif
s->ipc_fd = -1;
// init display
s->fb = framebuffer_init("ui", 0, true, &s->fb_w, &s->fb_h);
5 years ago
assert(s->fb);
set_awake(s, true);
s->model_changed = false;
s->livempc_or_radarstate_changed = false;
ui_nvg_init(s);
}
static void ui_init_vision(UIState *s, const VisionStreamBufs back_bufs,
int num_back_fds, const int *back_fds,
const VisionStreamBufs front_bufs, int num_front_fds,
const int *front_fds) {
const VisionUIInfo ui_info = back_bufs.buf_info.ui_info;
assert(num_back_fds == UI_BUF_COUNT);
assert(num_front_fds == UI_BUF_COUNT);
vipc_bufs_load(s->bufs, &back_bufs, num_back_fds, back_fds);
vipc_bufs_load(s->front_bufs, &front_bufs, num_front_fds, front_fds);
s->cur_vision_idx = -1;
s->cur_vision_front_idx = -1;
s->scene = (UIScene){
.frontview = getenv("FRONTVIEW") != NULL,
.fullview = getenv("FULLVIEW") != NULL,
.transformed_width = ui_info.transformed_width,
.transformed_height = ui_info.transformed_height,
.front_box_x = ui_info.front_box_x,
.front_box_y = ui_info.front_box_y,
.front_box_width = ui_info.front_box_width,
.front_box_height = ui_info.front_box_height,
.world_objects_visible = false, // Invisible until we receive a calibration message.
.gps_planner_active = false,
};
s->rgb_width = back_bufs.width;
s->rgb_height = back_bufs.height;
s->rgb_stride = back_bufs.stride;
s->rgb_buf_len = back_bufs.buf_len;
s->rgb_front_width = front_bufs.width;
s->rgb_front_height = front_bufs.height;
s->rgb_front_stride = front_bufs.stride;
s->rgb_front_buf_len = front_bufs.buf_len;
s->rgb_transform = (mat4){{
2.0f/s->rgb_width, 0.0f, 0.0f, -1.0f,
0.0f, 2.0f/s->rgb_height, 0.0f, -1.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
}};
read_param_float(&s->speed_lim_off, "SpeedLimitOffset");
read_param_bool(&s->is_metric, "IsMetric");
read_param_bool(&s->longitudinal_control, "LongitudinalControl");
read_param_bool(&s->limit_set_speed, "LimitSetSpeed");
// Set offsets so params don't get read at the same time
s->longitudinal_control_timeout = UI_FREQ / 3;
s->is_metric_timeout = UI_FREQ / 2;
s->limit_set_speed_timeout = UI_FREQ;
}
static PathData read_path(cereal_ModelData_PathData_ptr pathp) {
PathData ret = {0};
struct cereal_ModelData_PathData pathd;
cereal_read_ModelData_PathData(&pathd, pathp);
ret.prob = pathd.prob;
ret.std = pathd.std;
capn_list32 polyp = pathd.poly;
capn_resolve(&polyp.p);
for (int i = 0; i < POLYFIT_DEGREE; i++) {
ret.poly[i] = capn_to_f32(capn_get32(polyp, i));
}
// Compute points locations
for (int i = 0; i < MODEL_PATH_DISTANCE; i++) {
ret.points[i] = ret.poly[0] * (i*i*i) + ret.poly[1] * (i*i)+ ret.poly[2] * i + ret.poly[3];
}
return ret;
}
static ModelData read_model(cereal_ModelData_ptr modelp) {
struct cereal_ModelData modeld;
cereal_read_ModelData(&modeld, modelp);
ModelData d = {0};
d.path = read_path(modeld.path);
d.left_lane = read_path(modeld.leftLane);
d.right_lane = read_path(modeld.rightLane);
struct cereal_ModelData_LeadData leadd;
cereal_read_ModelData_LeadData(&leadd, modeld.lead);
d.lead = (LeadData){
.dist = leadd.dist, .prob = leadd.prob, .std = leadd.std,
};
return d;
}
static void update_status(UIState *s, int status) {
if (s->status != status) {
s->status = status;
}
}
void handle_message(UIState *s, Message * msg) {
struct capn ctx;
capn_init_mem(&ctx, (uint8_t*)msg->getData(), msg->getSize(), 0);
cereal_Event_ptr eventp;
eventp.p = capn_getp(capn_root(&ctx), 0, 1);
struct cereal_Event eventd;
cereal_read_Event(&eventd, eventp);
if (eventd.which == cereal_Event_controlsState && s->started) {
5 years ago
struct cereal_ControlsState datad;
cereal_read_ControlsState(&datad, eventd.controlsState);
s->controls_timeout = 1 * UI_FREQ;
s->scene.frontview = datad.rearViewCam;
if (!s->scene.frontview){s->controls_seen = true;}
5 years ago
if (datad.vCruise != s->scene.v_cruise) {
s->scene.v_cruise_update_ts = eventd.logMonoTime;
}
s->scene.v_cruise = datad.vCruise;
s->scene.v_ego = datad.vEgo;
s->scene.curvature = datad.curvature;
s->scene.engaged = datad.enabled;
s->scene.engageable = datad.engageable;
s->scene.gps_planner_active = datad.gpsPlannerActive;
s->scene.monitoring_active = datad.driverMonitoringOn;
s->scene.decel_for_model = datad.decelForModel;
if (datad.alertSound != cereal_CarControl_HUDControl_AudibleAlert_none && datad.alertSound != s->alert_sound) {
if (s->alert_sound != cereal_CarControl_HUDControl_AudibleAlert_none) {
stop_alert_sound(s->alert_sound);
}
play_alert_sound(datad.alertSound);
s->alert_sound = datad.alertSound;
snprintf(s->alert_type, sizeof(s->alert_type), "%s", datad.alertType.str);
} else if ((!datad.alertSound || datad.alertSound == cereal_CarControl_HUDControl_AudibleAlert_none)
&& s->alert_sound != cereal_CarControl_HUDControl_AudibleAlert_none) {
stop_alert_sound(s->alert_sound);
s->alert_type[0] = '\0';
s->alert_sound = cereal_CarControl_HUDControl_AudibleAlert_none;
}
if (datad.alertText1.str) {
snprintf(s->scene.alert_text1, sizeof(s->scene.alert_text1), "%s", datad.alertText1.str);
} else {
s->scene.alert_text1[0] = '\0';
}
if (datad.alertText2.str) {
snprintf(s->scene.alert_text2, sizeof(s->scene.alert_text2), "%s", datad.alertText2.str);
} else {
s->scene.alert_text2[0] = '\0';
}
s->scene.alert_ts = eventd.logMonoTime;
s->scene.alert_size = datad.alertSize;
if (datad.alertSize == cereal_ControlsState_AlertSize_none) {
s->alert_size = ALERTSIZE_NONE;
} else if (datad.alertSize == cereal_ControlsState_AlertSize_small) {
s->alert_size = ALERTSIZE_SMALL;
} else if (datad.alertSize == cereal_ControlsState_AlertSize_mid) {
s->alert_size = ALERTSIZE_MID;
} else if (datad.alertSize == cereal_ControlsState_AlertSize_full) {
s->alert_size = ALERTSIZE_FULL;
}
if (datad.alertStatus == cereal_ControlsState_AlertStatus_userPrompt) {
update_status(s, STATUS_WARNING);
} else if (datad.alertStatus == cereal_ControlsState_AlertStatus_critical) {
update_status(s, STATUS_ALERT);
} else if (datad.enabled) {
update_status(s, STATUS_ENGAGED);
} else {
update_status(s, STATUS_DISENGAGED);
5 years ago
}
s->scene.alert_blinkingrate = datad.alertBlinkingRate;
if (datad.alertBlinkingRate > 0.) {
if (s->alert_blinked) {
if (s->alert_blinking_alpha > 0.0 && s->alert_blinking_alpha < 1.0) {
s->alert_blinking_alpha += (0.05*datad.alertBlinkingRate);
} else {
s->alert_blinked = false;
}
} else {
if (s->alert_blinking_alpha > 0.25) {
s->alert_blinking_alpha -= (0.05*datad.alertBlinkingRate);
} else {
s->alert_blinking_alpha += 0.25;
s->alert_blinked = true;
}
}
}
} else if (eventd.which == cereal_Event_radarState) {
struct cereal_RadarState datad;
cereal_read_RadarState(&datad, eventd.radarState);
struct cereal_RadarState_LeadData leaddatad;
cereal_read_RadarState_LeadData(&leaddatad, datad.leadOne);
s->scene.lead_status = leaddatad.status;
s->scene.lead_d_rel = leaddatad.dRel;
s->scene.lead_y_rel = leaddatad.yRel;
s->scene.lead_v_rel = leaddatad.vRel;
cereal_read_RadarState_LeadData(&leaddatad, datad.leadTwo);
s->scene.lead_status2 = leaddatad.status;
s->scene.lead_d_rel2 = leaddatad.dRel;
s->scene.lead_y_rel2 = leaddatad.yRel;
s->scene.lead_v_rel2 = leaddatad.vRel;
5 years ago
s->livempc_or_radarstate_changed = true;
} else if (eventd.which == cereal_Event_liveCalibration) {
s->scene.world_objects_visible = true;
struct cereal_LiveCalibrationData datad;
cereal_read_LiveCalibrationData(&datad, eventd.liveCalibration);
capn_list32 extrinsicl = datad.extrinsicMatrix;
capn_resolve(&extrinsicl.p); // is this a bug?
for (int i = 0; i < 3 * 4; i++) {
s->scene.extrinsic_matrix.v[i] =
capn_to_f32(capn_get32(extrinsicl, i));
}
} else if (eventd.which == cereal_Event_model) {
s->scene.model = read_model(eventd.model);
s->model_changed = true;
} else if (eventd.which == cereal_Event_liveMpc) {
struct cereal_LiveMpcData datad;
cereal_read_LiveMpcData(&datad, eventd.liveMpc);
capn_list32 x_list = datad.x;
capn_resolve(&x_list.p);
for (int i = 0; i < 50; i++){
s->scene.mpc_x[i] = capn_to_f32(capn_get32(x_list, i));
}
capn_list32 y_list = datad.y;
capn_resolve(&y_list.p);
for (int i = 0; i < 50; i++){
s->scene.mpc_y[i] = capn_to_f32(capn_get32(y_list, i));
}
s->livempc_or_radarstate_changed = true;
} else if (eventd.which == cereal_Event_uiLayoutState) {
struct cereal_UiLayoutState datad;
cereal_read_UiLayoutState(&datad, eventd.uiLayoutState);
s->active_app = datad.activeApp;
s->scene.uilayout_sidebarcollapsed = datad.sidebarCollapsed;
if (datad.mockEngaged != s->scene.uilayout_mockengaged) {
s->scene.uilayout_mockengaged = datad.mockEngaged;
}
5 years ago
} else if (eventd.which == cereal_Event_liveMapData) {
struct cereal_LiveMapData datad;
cereal_read_LiveMapData(&datad, eventd.liveMapData);
s->scene.map_valid = datad.mapValid;
} else if (eventd.which == cereal_Event_thermal) {
struct cereal_ThermalData datad;
cereal_read_ThermalData(&datad, eventd.thermal);
s->scene.networkType = datad.networkType;
s->scene.networkStrength = datad.networkStrength;
s->scene.batteryPercent = datad.batteryPercent;
snprintf(s->scene.batteryStatus, sizeof(s->scene.batteryStatus), "%s", datad.batteryStatus.str);
s->scene.freeSpace = datad.freeSpace;
s->scene.thermalStatus = datad.thermalStatus;
s->scene.paTemp = datad.pa0;
s->thermal_started = datad.started;
} else if (eventd.which == cereal_Event_ubloxGnss) {
struct cereal_UbloxGnss datad;
cereal_read_UbloxGnss(&datad, eventd.ubloxGnss);
if (datad.which == cereal_UbloxGnss_measurementReport) {
struct cereal_UbloxGnss_MeasurementReport reportdatad;
cereal_read_UbloxGnss_MeasurementReport(&reportdatad, datad.measurementReport);
s->scene.satelliteCount = reportdatad.numMeas;
}
} else if (eventd.which == cereal_Event_health) {
struct cereal_HealthData datad;
cereal_read_HealthData(&datad, eventd.health);
s->scene.hwType = datad.hwType;
s->hardware_timeout = 5*30; // 5 seconds at 30 fps
} else if (eventd.which == cereal_Event_driverState) {
struct cereal_DriverState datad;
cereal_read_DriverState(&datad, eventd.driverState);
s->scene.face_prob = datad.faceProb;
capn_list32 fxy_list = datad.facePosition;
capn_resolve(&fxy_list.p);
s->scene.face_x = capn_to_f32(capn_get32(fxy_list, 0));
s->scene.face_y = capn_to_f32(capn_get32(fxy_list, 1));
} else if (eventd.which == cereal_Event_dMonitoringState) {
struct cereal_DMonitoringState datad;
cereal_read_DMonitoringState(&datad, eventd.dMonitoringState);
s->scene.is_rhd = datad.isRHD;
s->scene.awareness_status = datad.awarenessStatus;
s->preview_started = datad.isPreview;
5 years ago
}
s->started = s->thermal_started || s->preview_started ;
// Handle onroad/offroad transition
if (!s->started) {
if (s->status != STATUS_STOPPED) {
update_status(s, STATUS_STOPPED);
s->alert_sound_timeout = 0;
s->vision_seen = false;
s->controls_seen = false;
s->active_app = cereal_UiLayoutState_App_home;
update_offroad_layout_state(s);
}
} else if (s->status == STATUS_STOPPED) {
update_status(s, STATUS_DISENGAGED);
s->active_app = cereal_UiLayoutState_App_none;
update_offroad_layout_state(s);
}
5 years ago
capn_free(&ctx);
}
static void check_messages(UIState *s) {
while(true) {
auto polls = s->poller->poll(0);
if (polls.size() == 0)
break;
for (auto sock : polls){
Message * msg = sock->receive();
if (msg == NULL) continue;
handle_message(s, msg);
delete msg;
}
}
}
5 years ago
static void ui_update(UIState *s) {
int err;
if (s->vision_connect_firstrun) {
// cant run this in connector thread because opengl.
// do this here for now in lieu of a run_on_main_thread event
for (int i=0; i<UI_BUF_COUNT; i++) {
if(s->khr[i] != NULL) {
visionimg_destroy_gl(s->khr[i], s->priv_hnds[i]);
glDeleteTextures(1, &s->frame_texs[i]);
}
VisionImg img = {
.fd = s->bufs[i].fd,
.format = VISIONIMG_FORMAT_RGB24,
.width = s->rgb_width,
.height = s->rgb_height,
.stride = s->rgb_stride,
.bpp = 3,
.size = s->rgb_buf_len,
};
#ifndef QCOM
s->priv_hnds[i] = s->bufs[i].addr;
#endif
s->frame_texs[i] = visionimg_to_gl(&img, &s->khr[i], &s->priv_hnds[i]);
glBindTexture(GL_TEXTURE_2D, s->frame_texs[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// BGR
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
}
for (int i=0; i<UI_BUF_COUNT; i++) {
if(s->khr_front[i] != NULL) {
visionimg_destroy_gl(s->khr_front[i], s->priv_hnds_front[i]);
glDeleteTextures(1, &s->frame_front_texs[i]);
}
VisionImg img = {
.fd = s->front_bufs[i].fd,
.format = VISIONIMG_FORMAT_RGB24,
.width = s->rgb_front_width,
.height = s->rgb_front_height,
.stride = s->rgb_front_stride,
.bpp = 3,
.size = s->rgb_front_buf_len,
};
#ifndef QCOM
s->priv_hnds_front[i] = s->bufs[i].addr;
#endif
s->frame_front_texs[i] = visionimg_to_gl(&img, &s->khr_front[i], &s->priv_hnds_front[i]);
glBindTexture(GL_TEXTURE_2D, s->frame_front_texs[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// BGR
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
}
assert(glGetError() == GL_NO_ERROR);
s->scene.uilayout_sidebarcollapsed = true;
update_offroad_layout_state(s);
5 years ago
s->scene.ui_viz_rx = (box_x-sbr_w+bdr_s*2);
s->scene.ui_viz_rw = (box_w+sbr_w-(bdr_s*2));
s->scene.ui_viz_ro = 0;
s->vision_connect_firstrun = false;
s->alert_blinking_alpha = 1.0;
s->alert_blinked = false;
}
zmq_pollitem_t polls[1] = {{0}};
// Take an rgb image from visiond if there is one
while(true) {
assert(s->ipc_fd >= 0);
polls[0].fd = s->ipc_fd;
polls[0].events = ZMQ_POLLIN;
#ifdef UI_60FPS
// uses more CPU in both UI and surfaceflinger
// 16% / 21%
int ret = zmq_poll(polls, 1, 1);
#else
// 9% / 13% = a 14% savings
int ret = zmq_poll(polls, 1, 1000);
#endif
if (ret < 0) {
if (errno == EINTR || errno == EAGAIN) continue;
5 years ago
LOGE("poll failed (%d - %d)", ret, errno);
5 years ago
close(s->ipc_fd);
s->ipc_fd = -1;
s->vision_connected = false;
return;
} else if (ret == 0) {
break;
}
// vision ipc event
VisionPacket rp;
err = vipc_recv(s->ipc_fd, &rp);
if (err <= 0) {
LOGW("vision disconnected");
close(s->ipc_fd);
s->ipc_fd = -1;
s->vision_connected = false;
return;
}
if (rp.type == VIPC_STREAM_ACQUIRE) {
bool front = rp.d.stream_acq.type == VISION_STREAM_RGB_FRONT;
int idx = rp.d.stream_acq.idx;
int release_idx;
if (front) {
release_idx = s->cur_vision_front_idx;
} else {
release_idx = s->cur_vision_idx;
}
if (release_idx >= 0) {
VisionPacket rep = {
.type = VIPC_STREAM_RELEASE,
.d = { .stream_rel = {
.type = rp.d.stream_acq.type,
.idx = release_idx,
}},
};
vipc_send(s->ipc_fd, &rep);
}
if (front) {
assert(idx < UI_BUF_COUNT);
s->cur_vision_front_idx = idx;
} else {
assert(idx < UI_BUF_COUNT);
s->cur_vision_idx = idx;
// printf("v %d\n", ((uint8_t*)s->bufs[idx].addr)[0]);
}
} else {
assert(false);
}
break;
}
}
static int vision_subscribe(int fd, VisionPacket *rp, VisionStreamType type) {
int err;
LOGW("vision_subscribe type:%d", type);
VisionPacket p1 = {
.type = VIPC_STREAM_SUBSCRIBE,
.d = { .stream_sub = { .type = type, .tbuffer = true, }, },
};
err = vipc_send(fd, &p1);
if (err < 0) {
close(fd);
return 0;
}
do {
err = vipc_recv(fd, rp);
if (err <= 0) {
close(fd);
return 0;
}
// release what we aren't ready for yet
if (rp->type == VIPC_STREAM_ACQUIRE) {
VisionPacket rep = {
.type = VIPC_STREAM_RELEASE,
.d = { .stream_rel = {
.type = rp->d.stream_acq.type,
.idx = rp->d.stream_acq.idx,
}},
};
vipc_send(fd, &rep);
}
} while (rp->type != VIPC_STREAM_BUFS || rp->d.stream_bufs.type != type);
return 1;
}
static void* vision_connect_thread(void *args) {
int err;
set_thread_name("vision_connect");
UIState *s = (UIState*)args;
while (!do_exit) {
usleep(100000);
pthread_mutex_lock(&s->lock);
bool connected = s->vision_connected;
pthread_mutex_unlock(&s->lock);
if (connected) continue;
int fd = vipc_connect();
if (fd < 0) continue;
VisionPacket back_rp, front_rp;
if (!vision_subscribe(fd, &back_rp, VISION_STREAM_RGB_BACK)) continue;
if (!vision_subscribe(fd, &front_rp, VISION_STREAM_RGB_FRONT)) continue;
pthread_mutex_lock(&s->lock);
assert(!s->vision_connected);
s->ipc_fd = fd;
ui_init_vision(s,
back_rp.d.stream_bufs, back_rp.num_fds, back_rp.fds,
front_rp.d.stream_bufs, front_rp.num_fds, front_rp.fds);
s->vision_connected = true;
s->vision_seen = true;
5 years ago
s->vision_connect_firstrun = true;
// Drain sockets
while (true){
auto polls = s->poller->poll(0);
if (polls.size() == 0)
break;
for (auto sock : polls){
Message * msg = sock->receive();
if (msg == NULL) continue;
delete msg;
}
}
pthread_mutex_unlock(&s->lock);
}
return NULL;
}
#ifdef QCOM
#include <cutils/properties.h>
#include <hardware/sensors.h>
#include <utils/Timers.h>
static void* light_sensor_thread(void *args) {
int err;
set_thread_name("light_sensor");
UIState *s = (UIState*)args;
s->light_sensor = 0.0;
struct sensors_poll_device_t* device;
struct sensors_module_t* module;
hw_get_module(SENSORS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
sensors_open(&module->common, &device);
// need to do this
struct sensor_t const* list;
int count = module->get_sensors_list(module, &list);
int SENSOR_LIGHT = 7;
err = device->activate(device, SENSOR_LIGHT, 0);
if (err != 0) goto fail;
err = device->activate(device, SENSOR_LIGHT, 1);
if (err != 0) goto fail;
device->setDelay(device, SENSOR_LIGHT, ms2ns(100));
while (!do_exit) {
static const size_t numEvents = 1;
sensors_event_t buffer[numEvents];
int n = device->poll(device, buffer, numEvents);
if (n < 0) {
LOG_100("light_sensor_poll failed: %d", n);
}
if (n > 0) {
s->light_sensor = buffer[0].light;
}
}
sensors_close(device);
5 years ago
return NULL;
fail:
LOGE("LIGHT SENSOR IS MISSING");
s->light_sensor = 255;
return NULL;
}
#endif
int is_leon() {
#define MAXCHAR 1000
FILE *fp;
char str[MAXCHAR];
const char* filename = "/proc/cmdline";
fp = fopen(filename, "r");
if (fp == NULL){
printf("Could not open file %s",filename);
return 0;
}
fgets(str, MAXCHAR, fp);
fclose(fp);
return strstr(str, "letv") != NULL;
}
int main(int argc, char* argv[]) {
int err;
setpriority(PRIO_PROCESS, 0, -14);
zsys_handler_set(NULL);
signal(SIGINT, (sighandler_t)set_do_exit);
UIState uistate;
UIState *s = &uistate;
ui_init(s);
enable_event_processing(true);
5 years ago
pthread_t connect_thread_handle;
err = pthread_create(&connect_thread_handle, NULL,
vision_connect_thread, s);
assert(err == 0);
#ifdef QCOM
pthread_t light_sensor_thread_handle;
err = pthread_create(&light_sensor_thread_handle, NULL,
light_sensor_thread, s);
assert(err == 0);
#endif
TouchState touch = {0};
touch_init(&touch);
s->touch_fd = touch.fd;
ui_sound_init();
// light sensor scaling params
const int LEON = is_leon();
const float BRIGHTNESS_B = LEON ? 10.0 : 5.0;
const float BRIGHTNESS_M = LEON ? 2.6 : 1.3;
float smooth_brightness = BRIGHTNESS_B;
const int MIN_VOLUME = LEON ? 12 : 9;
const int MAX_VOLUME = LEON ? 15 : 12;
set_volume(MIN_VOLUME);
s->volume_timeout = 5 * UI_FREQ;
int draws = 0;
s->scene.satelliteCount = -1;
s->started = false;
s->vision_seen = false;
5 years ago
while (!do_exit) {
bool should_swap = false;
if (!s->started) {
5 years ago
// Delay a while to avoid 9% cpu usage while car is not started and user is keeping touching on the screen.
// Don't hold the lock while sleeping, so that vision_connect_thread have chances to get the lock.
usleep(30 * 1000);
}
pthread_mutex_lock(&s->lock);
double u1 = millis_since_boot();
// light sensor is only exposed on EONs
float clipped_brightness = (s->light_sensor*BRIGHTNESS_M) + BRIGHTNESS_B;
if (clipped_brightness > 512) clipped_brightness = 512;
smooth_brightness = clipped_brightness * 0.01 + smooth_brightness * 0.99;
if (smooth_brightness > 255) smooth_brightness = 255;
set_brightness(s, (int)smooth_brightness);
// resize vision for collapsing sidebar
const bool hasSidebar = !s->scene.uilayout_sidebarcollapsed;
s->scene.ui_viz_rx = hasSidebar ? box_x : (box_x - sbr_w + (bdr_s * 2));
s->scene.ui_viz_rw = hasSidebar ? box_w : (box_w + sbr_w - (bdr_s * 2));
s->scene.ui_viz_ro = hasSidebar ? -(sbr_w - 6 * bdr_s) : 0;
// poll for touch events
int touch_x = -1, touch_y = -1;
int touched = touch_poll(&touch, &touch_x, &touch_y, 0);
if (touched == 1) {
set_awake(s, true);
handle_sidebar_touch(s, touch_x, touch_y);
handle_vision_touch(s, touch_x, touch_y);
}
if (!s->started) {
// always process events offroad
check_messages(s);
5 years ago
} else {
set_awake(s, true);
// Car started, fetch a new rgb image from ipc
if (s->vision_connected){
ui_update(s);
5 years ago
}
check_messages(s);
// Visiond process is just stopped, force a redraw to make screen blank again.
if (!s->started) {
s->scene.satelliteCount = -1;
s->scene.uilayout_sidebarcollapsed = false;
update_offroad_layout_state(s);
5 years ago
ui_draw(s);
glFinish();
should_swap = true;
}
}
// manage wakefulness
if (s->awake_timeout > 0) {
s->awake_timeout--;
} else {
set_awake(s, false);
}
// manage hardware disconnect
if (s->hardware_timeout > 0) {
s->hardware_timeout--;
} else {
s->scene.hwType = cereal_HealthData_HwType_unknown;
}
// Don't waste resources on drawing in case screen is off
if (s->awake) {
5 years ago
ui_draw(s);
glFinish();
should_swap = true;
}
if (s->volume_timeout > 0) {
s->volume_timeout--;
} else {
int volume = fmin(MAX_VOLUME, MIN_VOLUME + s->scene.v_ego / 5); // up one notch every 5 m/s
set_volume(volume);
s->volume_timeout = 5 * UI_FREQ;
}
// If car is started and controlsState times out, display an alert
5 years ago
if (s->controls_timeout > 0) {
s->controls_timeout--;
} else {
if (s->started && s->controls_seen && strcmp(s->scene.alert_text2, "Controls Unresponsive") != 0) {
LOGE("Controls unresponsive");
5 years ago
s->scene.alert_size = ALERTSIZE_FULL;
update_status(s, STATUS_ALERT);
5 years ago
snprintf(s->scene.alert_text1, sizeof(s->scene.alert_text1), "%s", "TAKE CONTROL IMMEDIATELY");
snprintf(s->scene.alert_text2, sizeof(s->scene.alert_text2), "%s", "Controls Unresponsive");
ui_draw_vision_alert(s, s->scene.alert_size, s->status, s->scene.alert_text1, s->scene.alert_text2);
s->alert_sound_timeout = 2 * UI_FREQ;
s->alert_sound = cereal_CarControl_HUDControl_AudibleAlert_chimeWarningRepeat;
play_alert_sound(s->alert_sound);
}
5 years ago
s->alert_sound_timeout--;
s->controls_seen = false;
}
// stop playing alert sound
if ((!s->started || (s->started && s->alert_sound_timeout == 0)) &&
s->alert_sound != cereal_CarControl_HUDControl_AudibleAlert_none) {
stop_alert_sound(s->alert_sound);
s->alert_sound = cereal_CarControl_HUDControl_AudibleAlert_none;
}
5 years ago
read_param_bool_timeout(&s->is_metric, "IsMetric", &s->is_metric_timeout);
read_param_bool_timeout(&s->longitudinal_control, "LongitudinalControl", &s->longitudinal_control_timeout);
read_param_bool_timeout(&s->limit_set_speed, "LimitSetSpeed", &s->limit_set_speed_timeout);
read_param_float_timeout(&s->speed_lim_off, "SpeedLimitOffset", &s->limit_set_speed_timeout);
int param_read = read_param_uint64_timeout(&s->last_athena_ping, "LastAthenaPingTime", &s->last_athena_ping_timeout);
if (param_read != 0) {
s->scene.athenaStatus = NET_DISCONNECTED;
} else if (nanos_since_boot() - s->last_athena_ping < 70e9) {
s->scene.athenaStatus = NET_CONNECTED;
} else {
s->scene.athenaStatus = NET_ERROR;
}
update_offroad_layout_timeout(s, &s->offroad_layout_timeout);
5 years ago
pthread_mutex_unlock(&s->lock);
// the bg thread needs to be scheduled, so the main thread needs time without the lock
// safe to do this outside the lock?
if (should_swap) {
double u2 = millis_since_boot();
if (u2-u1 > 66) {
// warn on sub 15fps
LOGW("slow frame(%d) time: %.2f", draws, u2-u1);
}
draws++;
framebuffer_swap(s->fb);
}
}
set_awake(s, true);
ui_sound_destroy();
// wake up bg thread to exit
pthread_mutex_lock(&s->lock);
pthread_mutex_unlock(&s->lock);
#ifdef QCOM
// join light_sensor_thread?
#endif
err = pthread_join(connect_thread_handle, NULL);
assert(err == 0);
return 0;
}