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.
		
		
		
		
		
			
		
			
				
					
					
						
							360 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
	
	
							360 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;
 | 
						|
}
 | 
						|
 |