dragonpilot - 基於 openpilot 的開源駕駛輔助系統
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.

361 lines
11 KiB

#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <czmq.h>
#include "common/util.h"
#include "common/utilpp.h"
#include "common/params.h"
#include "common/touch.h"
#include "common/timing.h"
#include "common/swaglog.h"
#include "ui.hpp"
#include "paint.hpp"
// Includes for light sensor
#include <cutils/properties.h>
#include <hardware/sensors.h>
#include <utils/Timers.h>
volatile sig_atomic_t do_exit = 0;
static void set_do_exit(int sig) {
do_exit = 1;
}
static void* light_sensor_thread(void *args) {
set_thread_name("light_sensor");
int err;
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;
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);
return NULL;
fail:
LOGE("LIGHT SENSOR IS MISSING");
s->light_sensor = 255;
return NULL;
}
static void ui_set_brightness(UIState *s, int brightness) {
static int last_brightness = -1;
if (last_brightness != brightness && (s->awake || brightness == 0)) {
if (set_brightness(brightness)) {
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;
}
}
static void set_awake(UIState *s, bool awake) {
if (awake) {
// 30 second timeout
s->awake_timeout = 30*UI_FREQ;
}
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);
} else {
LOGW("awake off");
ui_set_brightness(s, 0);
framebuffer_set_power(s->fb, HWC_POWER_MODE_OFF);
enable_event_processing(false);
}
}
}
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 {
write_db_value("IsDriverViewEnabled", "0", 1);
}
}
}
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)) {
s->active_app = cereal::UiLayoutState::App::SETTINGS;
}
else 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)) {
if (s->started) {
s->active_app = cereal::UiLayoutState::App::NONE;
s->scene.uilayout_sidebarcollapsed = true;
} else {
s->active_app = cereal::UiLayoutState::App::HOME;
}
}
}
}
static void update_offroad_layout_state(UIState *s) {
static int timeout = 0;
static bool prev_collapsed = false;
static cereal::UiLayoutState::App prev_app = cereal::UiLayoutState::App::NONE;
if (timeout > 0) {
timeout--;
}
if (prev_collapsed != s->scene.uilayout_sidebarcollapsed || prev_app != s->active_app || timeout == 0) {
capnp::MallocMessageBuilder msg;
auto event = msg.initRoot<cereal::Event>();
event.setLogMonoTime(nanos_since_boot());
auto layout = event.initUiLayoutState();
layout.setActiveApp(s->active_app);
layout.setSidebarCollapsed(s->scene.uilayout_sidebarcollapsed);
s->pm->send("offroadLayout", msg);
LOGD("setting active app to %d with sidebar %d", (int)s->active_app, s->scene.uilayout_sidebarcollapsed);
prev_collapsed = s->scene.uilayout_sidebarcollapsed;
prev_app = s->active_app;
timeout = 2 * UI_FREQ;
}
}
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);
set_awake(s, true);
enable_event_processing(true);
pthread_t connect_thread_handle;
err = pthread_create(&connect_thread_handle, NULL,
vision_connect_thread, s);
assert(err == 0);
pthread_t light_sensor_thread_handle;
err = pthread_create(&light_sensor_thread_handle, NULL,
light_sensor_thread, s);
assert(err == 0);
TouchState touch = {0};
touch_init(&touch);
s->touch_fd = touch.fd;
// light sensor scaling params
const bool LEON = util::read_file("/proc/cmdline").find("letv") != std::string::npos;
float brightness_b, brightness_m;
int result = read_param(&brightness_b, "BRIGHTNESS_B", true);
result += read_param(&brightness_m, "BRIGHTNESS_M", true);
if(result != 0){
brightness_b = LEON ? 10.0 : 5.0;
brightness_m = LEON ? 2.6 : 1.3;
write_param_float(brightness_b, "BRIGHTNESS_B", true);
write_param_float(brightness_m, "BRIGHTNESS_M", true);
}
float smooth_brightness = brightness_b;
const int MIN_VOLUME = LEON ? 12 : 9;
const int MAX_VOLUME = LEON ? 15 : 12;
assert(s->sound.init(MIN_VOLUME));
int draws = 0;
while (!do_exit) {
bool should_swap = false;
if (!s->started) {
// 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;
ui_set_brightness(s, (int)smooth_brightness);
ui_update_sizes(s);
// 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);
if (s->started) {
s->controls_timeout = 5 * UI_FREQ;
}
} else {
set_awake(s, true);
// Car started, fetch a new rgb image from ipc
if (s->vision_connected){
ui_update(s);
}
check_messages(s);
// Visiond process is just stopped, force a redraw to make screen blank again.
if (!s->started) {
s->scene.uilayout_sidebarcollapsed = false;
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) {
ui_draw(s);
glFinish();
should_swap = true;
}
s->sound.setVolume(fmin(MAX_VOLUME, MIN_VOLUME + s->scene.controls_state.getVEgo() / 5)); // up one notch every 5 m/s
if (s->controls_timeout > 0) {
s->controls_timeout--;
} else if (s->started && !s->scene.frontview) {
if (!s->controls_seen) {
// car is started, but controlsState hasn't been seen at all
s->scene.alert_text1 = "openpilot Unavailable";
s->scene.alert_text2 = "Waiting for controls to start";
s->scene.alert_size = cereal::ControlsState::AlertSize::MID;
} else {
// car is started, but controls is lagging or died
LOGE("Controls unresponsive");
if (s->scene.alert_text2 != "Controls Unresponsive") {
s->sound.play(AudibleAlert::CHIME_WARNING_REPEAT);
}
s->scene.alert_text1 = "TAKE CONTROL IMMEDIATELY";
s->scene.alert_text2 = "Controls Unresponsive";
s->scene.alert_size = cereal::ControlsState::AlertSize::FULL;
update_status(s, STATUS_ALERT);
}
ui_draw_vision_alert(s, s->scene.alert_size, s->status, s->scene.alert_text1.c_str(), s->scene.alert_text2.c_str());
}
read_param_timeout(&s->is_metric, "IsMetric", &s->is_metric_timeout);
read_param_timeout(&s->longitudinal_control, "LongitudinalControl", &s->longitudinal_control_timeout);
read_param_timeout(&s->limit_set_speed, "LimitSetSpeed", &s->limit_set_speed_timeout);
read_param_timeout(&s->speed_lim_off, "SpeedLimitOffset", &s->limit_set_speed_timeout);
int param_read = read_param_timeout(&s->last_athena_ping, "LastAthenaPingTime", &s->last_athena_ping_timeout);
if (param_read != -1) { // Param was updated this loop
if (param_read != 0) { // Failed to read param
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_state(s);
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);
// wake up bg thread to exit
pthread_mutex_lock(&s->lock);
pthread_mutex_unlock(&s->lock);
// join light_sensor_thread?
err = pthread_join(connect_thread_handle, NULL);
assert(err == 0);
delete s->sm;
delete s->pm;
return 0;
}