dragonpilot mod for 0.8.5-4

This commit is contained in:
Rick Lan
2021-07-02 12:49:43 +08:00
parent 145b222f0d
commit 522fb06db7
224 changed files with 9120 additions and 7777 deletions
-10
View File
@@ -1,10 +0,0 @@
moc_*
*.moc
replay/replay
qt/text
qt/spinner
qt/setup/setup
qt/setup/reset
qt/setup/wifi
qt/setup/installer*
-83
View File
@@ -1,83 +0,0 @@
import os
Import('qt_env', 'arch', 'common', 'messaging', 'gpucommon', 'visionipc',
'cereal', 'transformations')
base_libs = [gpucommon, common, messaging, cereal, visionipc, transformations, 'zmq',
'capnp', 'kj', 'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"]
maps = arch in ['larch64', 'x86_64'] and \
os.path.exists(File("qt/maps/map.cc").srcnode().abspath)
if arch == 'aarch64':
base_libs += ['log', 'utils', 'gui', 'ui', 'CB', 'gsl', 'adreno_utils', 'cutils', 'uuid']
if maps and arch in ['x86_64']:
rpath = [Dir(f"#phonelibs/mapbox-gl-native-qt/{arch}").srcnode().abspath]
qt_env["RPATH"] += rpath
if arch == "Darwin":
del base_libs[base_libs.index('OpenCL')]
qt_env['FRAMEWORKS'] += ['OpenCL']
widgets_src = ["qt/widgets/input.cc", "qt/widgets/drive_stats.cc",
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc",
"qt/widgets/offroad_alerts.cc", "qt/widgets/setup.cc", "qt/widgets/keyboard.cc",
"qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#phonelibs/qrcode/QrCode.cc", "qt/api.cc",
"qt/request_repeater.cc"]
if arch != 'aarch64':
widgets_src += ["qt/offroad/networking.cc", "qt/offroad/wifiManager.cc"]
if maps:
base_libs += ['qmapboxgl']
widgets_src += ["qt/maps/map_helpers.cc", "qt/maps/map.cc"]
qt_env['CPPDEFINES'] = ["ENABLE_MAPS"]
widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs)
qt_libs = [widgets] + base_libs
# spinner and text window
qt_env.Program("qt/text", ["qt/text.cc"], LIBS=qt_libs)
qt_env.Program("qt/spinner", ["qt/spinner.cc"], LIBS=base_libs)
# build main UI
qt_src = ["main.cc", "ui.cc", "paint.cc", "qt/sidebar.cc", "qt/onroad.cc",
"qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc",
"qt/offroad/onboarding.cc", "qt/offroad/driverview.cc", "#phonelibs/nanovg/nanovg.c"]
qt_env.Program("_ui", qt_src, LIBS=qt_libs)
# setup, factory resetter, and installer
if arch != 'aarch64' and "BUILD_SETUP" in os.environ:
qt_env.Program("qt/setup/reset", ["qt/setup/reset.cc"], LIBS=qt_libs)
qt_env.Program("qt/setup/setup", ["qt/setup/setup.cc"], LIBS=qt_libs + ['curl', 'common', 'json11'])
qt_env.Program("qt/setup/wifi", ["qt/setup/wifi.cc"], LIBS=qt_libs + ['common', 'json11'])
installers = [
("openpilot", "master"),
("openpilot_test", "release3-staging"),
("openpilot_internal", "master"),
("dashcam", "dashcam3-staging"),
("dashcam_test", "dashcam3-staging"),
]
for name, branch in installers:
d = {'BRANCH': f"'\"{branch}\"'"}
if "internal" in name:
d['INTERNAL'] = "1"
import requests
r = requests.get("https://github.com/commaci2.keys")
r.raise_for_status()
d['SSH_KEYS'] = f'\\"{r.text.strip()}\\"'
obj = qt_env.Object(f"qt/setup/installer_{name}.o", ["qt/setup/installer.cc"], CPPDEFINES=d)
qt_env.Program(f"qt/setup/installer_{name}", obj, LIBS=qt_libs, CPPDEFINES=d)
# build headless replay
if arch == 'x86_64' and os.path.exists(Dir("#tools/").get_abspath()):
qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"]
replay_lib_src = ["replay/replay.cc", "replay/filereader.cc", "replay/framereader.cc"]
replay_lib = qt_env.Library("qt_replay", replay_lib_src, LIBS=base_libs)
replay_libs = [replay_lib, 'avutil', 'avcodec', 'avformat', 'swscale', 'bz2'] + qt_libs
qt_env.Program("replay/replay", ["replay/main.cc"], LIBS=replay_libs)
BIN
View File
Binary file not shown.
-31
View File
@@ -1,31 +0,0 @@
#include <QApplication>
#include <QSslConfiguration>
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/window.h"
int main(int argc, char *argv[]) {
QSurfaceFormat fmt;
#ifdef __APPLE__
fmt.setVersion(3, 2);
fmt.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
fmt.setRenderableType(QSurfaceFormat::OpenGL);
#else
fmt.setRenderableType(QSurfaceFormat::OpenGLES);
#endif
QSurfaceFormat::setDefaultFormat(fmt);
if (Hardware::EON()) {
QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
QSslConfiguration ssl = QSslConfiguration::defaultConfiguration();
ssl.setCaCertificates(QSslCertificate::fromPath("/usr/etc/tls/cert.pem"));
QSslConfiguration::setDefaultConfiguration(ssl);
}
QApplication a(argc, argv);
MainWindow w;
setMainWindow(&w);
a.installEventFilter(&w);
return a.exec();
}
-475
View File
@@ -1,475 +0,0 @@
#include "paint.h"
#include <assert.h>
#include <algorithm>
#ifdef __APPLE__
#include <OpenGL/gl3.h>
#define NANOVG_GL3_IMPLEMENTATION
#define nvgCreate nvgCreateGL3
#else
#include <GLES3/gl3.h>
#define NANOVG_GLES3_IMPLEMENTATION
#define nvgCreate nvgCreateGLES3
#endif
#define NANOVG_GLES3_IMPLEMENTATION
#include <nanovg_gl.h>
#include <nanovg_gl_utils.h>
#include "selfdrive/common/timing.h"
#include "selfdrive/common/util.h"
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/ui.h"
static void ui_draw_text(const UIState *s, float x, float y, const char *string, float size, NVGcolor color, const char *font_name) {
nvgFontFace(s->vg, font_name);
nvgFontSize(s->vg, size);
nvgFillColor(s->vg, color);
nvgText(s->vg, x, y, string, NULL);
}
static void draw_chevron(UIState *s, float x, float y, float sz, NVGcolor fillColor, NVGcolor glowColor) {
// glow
float g_xo = sz/5;
float g_yo = sz/10;
nvgBeginPath(s->vg);
nvgMoveTo(s->vg, x+(sz*1.35)+g_xo, y+sz+g_yo);
nvgLineTo(s->vg, x, y-g_xo);
nvgLineTo(s->vg, x-(sz*1.35)-g_xo, y+sz+g_yo);
nvgClosePath(s->vg);
nvgFillColor(s->vg, glowColor);
nvgFill(s->vg);
// chevron
nvgBeginPath(s->vg);
nvgMoveTo(s->vg, x+(sz*1.25), y+sz);
nvgLineTo(s->vg, x, y);
nvgLineTo(s->vg, x-(sz*1.25), y+sz);
nvgClosePath(s->vg);
nvgFillColor(s->vg, fillColor);
nvgFill(s->vg);
}
static void ui_draw_circle_image(const UIState *s, int center_x, int center_y, int radius, const char *image, NVGcolor color, float img_alpha) {
nvgBeginPath(s->vg);
nvgCircle(s->vg, center_x, center_y, radius);
nvgFillColor(s->vg, color);
nvgFill(s->vg);
const int img_size = radius * 1.5;
ui_draw_image(s, {center_x - (img_size / 2), center_y - (img_size / 2), img_size, img_size}, image, img_alpha);
}
static void ui_draw_circle_image(const UIState *s, int center_x, int center_y, int radius, const char *image, bool active) {
float bg_alpha = active ? 0.3f : 0.1f;
float img_alpha = active ? 1.0f : 0.15f;
ui_draw_circle_image(s, center_x, center_y, radius, image, nvgRGBA(0, 0, 0, (255 * bg_alpha)), img_alpha);
}
static void draw_lead(UIState *s, const cereal::RadarState::LeadData::Reader &lead_data, const vertex_data &vd) {
// Draw lead car indicator
auto [x, y] = vd;
float fillAlpha = 0;
float speedBuff = 10.;
float leadBuff = 40.;
float d_rel = lead_data.getDRel();
float v_rel = lead_data.getVRel();
if (d_rel < leadBuff) {
fillAlpha = 255*(1.0-(d_rel/leadBuff));
if (v_rel < 0) {
fillAlpha += 255*(-1*(v_rel/speedBuff));
}
fillAlpha = (int)(fmin(fillAlpha, 255));
}
float sz = std::clamp((25 * 30) / (d_rel / 3 + 30), 15.0f, 30.0f) * s->zoom;
x = std::clamp(x, 0.f, s->viz_rect.right() - sz / 2);
y = std::fmin(s->viz_rect.bottom() - sz * .6, y);
draw_chevron(s, x, y, sz, nvgRGBA(201, 34, 49, fillAlpha), COLOR_YELLOW);
}
static void ui_draw_line(UIState *s, const line_vertices_data &vd, NVGcolor *color, NVGpaint *paint) {
if (vd.cnt == 0) return;
const vertex_data *v = &vd.v[0];
nvgBeginPath(s->vg);
nvgMoveTo(s->vg, v[0].x, v[0].y);
for (int i = 1; i < vd.cnt; i++) {
nvgLineTo(s->vg, v[i].x, v[i].y);
}
nvgClosePath(s->vg);
if (color) {
nvgFillColor(s->vg, *color);
} else if (paint) {
nvgFillPaint(s->vg, *paint);
}
nvgFill(s->vg);
}
static void draw_frame(UIState *s) {
glBindVertexArray(s->frame_vao);
mat4 *out_mat = &s->rear_frame_mat;
glActiveTexture(GL_TEXTURE0);
if (s->last_frame) {
glBindTexture(GL_TEXTURE_2D, s->texture[s->last_frame->idx]->frame_tex);
if (!Hardware::EON()) {
// this is handled in ion on QCOM
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, s->last_frame->width, s->last_frame->height,
0, GL_RGB, GL_UNSIGNED_BYTE, s->last_frame->addr);
}
}
glUseProgram(s->gl_shader->prog);
glUniform1i(s->gl_shader->getUniformLocation("uTexture"), 0);
glUniformMatrix4fv(s->gl_shader->getUniformLocation("uTransform"), 1, GL_TRUE, out_mat->v);
assert(glGetError() == GL_NO_ERROR);
glEnableVertexAttribArray(0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (const void *)0);
glDisableVertexAttribArray(0);
glBindVertexArray(0);
}
static void ui_draw_vision_lane_lines(UIState *s) {
const UIScene &scene = s->scene;
NVGpaint track_bg;
if (!scene.end_to_end) {
// paint lanelines
for (int i = 0; i < std::size(scene.lane_line_vertices); i++) {
NVGcolor color = nvgRGBAf(1.0, 1.0, 1.0, scene.lane_line_probs[i]);
ui_draw_line(s, scene.lane_line_vertices[i], &color, nullptr);
}
// paint road edges
for (int i = 0; i < std::size(scene.road_edge_vertices); i++) {
NVGcolor color = nvgRGBAf(1.0, 0.0, 0.0, std::clamp<float>(1.0 - scene.road_edge_stds[i], 0.0, 1.0));
ui_draw_line(s, scene.road_edge_vertices[i], &color, nullptr);
}
track_bg = nvgLinearGradient(s->vg, s->fb_w, s->fb_h, s->fb_w, s->fb_h * .4,
COLOR_WHITE, COLOR_WHITE_ALPHA(0));
} else {
track_bg = nvgLinearGradient(s->vg, s->fb_w, s->fb_h, s->fb_w, s->fb_h * .4,
COLOR_RED, COLOR_RED_ALPHA(0));
}
// paint path
ui_draw_line(s, scene.track_vertices, nullptr, &track_bg);
}
// Draw all world space objects.
static void ui_draw_world(UIState *s) {
// Don't draw on top of sidebar
nvgScissor(s->vg, s->viz_rect.x, s->viz_rect.y, s->viz_rect.w, s->viz_rect.h);
// Draw lane edges and vision/mpc tracks
ui_draw_vision_lane_lines(s);
// Draw lead indicators if openpilot is handling longitudinal
if (s->scene.longitudinal_control) {
auto radar_state = (*s->sm)["radarState"].getRadarState();
auto lead_one = radar_state.getLeadOne();
auto lead_two = radar_state.getLeadTwo();
if (lead_one.getStatus()) {
draw_lead(s, lead_one, s->scene.lead_vertices[0]);
}
if (lead_two.getStatus() && (std::abs(lead_one.getDRel() - lead_two.getDRel()) > 3.0)) {
draw_lead(s, lead_two, s->scene.lead_vertices[1]);
}
}
nvgResetScissor(s->vg);
}
static void ui_draw_vision_maxspeed(UIState *s) {
const int SET_SPEED_NA = 255;
float maxspeed = (*s->sm)["controlsState"].getControlsState().getVCruise();
const bool is_cruise_set = maxspeed != 0 && maxspeed != SET_SPEED_NA;
if (is_cruise_set && !s->scene.is_metric) { maxspeed *= 0.6225; }
const Rect rect = {s->viz_rect.x + (bdr_s * 2), int(s->viz_rect.y + (bdr_s * 1.5)), 184, 202};
ui_fill_rect(s->vg, rect, COLOR_BLACK_ALPHA(100), 30.);
ui_draw_rect(s->vg, rect, COLOR_WHITE_ALPHA(100), 10, 20.);
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE);
ui_draw_text(s, rect.centerX(), 148, "MAX", 26 * 2.5, COLOR_WHITE_ALPHA(is_cruise_set ? 200 : 100), "sans-regular");
if (is_cruise_set) {
const std::string maxspeed_str = std::to_string((int)std::nearbyint(maxspeed));
ui_draw_text(s, rect.centerX(), 242, maxspeed_str.c_str(), 48 * 2.5, COLOR_WHITE, "sans-bold");
} else {
ui_draw_text(s, rect.centerX(), 242, "N/A", 42 * 2.5, COLOR_WHITE_ALPHA(100), "sans-semibold");
}
}
static void ui_draw_vision_speed(UIState *s) {
const float speed = std::max(0.0, (*s->sm)["carState"].getCarState().getVEgo() * (s->scene.is_metric ? 3.6 : 2.2369363));
const std::string speed_str = std::to_string((int)std::nearbyint(speed));
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE);
ui_draw_text(s, s->viz_rect.centerX(), 240, speed_str.c_str(), 96 * 2.5, COLOR_WHITE, "sans-bold");
ui_draw_text(s, s->viz_rect.centerX(), 320, s->scene.is_metric ? "km/h" : "mph", 36 * 2.5, COLOR_WHITE_ALPHA(200), "sans-regular");
}
static void ui_draw_vision_event(UIState *s) {
if (s->scene.engageable) {
// draw steering wheel
const int radius = 96;
const int center_x = s->viz_rect.right() - radius - bdr_s * 2;
const int center_y = s->viz_rect.y + radius + (bdr_s * 1.5);
const QColor &color = bg_colors[s->status];
NVGcolor nvg_color = nvgRGBA(color.red(), color.green(), color.blue(), color.alpha());
ui_draw_circle_image(s, center_x, center_y, radius, "wheel", nvg_color, 1.0f);
}
}
static void ui_draw_vision_face(UIState *s) {
const int radius = 96;
const int center_x = s->viz_rect.x + radius + (bdr_s * 2);
const int center_y = s->viz_rect.bottom() - footer_h / 2;
ui_draw_circle_image(s, center_x, center_y, radius, "driver_face", s->scene.dm_active);
}
static void ui_draw_vision_header(UIState *s) {
NVGpaint gradient = nvgLinearGradient(s->vg, s->viz_rect.x,
s->viz_rect.y+(header_h-(header_h/2.5)),
s->viz_rect.x, s->viz_rect.y+header_h,
nvgRGBAf(0,0,0,0.45), nvgRGBAf(0,0,0,0));
ui_fill_rect(s->vg, {s->viz_rect.x, s->viz_rect.y, s->viz_rect.w, header_h}, gradient);
ui_draw_vision_maxspeed(s);
ui_draw_vision_speed(s);
ui_draw_vision_event(s);
}
static void ui_draw_vision_frame(UIState *s) {
// Draw video frames
glEnable(GL_SCISSOR_TEST);
glViewport(s->video_rect.x, s->video_rect.y, s->video_rect.w, s->video_rect.h);
glScissor(s->viz_rect.x, s->viz_rect.y, s->viz_rect.w, s->viz_rect.h);
draw_frame(s);
glDisable(GL_SCISSOR_TEST);
glViewport(0, 0, s->fb_w, s->fb_h);
}
static void ui_draw_vision(UIState *s) {
const UIScene *scene = &s->scene;
// Draw augmented elements
if (scene->world_objects_visible) {
ui_draw_world(s);
}
// Set Speed, Current Speed, Status/Events
ui_draw_vision_header(s);
if ((*s->sm)["controlsState"].getControlsState().getAlertSize() == cereal::ControlsState::AlertSize::NONE) {
ui_draw_vision_face(s);
}
}
static void ui_draw_background(UIState *s) {
const QColor &color = bg_colors[s->status];
glClearColor(color.redF(), color.greenF(), color.blueF(), 1.0);
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
}
void ui_draw(UIState *s, int w, int h) {
s->viz_rect = Rect{bdr_s, bdr_s, w - 2 * bdr_s, h - 2 * bdr_s};
const bool draw_vision = s->scene.started && s->vipc_client->connected;
// GL drawing functions
ui_draw_background(s);
if (draw_vision) {
ui_draw_vision_frame(s);
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glViewport(0, 0, s->fb_w, s->fb_h);
// NVG drawing functions - should be no GL inside NVG frame
nvgBeginFrame(s->vg, s->fb_w, s->fb_h, 1.0f);
if (draw_vision) {
ui_draw_vision(s);
}
nvgEndFrame(s->vg);
glDisable(GL_BLEND);
}
void ui_draw_image(const UIState *s, const Rect &r, const char *name, float alpha) {
nvgBeginPath(s->vg);
NVGpaint imgPaint = nvgImagePattern(s->vg, r.x, r.y, r.w, r.h, 0, s->images.at(name), alpha);
nvgRect(s->vg, r.x, r.y, r.w, r.h);
nvgFillPaint(s->vg, imgPaint);
nvgFill(s->vg);
}
void ui_draw_rect(NVGcontext *vg, const Rect &r, NVGcolor color, int width, float radius) {
nvgBeginPath(vg);
radius > 0 ? nvgRoundedRect(vg, r.x, r.y, r.w, r.h, radius) : nvgRect(vg, r.x, r.y, r.w, r.h);
nvgStrokeColor(vg, color);
nvgStrokeWidth(vg, width);
nvgStroke(vg);
}
static inline void fill_rect(NVGcontext *vg, const Rect &r, const NVGcolor *color, const NVGpaint *paint, float radius) {
nvgBeginPath(vg);
radius > 0 ? nvgRoundedRect(vg, r.x, r.y, r.w, r.h, radius) : nvgRect(vg, r.x, r.y, r.w, r.h);
if (color) nvgFillColor(vg, *color);
if (paint) nvgFillPaint(vg, *paint);
nvgFill(vg);
}
void ui_fill_rect(NVGcontext *vg, const Rect &r, const NVGcolor &color, float radius) {
fill_rect(vg, r, &color, nullptr, radius);
}
void ui_fill_rect(NVGcontext *vg, const Rect &r, const NVGpaint &paint, float radius) {
fill_rect(vg, r, nullptr, &paint, radius);
}
static const char frame_vertex_shader[] =
#ifdef NANOVG_GL3_IMPLEMENTATION
"#version 150 core\n"
#else
"#version 300 es\n"
#endif
"in vec4 aPosition;\n"
"in vec4 aTexCoord;\n"
"uniform mat4 uTransform;\n"
"out vec4 vTexCoord;\n"
"void main() {\n"
" gl_Position = uTransform * aPosition;\n"
" vTexCoord = aTexCoord;\n"
"}\n";
static const char frame_fragment_shader[] =
#ifdef NANOVG_GL3_IMPLEMENTATION
"#version 150 core\n"
#else
"#version 300 es\n"
#endif
"precision mediump float;\n"
"uniform sampler2D uTexture;\n"
"in vec4 vTexCoord;\n"
"out vec4 colorOut;\n"
"void main() {\n"
" colorOut = texture(uTexture, vTexCoord.xy);\n"
#ifdef QCOM
" vec3 dz = vec3(0.0627f, 0.0627f, 0.0627f);\n"
" colorOut.rgb = ((vec3(1.0f, 1.0f, 1.0f) - dz) * colorOut.rgb / vec3(1.0f, 1.0f, 1.0f)) + dz;\n"
#endif
"}\n";
static const mat4 device_transform = {{
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
}};
void ui_nvg_init(UIState *s) {
// init drawing
// on EON, we enable MSAA
s->vg = Hardware::EON() ? nvgCreate(0) : nvgCreate(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG);
assert(s->vg);
// init fonts
std::pair<const char *, const char *> fonts[] = {
{"sans-regular", "../assets/fonts/opensans_regular.ttf"},
{"sans-semibold", "../assets/fonts/opensans_semibold.ttf"},
{"sans-bold", "../assets/fonts/opensans_bold.ttf"},
};
for (auto [name, file] : fonts) {
int font_id = nvgCreateFont(s->vg, name, file);
assert(font_id >= 0);
}
// init images
std::vector<std::pair<const char *, const char *>> images = {
{"wheel", "../assets/img_chffr_wheel.png"},
{"driver_face", "../assets/img_driver_face.png"},
};
for (auto [name, file] : images) {
s->images[name] = nvgCreateImage(s->vg, file, 1);
assert(s->images[name] != 0);
}
// init gl
s->gl_shader = std::make_unique<GLShader>(frame_vertex_shader, frame_fragment_shader);
GLint frame_pos_loc = glGetAttribLocation(s->gl_shader->prog, "aPosition");
GLint frame_texcoord_loc = glGetAttribLocation(s->gl_shader->prog, "aTexCoord");
glViewport(0, 0, s->fb_w, s->fb_h);
glDisable(GL_DEPTH_TEST);
assert(glGetError() == GL_NO_ERROR);
float x1 = 1.0, x2 = 0.0, y1 = 1.0, y2 = 0.0;
const uint8_t frame_indicies[] = {0, 1, 2, 0, 2, 3};
const float frame_coords[4][4] = {
{-1.0, -1.0, x2, y1}, //bl
{-1.0, 1.0, x2, y2}, //tl
{ 1.0, 1.0, x1, y2}, //tr
{ 1.0, -1.0, x1, y1}, //br
};
glGenVertexArrays(1, &s->frame_vao);
glBindVertexArray(s->frame_vao);
glGenBuffers(1, &s->frame_vbo);
glBindBuffer(GL_ARRAY_BUFFER, s->frame_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(frame_coords), frame_coords, GL_STATIC_DRAW);
glEnableVertexAttribArray(frame_pos_loc);
glVertexAttribPointer(frame_pos_loc, 2, GL_FLOAT, GL_FALSE,
sizeof(frame_coords[0]), (const void *)0);
glEnableVertexAttribArray(frame_texcoord_loc);
glVertexAttribPointer(frame_texcoord_loc, 2, GL_FLOAT, GL_FALSE,
sizeof(frame_coords[0]), (const void *)(sizeof(float) * 2));
glGenBuffers(1, &s->frame_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->frame_ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(frame_indicies), frame_indicies, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
ui_resize(s, s->fb_w, s->fb_h);
}
void ui_resize(UIState *s, int width, int height){
s->fb_w = width;
s->fb_h = height;
auto intrinsic_matrix = s->wide_camera ? ecam_intrinsic_matrix : fcam_intrinsic_matrix;
s->zoom = zoom / intrinsic_matrix.v[0];
if (s->wide_camera) {
s->zoom *= 0.5;
}
s->video_rect = Rect{bdr_s, bdr_s, s->fb_w - 2 * bdr_s, s->fb_h - 2 * bdr_s};
float zx = s->zoom * 2 * intrinsic_matrix.v[2] / s->video_rect.w;
float zy = s->zoom * 2 * intrinsic_matrix.v[5] / s->video_rect.h;
const mat4 frame_transform = {{
zx, 0.0, 0.0, 0.0,
0.0, zy, 0.0, -y_offset / s->video_rect.h * 2,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
}};
s->rear_frame_mat = matmul(device_transform, frame_transform);
// Apply transformation such that video pixel coordinates match video
// 1) Put (0, 0) in the middle of the video
nvgTranslate(s->vg, s->video_rect.x + s->video_rect.w / 2, s->video_rect.y + s->video_rect.h / 2 + y_offset);
// 2) Apply same scaling as video
nvgScale(s->vg, s->zoom, s->zoom);
// 3) Put (0, 0) in top left corner of video
nvgTranslate(s->vg, -intrinsic_matrix.v[2], -intrinsic_matrix.v[5]);
nvgCurrentTransform(s->vg, s->car_space_transform);
nvgResetTransform(s->vg);
}
-11
View File
@@ -1,11 +0,0 @@
#pragma once
#include "selfdrive/ui/ui.h"
void ui_draw(UIState *s, int w, int h);
void ui_draw_image(const UIState *s, const Rect &r, const char *name, float alpha);
void ui_draw_rect(NVGcontext *vg, const Rect &r, NVGcolor color, int width, float radius = 0);
void ui_fill_rect(NVGcontext *vg, const Rect &r, const NVGpaint &paint, float radius = 0);
void ui_fill_rect(NVGcontext *vg, const Rect &r, const NVGcolor &color, float radius = 0);
void ui_nvg_init(UIState *s);
void ui_resize(UIState *s, int width, int height);
-141
View File
@@ -1,141 +0,0 @@
#include "selfdrive/ui/qt/api.h"
#include <QDateTime>
#include <QDebug>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QRandomGenerator>
#include <QString>
#include <QTimer>
#include <QWidget>
#include "selfdrive/common/params.h"
#include "selfdrive/common/util.h"
#include "selfdrive/hardware/hw.h"
const std::string private_key_path =
Hardware::PC() ? util::getenv_default("HOME", "/.comma/persist/comma/id_rsa", "/persist/comma/id_rsa")
: "/persist/comma/id_rsa";
QByteArray CommaApi::rsa_sign(const QByteArray &data) {
auto file = QFile(private_key_path.c_str());
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << "No RSA private key found, please run manager.py or registration.py";
return QByteArray();
}
auto key = file.readAll();
file.close();
file.deleteLater();
BIO* mem = BIO_new_mem_buf(key.data(), key.size());
assert(mem);
RSA* rsa_private = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL);
assert(rsa_private);
auto sig = QByteArray();
sig.resize(RSA_size(rsa_private));
unsigned int sig_len;
int ret = RSA_sign(NID_sha256, (unsigned char*)data.data(), data.size(), (unsigned char*)sig.data(), &sig_len, rsa_private);
assert(ret == 1);
assert(sig_len == sig.size());
BIO_free(mem);
RSA_free(rsa_private);
return sig;
}
QString CommaApi::create_jwt(const QVector<QPair<QString, QJsonValue>> &payloads, int expiry) {
QString dongle_id = QString::fromStdString(Params().get("DongleId"));
QJsonObject header;
header.insert("alg", "RS256");
QJsonObject payload;
payload.insert("identity", dongle_id);
auto t = QDateTime::currentSecsSinceEpoch();
payload.insert("nbf", t);
payload.insert("iat", t);
payload.insert("exp", t + expiry);
for (auto &load : payloads) {
payload.insert(load.first, load.second);
}
auto b64_opts = QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals;
QString jwt = QJsonDocument(header).toJson(QJsonDocument::Compact).toBase64(b64_opts) + '.' +
QJsonDocument(payload).toJson(QJsonDocument::Compact).toBase64(b64_opts);
auto hash = QCryptographicHash::hash(jwt.toUtf8(), QCryptographicHash::Sha256);
auto sig = rsa_sign(hash);
jwt += '.' + sig.toBase64(b64_opts);
return jwt;
}
HttpRequest::HttpRequest(QObject *parent, const QString &requestURL, const QString &cache_key, bool create_jwt_) : cache_key(cache_key), create_jwt(create_jwt_), QObject(parent) {
networkAccessManager = new QNetworkAccessManager(this);
reply = NULL;
networkTimer = new QTimer(this);
networkTimer->setSingleShot(true);
networkTimer->setInterval(20000);
connect(networkTimer, &QTimer::timeout, this, &HttpRequest::requestTimeout);
sendRequest(requestURL);
if (!cache_key.isEmpty()) {
if (std::string cached_resp = Params().get(cache_key.toStdString()); !cached_resp.empty()) {
QTimer::singleShot(0, [=]() { emit receivedResponse(QString::fromStdString(cached_resp)); });
}
}
}
void HttpRequest::sendRequest(const QString &requestURL){
QString token;
if(create_jwt) {
token = CommaApi::create_jwt();
} else {
QString token_json = QString::fromStdString(util::read_file(util::getenv_default("HOME", "/.comma/auth.json", "/.comma/auth.json")));
QJsonDocument json_d = QJsonDocument::fromJson(token_json.toUtf8());
token = json_d["access_token"].toString();
}
QNetworkRequest request;
request.setUrl(QUrl(requestURL));
request.setRawHeader(QByteArray("Authorization"), ("JWT " + token).toUtf8());
reply = networkAccessManager->get(request);
networkTimer->start();
connect(reply, &QNetworkReply::finished, this, &HttpRequest::requestFinished);
}
void HttpRequest::requestTimeout(){
reply->abort();
}
// This function should always emit something
void HttpRequest::requestFinished(){
if (reply->error() != QNetworkReply::OperationCanceledError) {
networkTimer->stop();
QString response = reply->readAll();
if (reply->error() == QNetworkReply::NoError) {
// save to cache
if (!cache_key.isEmpty()) {
Params().put(cache_key.toStdString(), response.toStdString());
}
emit receivedResponse(response);
} else {
if (!cache_key.isEmpty()) {
Params().remove(cache_key.toStdString());
}
qDebug() << reply->errorString();
emit failedResponse(reply->errorString());
}
} else {
emit timeoutResponse("timeout");
}
reply->deleteLater();
reply = NULL;
}
-51
View File
@@ -1,51 +0,0 @@
#pragma once
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <QCryptographicHash>
#include <QJsonValue>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QPair>
#include <QString>
#include <QVector>
#include <QWidget>
#include <atomic>
class CommaApi : public QObject {
Q_OBJECT
public:
static QByteArray rsa_sign(const QByteArray &data);
static QString create_jwt(const QVector<QPair<QString, QJsonValue>> &payloads = {}, int expiry = 3600);
};
/**
* Makes a request to the request endpoint.
*/
class HttpRequest : public QObject {
Q_OBJECT
public:
explicit HttpRequest(QObject* parent, const QString &requestURL, const QString &cache_key = "", bool create_jwt_ = true);
QNetworkReply *reply;
void sendRequest(const QString &requestURL);
private:
QNetworkAccessManager *networkAccessManager;
QTimer *networkTimer;
QString cache_key;
bool create_jwt;
private slots:
void requestTimeout();
void requestFinished();
signals:
void receivedResponse(const QString &response);
void failedResponse(const QString &errorString);
void timeoutResponse(const QString &errorString);
};
-210
View File
@@ -1,210 +0,0 @@
#include "selfdrive/ui/qt/home.h"
#include <QDateTime>
#include <QHBoxLayout>
#include <QMouseEvent>
#include <QVBoxLayout>
#include "selfdrive/common/params.h"
#include "selfdrive/common/swaglog.h"
#include "selfdrive/common/timing.h"
#include "selfdrive/common/util.h"
#include "selfdrive/ui/qt/widgets/drive_stats.h"
#include "selfdrive/ui/qt/widgets/setup.h"
// HomeWindow: the container for the offroad and onroad UIs
HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
QHBoxLayout *layout = new QHBoxLayout(this);
layout->setMargin(0);
layout->setSpacing(0);
sidebar = new Sidebar(this);
layout->addWidget(sidebar);
QObject::connect(this, &HomeWindow::update, sidebar, &Sidebar::updateState);
QObject::connect(sidebar, &Sidebar::openSettings, this, &HomeWindow::openSettings);
slayout = new QStackedLayout();
layout->addLayout(slayout);
onroad = new OnroadWindow(this);
slayout->addWidget(onroad);
QObject::connect(this, &HomeWindow::update, onroad, &OnroadWindow::update);
QObject::connect(this, &HomeWindow::offroadTransitionSignal, onroad, &OnroadWindow::offroadTransitionSignal);
home = new OffroadHome();
slayout->addWidget(home);
QObject::connect(this, &HomeWindow::openSettings, home, &OffroadHome::refresh);
driver_view = new DriverViewWindow(this);
connect(driver_view, &DriverViewWindow::done, [=] {
showDriverView(false);
});
slayout->addWidget(driver_view);
setLayout(layout);
}
void HomeWindow::offroadTransition(bool offroad) {
if (offroad) {
slayout->setCurrentWidget(home);
} else {
slayout->setCurrentWidget(onroad);
}
sidebar->setVisible(offroad);
emit offroadTransitionSignal(offroad);
}
void HomeWindow::showDriverView(bool show) {
if (show) {
emit closeSettings();
slayout->setCurrentWidget(driver_view);
} else {
slayout->setCurrentWidget(home);
}
sidebar->setVisible(show == false);
}
void HomeWindow::mousePressEvent(QMouseEvent* e) {
// Handle sidebar collapsing
if (onroad->isVisible() && (!sidebar->isVisible() || e->x() > sidebar->width())) {
// TODO: Handle this without exposing pointer to map widget
// Hide map first if visible, then hide sidebar
if (onroad->map != nullptr && onroad->map->isVisible()){
onroad->map->setVisible(false);
} else if (!sidebar->isVisible()) {
sidebar->setVisible(true);
} else {
sidebar->setVisible(false);
if (onroad->map != nullptr) onroad->map->setVisible(true);
}
}
}
// OffroadHome: the offroad home page
OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
QVBoxLayout* main_layout = new QVBoxLayout();
main_layout->setMargin(50);
// top header
QHBoxLayout* header_layout = new QHBoxLayout();
date = new QLabel();
date->setStyleSheet(R"(font-size: 55px;)");
header_layout->addWidget(date, 0, Qt::AlignHCenter | Qt::AlignLeft);
alert_notification = new QPushButton();
alert_notification->setVisible(false);
QObject::connect(alert_notification, &QPushButton::released, this, &OffroadHome::openAlerts);
header_layout->addWidget(alert_notification, 0, Qt::AlignHCenter | Qt::AlignRight);
std::string brand = Params().getBool("Passive") ? "dashcam" : "openpilot";
QLabel* version = new QLabel(QString::fromStdString(brand + " v" + Params().get("Version")));
version->setStyleSheet(R"(font-size: 55px;)");
header_layout->addWidget(version, 0, Qt::AlignHCenter | Qt::AlignRight);
main_layout->addLayout(header_layout);
// main content
main_layout->addSpacing(25);
center_layout = new QStackedLayout();
QHBoxLayout* statsAndSetup = new QHBoxLayout();
statsAndSetup->setMargin(0);
DriveStats* drive = new DriveStats;
drive->setFixedSize(800, 800);
statsAndSetup->addWidget(drive);
SetupWidget* setup = new SetupWidget;
statsAndSetup->addWidget(setup);
QWidget* statsAndSetupWidget = new QWidget();
statsAndSetupWidget->setLayout(statsAndSetup);
center_layout->addWidget(statsAndSetupWidget);
alerts_widget = new OffroadAlert();
QObject::connect(alerts_widget, &OffroadAlert::closeAlerts, this, &OffroadHome::closeAlerts);
center_layout->addWidget(alerts_widget);
center_layout->setAlignment(alerts_widget, Qt::AlignCenter);
main_layout->addLayout(center_layout, 1);
// set up refresh timer
timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, this, &OffroadHome::refresh);
timer->start(10 * 1000);
setLayout(main_layout);
setStyleSheet(R"(
OffroadHome {
background-color: black;
}
* {
color: white;
}
)");
}
void OffroadHome::showEvent(QShowEvent *event) {
refresh();
}
void OffroadHome::openAlerts() {
center_layout->setCurrentIndex(1);
}
void OffroadHome::closeAlerts() {
center_layout->setCurrentIndex(0);
}
void OffroadHome::refresh() {
bool first_refresh = !date->text().size();
if (!isVisible() && !first_refresh) {
return;
}
date->setText(QDateTime::currentDateTime().toString("dddd, MMMM d"));
// update alerts
alerts_widget->refresh();
if (!alerts_widget->alertCount && !alerts_widget->updateAvailable) {
emit closeAlerts();
alert_notification->setVisible(false);
return;
}
if (alerts_widget->updateAvailable) {
alert_notification->setText("UPDATE");
} else {
int alerts = alerts_widget->alertCount;
alert_notification->setText(QString::number(alerts) + " ALERT" + (alerts == 1 ? "" : "S"));
}
if (!alert_notification->isVisible() && !first_refresh) {
emit openAlerts();
}
alert_notification->setVisible(true);
// Red background for alerts, blue for update available
QString style = QString(R"(
padding: 15px;
padding-left: 30px;
padding-right: 30px;
border: 1px solid;
border-radius: 5px;
font-size: 40px;
font-weight: 500;
background-color: #E22C2C;
)");
if (alerts_widget->updateAvailable) {
style.replace("#E22C2C", "#364DEF");
}
alert_notification->setStyleSheet(style);
}
-67
View File
@@ -1,67 +0,0 @@
#pragma once
#include <QFrame>
#include <QLabel>
#include <QPushButton>
#include <QStackedLayout>
#include <QTimer>
#include <QWidget>
#include "selfdrive/ui/qt/offroad/driverview.h"
#include "selfdrive/ui/qt/onroad.h"
#include "selfdrive/ui/qt/sidebar.h"
#include "selfdrive/ui/qt/widgets/offroad_alerts.h"
#include "selfdrive/ui/ui.h"
class OffroadHome : public QFrame {
Q_OBJECT
public:
explicit OffroadHome(QWidget* parent = 0);
protected:
void showEvent(QShowEvent *event) override;
private:
QTimer* timer;
QLabel* date;
QStackedLayout* center_layout;
OffroadAlert* alerts_widget;
QPushButton* alert_notification;
public slots:
void closeAlerts();
void openAlerts();
void refresh();
};
class HomeWindow : public QWidget {
Q_OBJECT
public:
explicit HomeWindow(QWidget* parent = 0);
signals:
void openSettings();
void closeSettings();
// forwarded signals
void displayPowerChanged(bool on);
void update(const UIState &s);
void offroadTransitionSignal(bool offroad);
public slots:
void offroadTransition(bool offroad);
void showDriverView(bool show);
protected:
void mousePressEvent(QMouseEvent* e) override;
private:
Sidebar *sidebar;
OffroadHome *home;
OnroadWindow *onroad;
DriverViewWindow *driver_view;
QStackedLayout *slayout;
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

-109
View File
@@ -1,109 +0,0 @@
#include "selfdrive/ui/qt/offroad/driverview.h"
#include <QPainter>
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/util.h"
const int FACE_IMG_SIZE = 130;
DriverViewWindow::DriverViewWindow(QWidget* parent) : QWidget(parent) {
setAttribute(Qt::WA_OpaquePaintEvent);
layout = new QStackedLayout(this);
layout->setStackingMode(QStackedLayout::StackAll);
cameraView = new CameraViewWidget(VISION_STREAM_RGB_FRONT, this);
layout->addWidget(cameraView);
scene = new DriverViewScene(this);
connect(cameraView, &CameraViewWidget::frameUpdated, scene, &DriverViewScene::frameUpdated);
layout->addWidget(scene);
layout->setCurrentWidget(scene);
}
void DriverViewWindow::mousePressEvent(QMouseEvent* e) {
emit done();
}
DriverViewScene::DriverViewScene(QWidget* parent) : sm({"driverState"}), QWidget(parent) {
face = QImage("../assets/img_driver_face.png").scaled(FACE_IMG_SIZE, FACE_IMG_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
void DriverViewScene::showEvent(QShowEvent* event) {
frame_updated = false;
is_rhd = params.getBool("IsRHD");
params.putBool("IsDriverViewEnabled", true);
}
void DriverViewScene::hideEvent(QHideEvent* event) {
params.putBool("IsDriverViewEnabled", false);
}
void DriverViewScene::frameUpdated() {
frame_updated = true;
sm.update(0);
update();
}
void DriverViewScene::paintEvent(QPaintEvent* event) {
QPainter p(this);
// startup msg
if (!frame_updated) {
p.setPen(QColor(0xff, 0xff, 0xff));
p.setRenderHint(QPainter::TextAntialiasing);
configFont(p, "Inter", 100, "Bold");
p.drawText(geometry(), Qt::AlignCenter, "camera starting");
return;
}
const int width = 4 * height() / 3;
const QRect rect2 = {rect().center().x() - width / 2, rect().top(), width, rect().height()};
const QRect valid_rect = {is_rhd ? rect2.right() - rect2.height() / 2 : rect2.left(), rect2.top(), rect2.height() / 2, rect2.height()};
// blackout
const int blackout_x_r = valid_rect.right();
const QRect& blackout_rect = Hardware::TICI() ? rect() : rect2;
const int blackout_w_r = blackout_rect.right() - valid_rect.right();
const int blackout_x_l = blackout_rect.left();
const int blackout_w_l = valid_rect.left() - blackout_x_l;
QColor bg(0, 0, 0, 140);
p.setPen(QPen(bg));
p.setBrush(QBrush(bg));
p.drawRect(blackout_x_l, rect2.top(), blackout_w_l, rect2.height());
p.drawRect(blackout_x_r, rect2.top(), blackout_w_r, rect2.height());
// face bounding box
cereal::DriverState::Reader driver_state = sm["driverState"].getDriverState();
bool face_detected = driver_state.getFaceProb() > 0.4;
if (face_detected) {
auto fxy_list = driver_state.getFacePosition();
float face_x = fxy_list[0];
float face_y = fxy_list[1];
int fbox_x = valid_rect.center().x() + (is_rhd ? face_x : -face_x) * valid_rect.width();
int fbox_y = valid_rect.center().y() + face_y * valid_rect.height();
float alpha = 0.2;
face_x = std::abs(face_x);
face_y = std::abs(face_y);
if (face_x <= 0.35 && face_y <= 0.4) {
alpha = 0.8 - (face_x > face_y ? face_x : face_y) * 0.6 / 0.375;
}
const int box_size = 0.6 * rect2.height() / 2;
QPen pen(QColor(255, 255, 255, alpha * 255));
pen.setWidth(10);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
p.drawRoundedRect(fbox_x - box_size / 2, fbox_y - box_size / 2, box_size, box_size, 35.0, 35.0);
}
// icon
const int img_offset = 30;
const int img_x = is_rhd ? rect2.right() - FACE_IMG_SIZE - img_offset : rect2.left() + img_offset;
const int img_y = rect2.bottom() - FACE_IMG_SIZE - img_offset;
p.setPen(Qt::NoPen);
p.setOpacity(face_detected ? 1.0 : 0.3);
p.drawImage(img_x, img_y, face);
}
-48
View File
@@ -1,48 +0,0 @@
#pragma once
#include <memory>
#include <QStackedLayout>
#include "selfdrive/common/util.h"
#include "selfdrive/ui/qt/widgets/cameraview.h"
class DriverViewScene : public QWidget {
Q_OBJECT
public:
explicit DriverViewScene(QWidget *parent);
public slots:
void frameUpdated();
protected:
void showEvent(QShowEvent *event) override;
void hideEvent(QHideEvent *event) override;
void paintEvent(QPaintEvent *event) override;
private:
Params params;
SubMaster sm;
QImage face;
bool is_rhd = false;
bool frame_updated = false;
};
class DriverViewWindow : public QWidget {
Q_OBJECT
public:
explicit DriverViewWindow(QWidget *parent);
signals:
void done();
protected:
void mousePressEvent(QMouseEvent* e) override;
private:
CameraViewWidget *cameraView;
DriverViewScene *scene;
QStackedLayout *layout;
};
-236
View File
@@ -1,236 +0,0 @@
#include "networking.h"
#include <QDebug>
#include <QHBoxLayout>
#include <QLabel>
#include <QPixmap>
#include "selfdrive/ui/qt/widgets/scrollview.h"
#include "selfdrive/ui/qt/util.h"
// Networking functions
Networking::Networking(QWidget* parent, bool show_advanced) : QWidget(parent), show_advanced(show_advanced){
s = new QStackedLayout;
QLabel* warning = new QLabel("Network manager is inactive!");
warning->setAlignment(Qt::AlignCenter);
warning->setStyleSheet(R"(font-size: 65px;)");
s->addWidget(warning);
setLayout(s);
QTimer* timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, this, &Networking::refresh);
timer->start(5000);
attemptInitialization();
}
void Networking::attemptInitialization(){
// Checks if network manager is active
try {
wifi = new WifiManager(this);
} catch (std::exception &e) {
return;
}
connect(wifi, &WifiManager::wrongPassword, this, &Networking::wrongPassword);
QVBoxLayout* vlayout = new QVBoxLayout;
if (show_advanced) {
QPushButton* advancedSettings = new QPushButton("Advanced");
advancedSettings->setStyleSheet("margin-right: 30px;");
advancedSettings->setFixedSize(350, 100);
connect(advancedSettings, &QPushButton::released, [=](){ s->setCurrentWidget(an); });
vlayout->addSpacing(10);
vlayout->addWidget(advancedSettings, 0, Qt::AlignRight);
vlayout->addSpacing(10);
}
wifiWidget = new WifiUI(this, wifi);
connect(wifiWidget, &WifiUI::connectToNetwork, this, &Networking::connectToNetwork);
vlayout->addWidget(new ScrollView(wifiWidget, this), 1);
QWidget* wifiScreen = new QWidget(this);
wifiScreen->setLayout(vlayout);
s->addWidget(wifiScreen);
an = new AdvancedNetworking(this, wifi);
connect(an, &AdvancedNetworking::backPress, [=](){s->setCurrentWidget(wifiScreen);});
s->addWidget(an);
setStyleSheet(R"(
QPushButton {
font-size: 50px;
margin: 0px;
padding: 15px;
border-width: 0;
border-radius: 30px;
color: #dddddd;
background-color: #444444;
}
QPushButton:disabled {
color: #777777;
background-color: #222222;
}
)");
s->setCurrentWidget(wifiScreen);
ui_setup_complete = true;
}
void Networking::refresh(){
if (!this->isVisible()) {
return;
}
if (!ui_setup_complete) {
attemptInitialization();
if (!ui_setup_complete) {
return;
}
}
wifiWidget->refresh();
an->refresh();
}
void Networking::connectToNetwork(Network n) {
if (n.security_type == SecurityType::OPEN) {
wifi->connect(n);
} else if (n.security_type == SecurityType::WPA) {
QString pass = InputDialog::getText("Enter password for \"" + n.ssid + "\"", 8);
wifi->connect(n, pass);
}
}
void Networking::wrongPassword(QString ssid) {
for (Network n : wifi->seen_networks) {
if (n.ssid == ssid) {
QString pass = InputDialog::getText("Wrong password for \"" + n.ssid +"\"", 8);
wifi->connect(n, pass);
return;
}
}
}
// AdvancedNetworking functions
AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWidget(parent), wifi(wifi){
QVBoxLayout* vlayout = new QVBoxLayout;
vlayout->setMargin(40);
vlayout->setSpacing(20);
// Back button
QPushButton* back = new QPushButton("Back");
back->setFixedSize(500, 100);
connect(back, &QPushButton::released, [=](){emit backPress();});
vlayout->addWidget(back, 0, Qt::AlignLeft);
// Enable tethering layout
ToggleControl *tetheringToggle = new ToggleControl("Enable Tethering", "", "", wifi->tetheringEnabled());
vlayout->addWidget(tetheringToggle);
QObject::connect(tetheringToggle, &ToggleControl::toggleFlipped, this, &AdvancedNetworking::toggleTethering);
vlayout->addWidget(horizontal_line(), 0);
// Change tethering password
editPasswordButton = new ButtonControl("Tethering Password", "EDIT", "", [=](){
QString pass = InputDialog::getText("Enter new tethering password", 8);
if (pass.size()) {
wifi->changeTetheringPassword(pass);
}
});
vlayout->addWidget(editPasswordButton, 0);
vlayout->addWidget(horizontal_line(), 0);
// IP address
ipLabel = new LabelControl("IP Address", wifi->ipv4_address);
vlayout->addWidget(ipLabel, 0);
vlayout->addWidget(horizontal_line(), 0);
// SSH keys
vlayout->addWidget(new SshToggle());
vlayout->addWidget(horizontal_line(), 0);
vlayout->addWidget(new SshControl());
vlayout->addStretch(1);
setLayout(vlayout);
}
void AdvancedNetworking::refresh(){
ipLabel->setText(wifi->ipv4_address);
update();
}
void AdvancedNetworking::toggleTethering(bool enable) {
if (enable) {
wifi->enableTethering();
} else {
wifi->disableTethering();
}
editPasswordButton->setEnabled(!enable);
}
// WifiUI functions
WifiUI::WifiUI(QWidget *parent, WifiManager* wifi) : QWidget(parent), wifi(wifi) {
vlayout = new QVBoxLayout;
// Scan on startup
QLabel *scanning = new QLabel("Scanning for networks");
scanning->setStyleSheet(R"(font-size: 65px;)");
vlayout->addWidget(scanning, 0, Qt::AlignCenter);
vlayout->setSpacing(25);
setLayout(vlayout);
}
void WifiUI::refresh() {
wifi->request_scan();
wifi->refreshNetworks();
clearLayout(vlayout);
connectButtons = new QButtonGroup(this); // TODO check if this is a leak
QObject::connect(connectButtons, qOverload<QAbstractButton*>(&QButtonGroup::buttonClicked), this, &WifiUI::handleButton);
int i = 0;
for (Network &network : wifi->seen_networks) {
QHBoxLayout *hlayout = new QHBoxLayout;
hlayout->addSpacing(50);
QLabel *ssid_label = new QLabel(QString::fromUtf8(network.ssid));
ssid_label->setStyleSheet("font-size: 55px;");
hlayout->addWidget(ssid_label, 1, Qt::AlignLeft);
// TODO: don't use images for this
// strength indicator
unsigned int strength_scale = network.strength / 17;
QPixmap pix("../assets/images/network_" + QString::number(strength_scale) + ".png");
QLabel *icon = new QLabel();
icon->setPixmap(pix.scaledToWidth(100, Qt::SmoothTransformation));
icon->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
hlayout->addWidget(icon, 0, Qt::AlignRight);
// connect button
QPushButton* btn = new QPushButton(network.security_type == SecurityType::UNSUPPORTED ? "Unsupported" : (network.connected == ConnectedType::CONNECTED ? "Connected" : (network.connected == ConnectedType::CONNECTING ? "Connecting" : "Connect")));
btn->setDisabled(network.connected == ConnectedType::CONNECTED || network.connected == ConnectedType::CONNECTING || network.security_type == SecurityType::UNSUPPORTED);
btn->setFixedWidth(350);
hlayout->addWidget(btn, 0, Qt::AlignRight);
connectButtons->addButton(btn, i);
vlayout->addLayout(hlayout, 1);
// Don't add the last horizontal line
if (i+1 < wifi->seen_networks.size()) {
vlayout->addWidget(horizontal_line(), 0);
}
i++;
}
vlayout->addStretch(3);
}
void WifiUI::handleButton(QAbstractButton* button) {
QPushButton* btn = static_cast<QPushButton*>(button);
Network n = wifi->seen_networks[connectButtons->id(btn)];
emit connectToNetwork(n);
}
-77
View File
@@ -1,77 +0,0 @@
#pragma once
#include <QButtonGroup>
#include <QPushButton>
#include <QStackedWidget>
#include <QVBoxLayout>
#include <QWidget>
#include "selfdrive/ui/qt/offroad/wifiManager.h"
#include "selfdrive/ui/qt/widgets/input.h"
#include "selfdrive/ui/qt/widgets/ssh_keys.h"
#include "selfdrive/ui/qt/widgets/toggle.h"
class WifiUI : public QWidget {
Q_OBJECT
public:
explicit WifiUI(QWidget *parent = 0, WifiManager* wifi = 0);
private:
WifiManager *wifi = nullptr;
QVBoxLayout *vlayout;
QButtonGroup *connectButtons;
bool tetheringEnabled;
signals:
void connectToNetwork(Network n);
public slots:
void refresh();
void handleButton(QAbstractButton* m_button);
};
class AdvancedNetworking : public QWidget {
Q_OBJECT
public:
explicit AdvancedNetworking(QWidget* parent = 0, WifiManager* wifi = 0);
private:
LabelControl* ipLabel;
ButtonControl* editPasswordButton;
WifiManager* wifi = nullptr;
signals:
void backPress();
public slots:
void toggleTethering(bool enable);
void refresh();
};
class Networking : public QWidget {
Q_OBJECT
public:
explicit Networking(QWidget* parent = 0, bool show_advanced = true);
private:
QStackedLayout* s = nullptr; // nm_warning, wifiScreen, advanced
QWidget* wifiScreen = nullptr;
AdvancedNetworking* an = nullptr;
bool ui_setup_complete = false;
bool show_advanced;
Network selectedNetwork;
WifiUI* wifiWidget;
WifiManager* wifi = nullptr;
void attemptInitialization();
private slots:
void connectToNetwork(Network n);
void refresh();
void wrongPassword(QString ssid);
};
-227
View File
@@ -1,227 +0,0 @@
#include "onboarding.h"
#include <QDesktopWidget>
#include <QLabel>
#include <QPainter>
#include <QQmlContext>
#include <QQuickWidget>
#include <QVBoxLayout>
#include "selfdrive/common/params.h"
#include "selfdrive/common/util.h"
#include "selfdrive/ui/qt/home.h"
#include "selfdrive/ui/qt/widgets/input.h"
void TrainingGuide::mouseReleaseEvent(QMouseEvent *e) {
QPoint touch = QPoint(e->x(), e->y()) - imageCorner;
//qDebug() << touch.x() << ", " << touch.y();
// Check for restart
if (currentIndex == (boundingBox.size() - 1) && 200 <= touch.x() && touch.x() <= 920 &&
760 <= touch.y() && touch.y() <= 960) {
currentIndex = 0;
} else if (boundingBox[currentIndex][0] <= touch.x() && touch.x() <= boundingBox[currentIndex][1] &&
boundingBox[currentIndex][2] <= touch.y() && touch.y() <= boundingBox[currentIndex][3]) {
currentIndex += 1;
}
if (currentIndex >= boundingBox.size()) {
emit completedTraining();
} else {
image.load("../assets/training/step" + QString::number(currentIndex) + ".png");
update();
}
}
void TrainingGuide::showEvent(QShowEvent *event) {
currentIndex = 0;
image.load("../assets/training/step0.png");
}
void TrainingGuide::paintEvent(QPaintEvent *event) {
QPainter painter(this);
QRect bg(0, 0, painter.device()->width(), painter.device()->height());
QBrush bgBrush("#000000");
painter.fillRect(bg, bgBrush);
QRect rect(image.rect());
rect.moveCenter(bg.center());
painter.drawImage(rect.topLeft(), image);
imageCorner = rect.topLeft();
}
void TermsPage::showEvent(QShowEvent *event) {
// late init, building QML widget takes 200ms
if (layout()) {
return;
}
QVBoxLayout *main_layout = new QVBoxLayout;
main_layout->setMargin(40);
main_layout->setSpacing(40);
QQuickWidget *text = new QQuickWidget(this);
text->setResizeMode(QQuickWidget::SizeRootObjectToView);
text->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
text->setAttribute(Qt::WA_AlwaysStackOnTop);
text->setClearColor(Qt::transparent);
QString text_view = util::read_file("../assets/offroad/tc.html").c_str();
text->rootContext()->setContextProperty("text_view", text_view);
text->rootContext()->setContextProperty("font_size", 55);
text->setSource(QUrl::fromLocalFile("qt/offroad/text_view.qml"));
main_layout->addWidget(text);
QObject *obj = (QObject*)text->rootObject();
QObject::connect(obj, SIGNAL(qmlSignal()), SLOT(enableAccept()));
QHBoxLayout* buttons = new QHBoxLayout;
main_layout->addLayout(buttons);
decline_btn = new QPushButton("Decline");
buttons->addWidget(decline_btn);
QObject::connect(decline_btn, &QPushButton::released, this, &TermsPage::declinedTerms);
buttons->addSpacing(50);
accept_btn = new QPushButton("Scroll to accept");
accept_btn->setEnabled(false);
buttons->addWidget(accept_btn);
QObject::connect(accept_btn, &QPushButton::released, this, &TermsPage::acceptedTerms);
setLayout(main_layout);
setStyleSheet(R"(
QPushButton {
padding: 50px;
font-size: 50px;
border-radius: 10px;
background-color: #292929;
}
)");
}
void TermsPage::enableAccept(){
accept_btn->setText("Accept");
accept_btn->setEnabled(true);
return;
}
void DeclinePage::showEvent(QShowEvent *event) {
if (layout()) {
return;
}
QVBoxLayout *main_layout = new QVBoxLayout;
main_layout->setMargin(40);
main_layout->setSpacing(40);
QLabel *text = new QLabel(this);
text->setText("You must accept the Terms and Conditions in order to use openpilot!");
text->setStyleSheet(R"(font-size: 50px;)");
main_layout->addWidget(text, 0, Qt::AlignCenter);
QHBoxLayout* buttons = new QHBoxLayout;
main_layout->addLayout(buttons);
back_btn = new QPushButton("Back");
buttons->addWidget(back_btn);
buttons->addSpacing(50);
QObject::connect(back_btn, &QPushButton::released, this, &DeclinePage::getBack);
uninstall_btn = new QPushButton("Decline, uninstall openpilot");
uninstall_btn->setStyleSheet("background-color: #E22C2C;");
buttons->addWidget(uninstall_btn);
QObject::connect(uninstall_btn, &QPushButton::released, [=](){
if (ConfirmationDialog::confirm("Are you sure you want to uninstall?", this)) {
Params().putBool("DoUninstall", true);
}
});
setLayout(main_layout);
setStyleSheet(R"(
QPushButton {
padding: 50px;
font-size: 50px;
border-radius: 10px;
background-color: #292929;
}
)");
}
void OnboardingWindow::updateActiveScreen() {
updateOnboardingStatus();
if (!accepted_terms) {
setCurrentIndex(0);
} else if (!training_done) {
setCurrentIndex(1);
} else {
emit onboardingDone();
}
}
OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
params = Params();
current_terms_version = params.get("TermsVersion", false);
current_training_version = params.get("TrainingVersion", false);
TermsPage* terms = new TermsPage(this);
addWidget(terms);
connect(terms, &TermsPage::acceptedTerms, [=](){
Params().put("HasAcceptedTerms", current_terms_version);
updateActiveScreen();
});
TrainingGuide* tr = new TrainingGuide(this);
connect(tr, &TrainingGuide::completedTraining, [=](){
Params().put("CompletedTrainingVersion", current_training_version);
updateActiveScreen();
});
addWidget(tr);
DeclinePage* declinePage = new DeclinePage(this);
addWidget(declinePage);
connect(terms, &TermsPage::declinedTerms, [=](){
setCurrentIndex(2);
});
connect(declinePage, &DeclinePage::getBack, [=](){
updateActiveScreen();
});
setStyleSheet(R"(
* {
color: white;
background-color: black;
}
QPushButton {
padding: 50px;
border-radius: 30px;
background-color: #292929;
}
QPushButton:disabled {
color: #777777;
background-color: #222222;
}
)");
updateActiveScreen();
}
void OnboardingWindow::updateOnboardingStatus() {
accepted_terms = params.get("HasAcceptedTerms", false).compare(current_terms_version) == 0;
training_done = params.get("CompletedTrainingVersion", false).compare(current_training_version) == 0;
}
bool OnboardingWindow::isOnboardingDone() {
updateOnboardingStatus();
return accepted_terms && training_done;
}
-115
View File
@@ -1,115 +0,0 @@
#pragma once
#include <QImage>
#include <QMouseEvent>
#include <QPushButton>
#include <QStackedWidget>
#include <QTextEdit>
#include <QWidget>
#include "selfdrive/common/params.h"
class TrainingGuide : public QFrame {
Q_OBJECT
public:
explicit TrainingGuide(QWidget *parent = 0) : QFrame(parent) {};
protected:
void showEvent(QShowEvent *event) override;
void paintEvent(QPaintEvent *event) override;
void mouseReleaseEvent(QMouseEvent* e) override;
private:
QImage image;
QPoint imageCorner;
int currentIndex = 0;
// Bounding boxes for the a given training guide step
// (minx, maxx, miny, maxy)
QVector<QVector<int>> boundingBox {
{650, 1375, 700, 900},
{1600, 1920, 0, 1080},
{1600, 1920, 0, 1080},
{1240, 1750, 480, 1080},
{1570, 1780, 620, 750},
{1600, 1920, 0, 1080},
{1630, 1740, 620, 780},
{1200, 1920, 0, 1080},
{1455, 1850, 400, 660},
{1460, 1800, 195, 520},
{1600, 1920, 0, 1080},
{1350, 1920, 65, 1080},
{1600, 1920, 0, 1080},
{1570, 1900, 130, 1000},
{1350, 1770, 500, 700},
{1600, 1920, 0, 1080},
{1600, 1920, 0, 1080},
{1000, 1800, 760, 954},
};
signals:
void completedTraining();
};
class TermsPage : public QFrame {
Q_OBJECT
public:
explicit TermsPage(QWidget *parent = 0) : QFrame(parent) {};
protected:
void showEvent(QShowEvent *event) override;
private:
QPushButton *accept_btn;
QPushButton *decline_btn;
public slots:
void enableAccept();
signals:
void acceptedTerms();
void declinedTerms();
};
class DeclinePage : public QFrame {
Q_OBJECT
public:
explicit DeclinePage(QWidget *parent = 0) : QFrame(parent) {};
protected:
void showEvent(QShowEvent *event) override;
private:
QPushButton *back_btn;
QPushButton *uninstall_btn;
signals:
void getBack();
};
class OnboardingWindow : public QStackedWidget {
Q_OBJECT
public:
explicit OnboardingWindow(QWidget *parent = 0);
bool isOnboardingDone();
private:
Params params;
std::string current_terms_version;
std::string current_training_version;
bool accepted_terms = false;
bool training_done = false;
void updateOnboardingStatus();
signals:
void onboardingDone();
void resetTrainingGuide();
public slots:
void updateActiveScreen();
};
-425
View File
@@ -1,425 +0,0 @@
#include "settings.h"
#include <cassert>
#include <string>
#ifndef QCOM
#include "selfdrive/ui/qt/offroad/networking.h"
#endif
#include "selfdrive/common/params.h"
#include "selfdrive/common/util.h"
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/qt/widgets/controls.h"
#include "selfdrive/ui/qt/widgets/input.h"
#include "selfdrive/ui/qt/widgets/offroad_alerts.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
#include "selfdrive/ui/qt/widgets/ssh_keys.h"
#include "selfdrive/ui/qt/widgets/toggle.h"
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/util.h"
TogglesPanel::TogglesPanel(QWidget *parent) : QWidget(parent) {
QVBoxLayout *toggles_list = new QVBoxLayout();
QList<ParamControl*> toggles;
toggles.append(new ParamControl("OpenpilotEnabledToggle",
"Enable openpilot",
"Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.",
"../assets/offroad/icon_openpilot.png",
this));
toggles.append(new ParamControl("IsLdwEnabled",
"Enable Lane Departure Warnings",
"Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31mph (50kph).",
"../assets/offroad/icon_warning.png",
this));
toggles.append(new ParamControl("IsRHD",
"Enable Right-Hand Drive",
"Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat.",
"../assets/offroad/icon_openpilot_mirrored.png",
this));
toggles.append(new ParamControl("IsMetric",
"Use Metric System",
"Display speed in km/h instead of mp/h.",
"../assets/offroad/icon_metric.png",
this));
toggles.append(new ParamControl("CommunityFeaturesToggle",
"Enable Community Features",
"Use features from the open source community that are not maintained or supported by comma.ai and have not been confirmed to meet the standard safety model. These features include community supported cars and community supported hardware. Be extra cautious when using these features",
"../assets/offroad/icon_shell.png",
this));
toggles.append(new ParamControl("UploadRaw",
"Upload Raw Logs",
"Upload full logs and full resolution video by default while on WiFi. If not enabled, individual logs can be marked for upload at my.comma.ai/useradmin.",
"../assets/offroad/icon_network.png",
this));
ParamControl *record_toggle = new ParamControl("RecordFront",
"Record and Upload Driver Camera",
"Upload data from the driver facing camera and help improve the driver monitoring algorithm.",
"../assets/offroad/icon_monitoring.png",
this);
toggles.append(record_toggle);
toggles.append(new ParamControl("EndToEndToggle",
"\U0001f96c Disable use of lanelines (Alpha) \U0001f96c",
"In this mode openpilot will ignore lanelines and just drive how it thinks a human would.",
"../assets/offroad/icon_road.png",
this));
if (Hardware::TICI()) {
toggles.append(new ParamControl("EnableWideCamera",
"Enable use of Wide Angle Camera",
"Use wide angle camera for driving and ui.",
"../assets/offroad/icon_openpilot.png",
this));
QObject::connect(toggles.back(), &ToggleControl::toggleFlipped, [=](bool state) {
Params().remove("CalibrationParams");
});
toggles.append(new ParamControl("EnableLteOnroad",
"Enable LTE while onroad",
"",
"../assets/offroad/icon_network.png",
this));
}
bool record_lock = Params().getBool("RecordFrontLock");
record_toggle->setEnabled(!record_lock);
for(ParamControl *toggle : toggles){
if(toggles_list->count() != 0){
toggles_list->addWidget(horizontal_line());
}
toggles_list->addWidget(toggle);
}
setLayout(toggles_list);
}
DevicePanel::DevicePanel(QWidget* parent) : QWidget(parent) {
QVBoxLayout *device_layout = new QVBoxLayout;
Params params = Params();
QString dongle = QString::fromStdString(params.get("DongleId", false));
device_layout->addWidget(new LabelControl("Dongle ID", dongle));
device_layout->addWidget(horizontal_line());
QString serial = QString::fromStdString(params.get("HardwareSerial", false));
device_layout->addWidget(new LabelControl("Serial", serial));
// offroad-only buttons
QList<ButtonControl*> offroad_btns;
offroad_btns.append(new ButtonControl("Driver Camera", "PREVIEW",
"Preview the driver facing camera to help optimize device mounting position for best driver monitoring experience. (vehicle must be off)",
[=]() { emit showDriverView(); }, "", this));
QString resetCalibDesc = "openpilot requires the device to be mounted within 4° left or right and within 5° up or down. openpilot is continuously calibrating, resetting is rarely required.";
ButtonControl *resetCalibBtn = new ButtonControl("Reset Calibration", "RESET", resetCalibDesc, [=]() {
if (ConfirmationDialog::confirm("Are you sure you want to reset calibration?", this)) {
Params().remove("CalibrationParams");
}
}, "", this);
connect(resetCalibBtn, &ButtonControl::showDescription, [=]() {
QString desc = resetCalibDesc;
std::string calib_bytes = Params().get("CalibrationParams");
if (!calib_bytes.empty()) {
try {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(calib_bytes.data(), calib_bytes.size()));
auto calib = cmsg.getRoot<cereal::Event>().getLiveCalibration();
if (calib.getCalStatus() != 0) {
double pitch = calib.getRpyCalib()[1] * (180 / M_PI);
double yaw = calib.getRpyCalib()[2] * (180 / M_PI);
desc += QString(" Your device is pointed %1° %2 and %3° %4.")
.arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? "up" : "down",
QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? "right" : "left");
}
} catch (kj::Exception) {
qInfo() << "invalid CalibrationParams";
}
}
resetCalibBtn->setDescription(desc);
});
offroad_btns.append(resetCalibBtn);
offroad_btns.append(new ButtonControl("Review Training Guide", "REVIEW",
"Review the rules, features, and limitations of openpilot", [=]() {
if (ConfirmationDialog::confirm("Are you sure you want to review the training guide?", this)) {
Params().remove("CompletedTrainingVersion");
emit reviewTrainingGuide();
}
}, "", this));
QString brand = params.getBool("Passive") ? "dashcam" : "openpilot";
offroad_btns.append(new ButtonControl("Uninstall " + brand, "UNINSTALL", "", [=]() {
if (ConfirmationDialog::confirm("Are you sure you want to uninstall?", this)) {
Params().putBool("DoUninstall", true);
}
}, "", this));
for(auto &btn : offroad_btns){
device_layout->addWidget(horizontal_line());
QObject::connect(parent, SIGNAL(offroadTransition(bool)), btn, SLOT(setEnabled(bool)));
device_layout->addWidget(btn);
}
// power buttons
QHBoxLayout *power_layout = new QHBoxLayout();
power_layout->setSpacing(30);
QPushButton *reboot_btn = new QPushButton("Reboot");
power_layout->addWidget(reboot_btn);
QObject::connect(reboot_btn, &QPushButton::released, [=]() {
if (ConfirmationDialog::confirm("Are you sure you want to reboot?", this)) {
Hardware::reboot();
}
});
QPushButton *poweroff_btn = new QPushButton("Power Off");
poweroff_btn->setStyleSheet("background-color: #E22C2C;");
power_layout->addWidget(poweroff_btn);
QObject::connect(poweroff_btn, &QPushButton::released, [=]() {
if (ConfirmationDialog::confirm("Are you sure you want to power off?", this)) {
Hardware::poweroff();
}
});
device_layout->addLayout(power_layout);
setLayout(device_layout);
setStyleSheet(R"(
QPushButton {
padding: 0;
height: 120px;
border-radius: 15px;
background-color: #393939;
}
)");
}
SoftwarePanel::SoftwarePanel(QWidget* parent) : QFrame(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(this);
setLayout(main_layout);
setStyleSheet(R"(QLabel {font-size: 50px;})");
fs_watch = new QFileSystemWatcher(this);
QObject::connect(fs_watch, &QFileSystemWatcher::fileChanged, [=](const QString path) {
int update_failed_count = Params().get<int>("UpdateFailedCount").value_or(0);
if (path.contains("UpdateFailedCount") && update_failed_count > 0) {
lastUpdateTimeLbl->setText("failed to fetch update");
updateButton->setText("CHECK");
updateButton->setEnabled(true);
} else if (path.contains("LastUpdateTime")) {
updateLabels();
}
});
}
void SoftwarePanel::showEvent(QShowEvent *event) {
updateLabels();
}
void SoftwarePanel::updateLabels() {
Params params = Params();
std::string brand = params.getBool("Passive") ? "dashcam" : "openpilot";
QList<QPair<QString, std::string>> dev_params = {
{"Git Branch", params.get("GitBranch")},
{"Git Commit", params.get("GitCommit").substr(0, 10)},
{"Panda Firmware", params.get("PandaFirmwareHex")},
{"OS Version", Hardware::get_os_version()},
};
QString version = QString::fromStdString(brand + " v" + params.get("Version").substr(0, 14)).trimmed();
QString lastUpdateTime = "";
std::string last_update_param = params.get("LastUpdateTime");
if (!last_update_param.empty()){
QDateTime lastUpdateDate = QDateTime::fromString(QString::fromStdString(last_update_param + "Z"), Qt::ISODate);
lastUpdateTime = timeAgo(lastUpdateDate);
}
if (labels.size() < dev_params.size()) {
versionLbl = new LabelControl("Version", version, QString::fromStdString(params.get("ReleaseNotes")).trimmed());
layout()->addWidget(versionLbl);
layout()->addWidget(horizontal_line());
lastUpdateTimeLbl = new LabelControl("Last Update Check", lastUpdateTime, "The last time openpilot successfully checked for an update. The updater only runs while the car is off.");
layout()->addWidget(lastUpdateTimeLbl);
layout()->addWidget(horizontal_line());
updateButton = new ButtonControl("Check for Update", "CHECK", "", [=]() {
Params params = Params();
if (params.getBool("IsOffroad")) {
fs_watch->addPath(QString::fromStdString(params.getParamsPath()) + "/d/LastUpdateTime");
fs_watch->addPath(QString::fromStdString(params.getParamsPath()) + "/d/UpdateFailedCount");
updateButton->setText("CHECKING");
updateButton->setEnabled(false);
}
std::system("pkill -1 -f selfdrive.updated");
}, "", this);
layout()->addWidget(updateButton);
layout()->addWidget(horizontal_line());
} else {
versionLbl->setText(version);
lastUpdateTimeLbl->setText(lastUpdateTime);
updateButton->setText("CHECK");
updateButton->setEnabled(true);
}
for (int i = 0; i < dev_params.size(); i++) {
const auto &[name, value] = dev_params[i];
QString val = QString::fromStdString(value).trimmed();
if (labels.size() > i) {
labels[i]->setText(val);
} else {
labels.push_back(new LabelControl(name, val));
layout()->addWidget(labels[i]);
if (i < (dev_params.size() - 1)) {
layout()->addWidget(horizontal_line());
}
}
}
}
QWidget * network_panel(QWidget * parent) {
#ifdef QCOM
QVBoxLayout *layout = new QVBoxLayout;
layout->setSpacing(30);
// wifi + tethering buttons
layout->addWidget(new ButtonControl("WiFi Settings", "OPEN", "",
[=]() { HardwareEon::launch_wifi(); }));
layout->addWidget(horizontal_line());
layout->addWidget(new ButtonControl("Tethering Settings", "OPEN", "",
[=]() { HardwareEon::launch_tethering(); }));
layout->addWidget(horizontal_line());
// SSH key management
layout->addWidget(new SshToggle());
layout->addWidget(horizontal_line());
layout->addWidget(new SshControl());
layout->addStretch(1);
QWidget *w = new QWidget;
w->setLayout(layout);
#else
Networking *w = new Networking(parent);
#endif
return w;
}
void SettingsWindow::showEvent(QShowEvent *event) {
if (layout()) {
panel_widget->setCurrentIndex(0);
nav_btns->buttons()[0]->setChecked(true);
return;
}
// setup two main layouts
QVBoxLayout *sidebar_layout = new QVBoxLayout();
sidebar_layout->setMargin(0);
panel_widget = new QStackedWidget();
panel_widget->setStyleSheet(R"(
border-radius: 30px;
background-color: #292929;
)");
// close button
QPushButton *close_btn = new QPushButton("X");
close_btn->setStyleSheet(R"(
font-size: 90px;
font-weight: bold;
border 1px grey solid;
border-radius: 100px;
background-color: #292929;
)");
close_btn->setFixedSize(200, 200);
sidebar_layout->addSpacing(45);
sidebar_layout->addWidget(close_btn, 0, Qt::AlignCenter);
QObject::connect(close_btn, &QPushButton::released, this, &SettingsWindow::closeSettings);
// setup panels
DevicePanel *device = new DevicePanel(this);
QObject::connect(device, &DevicePanel::reviewTrainingGuide, this, &SettingsWindow::reviewTrainingGuide);
QObject::connect(device, &DevicePanel::showDriverView, this, &SettingsWindow::showDriverView);
QPair<QString, QWidget *> panels[] = {
{"Device", device},
{"Network", network_panel(this)},
{"Toggles", new TogglesPanel(this)},
{"Software", new SoftwarePanel()},
};
sidebar_layout->addSpacing(45);
nav_btns = new QButtonGroup();
for (auto &[name, panel] : panels) {
QPushButton *btn = new QPushButton(name);
btn->setCheckable(true);
btn->setChecked(nav_btns->buttons().size() == 0);
btn->setStyleSheet(R"(
QPushButton {
color: grey;
border: none;
background: none;
font-size: 65px;
font-weight: 500;
padding-top: 35px;
padding-bottom: 35px;
}
QPushButton:checked {
color: white;
}
)");
nav_btns->addButton(btn);
sidebar_layout->addWidget(btn, 0, Qt::AlignRight);
panel->setContentsMargins(50, 25, 50, 25);
ScrollView *panel_frame = new ScrollView(panel, this);
panel_widget->addWidget(panel_frame);
QObject::connect(btn, &QPushButton::released, [=, w = panel_frame]() {
panel_widget->setCurrentWidget(w);
});
}
sidebar_layout->setContentsMargins(50, 50, 100, 50);
// main settings layout, sidebar + main panel
QHBoxLayout *settings_layout = new QHBoxLayout();
sidebar_widget = new QWidget;
sidebar_widget->setLayout(sidebar_layout);
sidebar_widget->setFixedWidth(500);
settings_layout->addWidget(sidebar_widget);
settings_layout->addWidget(panel_widget);
setLayout(settings_layout);
setStyleSheet(R"(
* {
color: white;
font-size: 50px;
}
SettingsWindow {
background-color: black;
}
)");
}
void SettingsWindow::hideEvent(QHideEvent *event){
#ifdef QCOM
HardwareEon::close_activities();
#endif
// TODO: this should be handled by the Dialog classes
QList<QWidget*> children = findChildren<QWidget *>();
for(auto &w : children){
if(w->metaObject()->superClass()->className() == QString("QDialog")){
w->close();
}
}
}
-72
View File
@@ -1,72 +0,0 @@
#pragma once
#include <QButtonGroup>
#include <QFileSystemWatcher>
#include <QFrame>
#include <QLabel>
#include <QPushButton>
#include <QScrollArea>
#include <QStackedWidget>
#include <QTimer>
#include <QWidget>
#include "selfdrive/ui/qt/widgets/controls.h"
// ********** settings window + top-level panels **********
class DevicePanel : public QWidget {
Q_OBJECT
public:
explicit DevicePanel(QWidget* parent = nullptr);
signals:
void reviewTrainingGuide();
void showDriverView();
};
class TogglesPanel : public QWidget {
Q_OBJECT
public:
explicit TogglesPanel(QWidget *parent = nullptr);
};
class SoftwarePanel : public QFrame {
Q_OBJECT
public:
explicit SoftwarePanel(QWidget* parent = nullptr);
protected:
void showEvent(QShowEvent *event) override;
private:
QList<LabelControl *> labels;
LabelControl *versionLbl;
LabelControl *lastUpdateTimeLbl;
ButtonControl *updateButton;
void updateLabels();
QFileSystemWatcher *fs_watch;
};
class SettingsWindow : public QFrame {
Q_OBJECT
public:
explicit SettingsWindow(QWidget *parent = 0) : QFrame(parent) {};
protected:
void hideEvent(QHideEvent *event) override;
void showEvent(QShowEvent *event) override;
signals:
void closeSettings();
void offroadTransition(bool offroad);
void reviewTrainingGuide();
void showDriverView();
private:
QPushButton *sidebar_alert_widget;
QWidget *sidebar_widget;
QButtonGroup *nav_btns;
QStackedWidget *panel_widget;
};
-522
View File
@@ -1,522 +0,0 @@
#include "wifiManager.h"
#include <stdlib.h>
#include <algorithm>
#include <set>
#include "selfdrive/common/params.h"
#include "selfdrive/common/swaglog.h"
/**
* We are using a NetworkManager DBUS API : https://developer.gnome.org/NetworkManager/1.26/spec.html
* */
// https://developer.gnome.org/NetworkManager/1.26/nm-dbus-types.html#NM80211ApFlags
const int NM_802_11_AP_FLAGS_PRIVACY = 0x00000001;
// https://developer.gnome.org/NetworkManager/1.26/nm-dbus-types.html#NM80211ApSecurityFlags
const int NM_802_11_AP_SEC_PAIR_WEP40 = 0x00000001;
const int NM_802_11_AP_SEC_PAIR_WEP104 = 0x00000002;
const int NM_802_11_AP_SEC_GROUP_WEP40 = 0x00000010;
const int NM_802_11_AP_SEC_GROUP_WEP104 = 0x00000020;
const int NM_802_11_AP_SEC_KEY_MGMT_PSK = 0x00000100;
const int NM_802_11_AP_SEC_KEY_MGMT_802_1X = 0x00000200;
const QString nm_path = "/org/freedesktop/NetworkManager";
const QString nm_settings_path = "/org/freedesktop/NetworkManager/Settings";
const QString nm_iface = "org.freedesktop.NetworkManager";
const QString props_iface = "org.freedesktop.DBus.Properties";
const QString nm_settings_iface = "org.freedesktop.NetworkManager.Settings";
const QString nm_settings_conn_iface = "org.freedesktop.NetworkManager.Settings.Connection";
const QString device_iface = "org.freedesktop.NetworkManager.Device";
const QString wireless_device_iface = "org.freedesktop.NetworkManager.Device.Wireless";
const QString ap_iface = "org.freedesktop.NetworkManager.AccessPoint";
const QString connection_iface = "org.freedesktop.NetworkManager.Connection.Active";
const QString ipv4config_iface = "org.freedesktop.NetworkManager.IP4Config";
const QString nm_service = "org.freedesktop.NetworkManager";
const int state_connected = 100;
const int state_need_auth = 60;
const int reason_wrong_password = 8;
const int dbus_timeout = 100;
template <typename T>
T get_response(QDBusMessage response) {
QVariant first = response.arguments().at(0);
QDBusVariant dbvFirst = first.value<QDBusVariant>();
QVariant vFirst = dbvFirst.variant();
if (vFirst.canConvert<T>()) {
return vFirst.value<T>();
} else {
LOGE("Variant unpacking failure");
return T();
}
}
bool compare_by_strength(const Network &a, const Network &b) {
if (a.connected == ConnectedType::CONNECTED) return true;
if (b.connected == ConnectedType::CONNECTED) return false;
if (a.connected == ConnectedType::CONNECTING) return true;
if (b.connected == ConnectedType::CONNECTING) return false;
return a.strength > b.strength;
}
WifiManager::WifiManager(QWidget* parent) {
qDBusRegisterMetaType<Connection>();
qDBusRegisterMetaType<IpConfig>();
connecting_to_network = "";
adapter = get_adapter();
bool has_adapter = adapter != "";
if (!has_adapter){
throw std::runtime_error("Error connecting to NetworkManager");
}
QDBusInterface nm(nm_service, adapter, device_iface, bus);
bus.connect(nm_service, adapter, device_iface, "StateChanged", this, SLOT(change(unsigned int, unsigned int, unsigned int)));
QDBusInterface device_props(nm_service, adapter, props_iface, bus);
device_props.setTimeout(dbus_timeout);
QDBusMessage response = device_props.call("Get", device_iface, "State");
raw_adapter_state = get_response<uint>(response);
change(raw_adapter_state, 0, 0);
// Set tethering ssid as "weedle" + first 4 characters of a dongle id
tethering_ssid = "weedle";
std::string bytes = Params().get("DongleId");
if (bytes.length() >= 4) {
tethering_ssid+="-"+QString::fromStdString(bytes.substr(0,4));
}
// Create dbus interface for tethering button. This populates the introspection cache,
// making sure all future creations are non-blocking
// https://bugreports.qt.io/browse/QTBUG-14485
QDBusInterface(nm_service, nm_settings_path, nm_settings_iface, bus);
}
void WifiManager::refreshNetworks() {
seen_networks.clear();
seen_ssids.clear();
ipv4_address = get_ipv4_address();
for (Network &network : get_networks()) {
if (seen_ssids.count(network.ssid)) {
continue;
}
seen_ssids.push_back(network.ssid);
seen_networks.push_back(network);
}
}
QString WifiManager::get_ipv4_address(){
if (raw_adapter_state != state_connected){
return "";
}
QVector<QDBusObjectPath> conns = get_active_connections();
for (auto &p : conns){
QString active_connection = p.path();
QDBusInterface nm(nm_service, active_connection, props_iface, bus);
nm.setTimeout(dbus_timeout);
QDBusObjectPath pth = get_response<QDBusObjectPath>(nm.call("Get", connection_iface, "Ip4Config"));
QString ip4config = pth.path();
QString type = get_response<QString>(nm.call("Get", connection_iface, "Type"));
if (type == "802-11-wireless") {
QDBusInterface nm2(nm_service, ip4config, props_iface, bus);
nm2.setTimeout(dbus_timeout);
const QDBusArgument &arr = get_response<QDBusArgument>(nm2.call("Get", ipv4config_iface, "AddressData"));
QMap<QString, QVariant> pth2;
arr.beginArray();
while (!arr.atEnd()){
arr >> pth2;
QString ipv4 = pth2.value("address").value<QString>();
arr.endArray();
return ipv4;
}
arr.endArray();
}
}
return "";
}
QList<Network> WifiManager::get_networks() {
QList<Network> r;
QDBusInterface nm(nm_service, adapter, wireless_device_iface, bus);
nm.setTimeout(dbus_timeout);
QDBusMessage response = nm.call("GetAllAccessPoints");
QVariant first = response.arguments().at(0);
QString active_ap = get_active_ap();
const QDBusArgument &args = first.value<QDBusArgument>();
args.beginArray();
while (!args.atEnd()) {
QDBusObjectPath path;
args >> path;
QByteArray ssid = get_property(path.path(), "Ssid");
unsigned int strength = get_ap_strength(path.path());
SecurityType security = getSecurityType(path.path());
ConnectedType ctype;
if (path.path() != active_ap) {
ctype = ConnectedType::DISCONNECTED;
} else {
if (ssid == connecting_to_network) {
ctype = ConnectedType::CONNECTING;
} else {
ctype = ConnectedType::CONNECTED;
}
}
Network network = {path.path(), ssid, strength, ctype, security};
if (ssid.length()) {
r.push_back(network);
}
}
args.endArray();
std::sort(r.begin(), r.end(), compare_by_strength);
return r;
}
SecurityType WifiManager::getSecurityType(QString path) {
int sflag = get_property(path, "Flags").toInt();
int wpaflag = get_property(path, "WpaFlags").toInt();
int rsnflag = get_property(path, "RsnFlags").toInt();
int wpa_props = wpaflag | rsnflag;
// obtained by looking at flags of networks in the office as reported by an Android phone
const int supports_wpa = NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104 | NM_802_11_AP_SEC_GROUP_WEP40 | NM_802_11_AP_SEC_GROUP_WEP104 | NM_802_11_AP_SEC_KEY_MGMT_PSK;
if (sflag == 0) {
return SecurityType::OPEN;
} else if ((sflag & NM_802_11_AP_FLAGS_PRIVACY) && (wpa_props & supports_wpa) && !(wpa_props & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) {
return SecurityType::WPA;
} else {
return SecurityType::UNSUPPORTED;
}
}
void WifiManager::connect(Network n) {
return connect(n, "", "");
}
void WifiManager::connect(Network n, QString password) {
return connect(n, "", password);
}
void WifiManager::connect(Network n, QString username, QString password) {
connecting_to_network = n.ssid;
// disconnect();
clear_connections(n.ssid); //Clear all connections that may already exist to the network we are connecting
connect(n.ssid, username, password, n.security_type);
}
void WifiManager::connect(QByteArray ssid, QString username, QString password, SecurityType security_type) {
Connection connection;
connection["connection"]["type"] = "802-11-wireless";
connection["connection"]["uuid"] = QUuid::createUuid().toString().remove('{').remove('}');
connection["connection"]["id"] = "openpilot connection "+QString::fromStdString(ssid.toStdString());
connection["connection"]["autoconnect-retries"] = 0;
connection["802-11-wireless"]["ssid"] = ssid;
connection["802-11-wireless"]["mode"] = "infrastructure";
if (security_type == SecurityType::WPA) {
connection["802-11-wireless-security"]["key-mgmt"] = "wpa-psk";
connection["802-11-wireless-security"]["auth-alg"] = "open";
connection["802-11-wireless-security"]["psk"] = password;
}
connection["ipv4"]["method"] = "auto";
connection["ipv6"]["method"] = "ignore";
QDBusInterface nm_settings(nm_service, nm_settings_path, nm_settings_iface, bus);
nm_settings.setTimeout(dbus_timeout);
nm_settings.call("AddConnection", QVariant::fromValue(connection));
activate_wifi_connection(QString(ssid));
}
void WifiManager::deactivate_connections(QString ssid) {
for (QDBusObjectPath active_connection_raw : get_active_connections()) {
QString active_connection = active_connection_raw.path();
QDBusInterface nm(nm_service, active_connection, props_iface, bus);
nm.setTimeout(dbus_timeout);
QDBusObjectPath pth = get_response<QDBusObjectPath>(nm.call("Get", connection_iface, "SpecificObject"));
if (pth.path() != "" && pth.path() != "/") {
QString Ssid = get_property(pth.path(), "Ssid");
if (Ssid == ssid) {
QDBusInterface nm2(nm_service, nm_path, nm_iface, bus);
nm2.setTimeout(dbus_timeout);
nm2.call("DeactivateConnection", QVariant::fromValue(active_connection_raw));// TODO change to disconnect
}
}
}
}
QVector<QDBusObjectPath> WifiManager::get_active_connections() {
QDBusInterface nm(nm_service, nm_path, props_iface, bus);
nm.setTimeout(dbus_timeout);
QDBusMessage response = nm.call("Get", nm_iface, "ActiveConnections");
const QDBusArgument &arr = get_response<QDBusArgument>(response);
QVector<QDBusObjectPath> conns;
QDBusObjectPath path;
arr.beginArray();
while (!arr.atEnd()) {
arr >> path;
conns.push_back(path);
}
arr.endArray();
return conns;
}
void WifiManager::clear_connections(QString ssid) {
for(QDBusObjectPath path : list_connections()){
QDBusInterface nm2(nm_service, path.path(), nm_settings_conn_iface, bus);
nm2.setTimeout(dbus_timeout);
QDBusMessage response = nm2.call("GetSettings");
const QDBusArgument &dbusArg = response.arguments().at(0).value<QDBusArgument>();
QMap<QString, QMap<QString,QVariant>> map;
dbusArg >> map;
for (auto &inner : map) {
for (auto &val : inner) {
QString key = inner.key(val);
if (key == "ssid") {
if (val == ssid) {
nm2.call("Delete");
}
}
}
}
}
}
void WifiManager::request_scan() {
QDBusInterface nm(nm_service, adapter, wireless_device_iface, bus);
nm.setTimeout(dbus_timeout);
nm.call("RequestScan", QVariantMap());
}
uint WifiManager::get_wifi_device_state() {
QDBusInterface device_props(nm_service, adapter, props_iface, bus);
device_props.setTimeout(dbus_timeout);
QDBusMessage response = device_props.call("Get", device_iface, "State");
uint resp = get_response<uint>(response);
return resp;
}
QString WifiManager::get_active_ap() {
QDBusInterface device_props(nm_service, adapter, props_iface, bus);
device_props.setTimeout(dbus_timeout);
QDBusMessage response = device_props.call("Get", wireless_device_iface, "ActiveAccessPoint");
QDBusObjectPath r = get_response<QDBusObjectPath>(response);
return r.path();
}
QByteArray WifiManager::get_property(QString network_path ,QString property) {
QDBusInterface device_props(nm_service, network_path, props_iface, bus);
device_props.setTimeout(dbus_timeout);
QDBusMessage response = device_props.call("Get", ap_iface, property);
return get_response<QByteArray>(response);
}
unsigned int WifiManager::get_ap_strength(QString network_path) {
QDBusInterface device_props(nm_service, network_path, props_iface, bus);
device_props.setTimeout(dbus_timeout);
QDBusMessage response = device_props.call("Get", ap_iface, "Strength");
return get_response<unsigned int>(response);
}
QString WifiManager::get_adapter() {
QDBusInterface nm(nm_service, nm_path, nm_iface, bus);
nm.setTimeout(dbus_timeout);
QDBusMessage response = nm.call("GetDevices");
QVariant first = response.arguments().at(0);
QString adapter_path = "";
const QDBusArgument &args = first.value<QDBusArgument>();
args.beginArray();
while (!args.atEnd()) {
QDBusObjectPath path;
args >> path;
// Get device type
QDBusInterface device_props(nm_service, path.path(), props_iface, bus);
device_props.setTimeout(dbus_timeout);
QDBusMessage response = device_props.call("Get", device_iface, "DeviceType");
uint device_type = get_response<uint>(response);
if (device_type == 2) { // Wireless
adapter_path = path.path();
break;
}
}
args.endArray();
return adapter_path;
}
void WifiManager::change(unsigned int new_state, unsigned int previous_state, unsigned int change_reason) {
raw_adapter_state = new_state;
if (new_state == state_need_auth && change_reason == reason_wrong_password) {
emit wrongPassword(connecting_to_network);
} else if (new_state == state_connected) {
emit successfulConnection(connecting_to_network);
connecting_to_network = "";
}
}
void WifiManager::disconnect() {
QString active_ap = get_active_ap();
if (active_ap != "" && active_ap != "/") {
deactivate_connections(get_property(active_ap, "Ssid"));
}
}
QVector<QDBusObjectPath> WifiManager::list_connections(){
QVector<QDBusObjectPath> connections;
QDBusInterface nm(nm_service, nm_settings_path, nm_settings_iface, bus);
nm.setTimeout(dbus_timeout);
QDBusMessage response = nm.call("ListConnections");
QVariant first = response.arguments().at(0);
const QDBusArgument &args = first.value<QDBusArgument>();
args.beginArray();
while (!args.atEnd()) {
QDBusObjectPath path;
args >> path;
connections.push_back(path);
}
return connections;
}
bool WifiManager::activate_wifi_connection(QString ssid){
QString devicePath = get_adapter();
for(QDBusObjectPath path : list_connections()){
QDBusInterface nm2(nm_service, path.path(), nm_settings_conn_iface, bus);
nm2.setTimeout(dbus_timeout);
QDBusMessage response = nm2.call("GetSettings");
const QDBusArgument &dbusArg = response.arguments().at(0).value<QDBusArgument>();
QMap<QString, QMap<QString,QVariant>> map;
dbusArg >> map;
for (auto &inner : map) {
for (auto &val : inner) {
QString key = inner.key(val);
if (key == "ssid") {
if (val == ssid) {
QDBusInterface nm3(nm_service, nm_path, nm_iface, bus);
nm3.setTimeout(dbus_timeout);
nm3.call("ActivateConnection", QVariant::fromValue(path), QVariant::fromValue(QDBusObjectPath(devicePath)), QVariant::fromValue(QDBusObjectPath("/")));
return true;
}
}
}
}
}
return false;
}
//Functions for tethering
bool WifiManager::activate_tethering_connection(){
QString devicePath = get_adapter();
for(QDBusObjectPath path : list_connections()){
QDBusInterface nm2(nm_service, path.path(), nm_settings_conn_iface, bus);
nm2.setTimeout(dbus_timeout);
QDBusMessage response = nm2.call("GetSettings");
const QDBusArgument &dbusArg = response.arguments().at(0).value<QDBusArgument>();
QMap<QString, QMap<QString,QVariant>> map;
dbusArg >> map;
for (auto &inner : map) {
for (auto &val : inner) {
QString key = inner.key(val);
if (key == "ssid") {
if (val == tethering_ssid.toUtf8()) {
QDBusInterface nm3(nm_service, nm_path, nm_iface, bus);
nm3.setTimeout(dbus_timeout);
nm3.call("ActivateConnection", QVariant::fromValue(path), QVariant::fromValue(QDBusObjectPath(devicePath)), QVariant::fromValue(QDBusObjectPath("/")));
return true;
}
}
}
}
}
return false;
}
void WifiManager::addTetheringConnection(){
Connection connection;
connection["connection"]["id"] = "Hotspot";
connection["connection"]["uuid"] = QUuid::createUuid().toString().remove('{').remove('}');
connection["connection"]["type"] = "802-11-wireless";
connection["connection"]["interface-name"] = "wlan0";
connection["connection"]["autoconnect"] = false;
connection["802-11-wireless"]["band"] = "bg";
connection["802-11-wireless"]["mode"] = "ap";
connection["802-11-wireless"]["ssid"] = tethering_ssid.toUtf8();
connection["802-11-wireless-security"]["group"] = QStringList("ccmp");
connection["802-11-wireless-security"]["key-mgmt"] = "wpa-psk";
connection["802-11-wireless-security"]["pairwise"] = QStringList("ccmp");
connection["802-11-wireless-security"]["proto"] = QStringList("rsn");
connection["802-11-wireless-security"]["psk"] = tetheringPassword;
connection["ipv4"]["method"] = "shared";
QMap<QString,QVariant> address;
address["address"] = "192.168.43.1";
address["prefix"] = 24u;
connection["ipv4"]["address-data"] = QVariant::fromValue(IpConfig() << address);
connection["ipv4"]["gateway"] = "192.168.43.1";
connection["ipv6"]["method"] = "ignore";
QDBusInterface nm_settings(nm_service, nm_settings_path, nm_settings_iface, bus);
nm_settings.setTimeout(dbus_timeout);
nm_settings.call("AddConnection", QVariant::fromValue(connection));
}
void WifiManager::enableTethering() {
if(activate_tethering_connection()){
return;
}
addTetheringConnection();
activate_tethering_connection();
}
void WifiManager::disableTethering() {
deactivate_connections(tethering_ssid.toUtf8());
}
bool WifiManager::tetheringEnabled() {
QString active_ap = get_active_ap();
return get_property(active_ap, "Ssid") == tethering_ssid;
}
void WifiManager::changeTetheringPassword(QString newPassword){
tetheringPassword = newPassword;
clear_connections(tethering_ssid.toUtf8());
addTetheringConnection();
}
-82
View File
@@ -1,82 +0,0 @@
#pragma once
#include <QWidget>
#include <QtDBus>
enum class SecurityType {
OPEN,
WPA,
UNSUPPORTED
};
enum class ConnectedType{
DISCONNECTED,
CONNECTING,
CONNECTED
};
typedef QMap<QString, QMap<QString, QVariant>> Connection;
typedef QVector<QMap<QString, QVariant>> IpConfig;
struct Network {
QString path;
QByteArray ssid;
unsigned int strength;
ConnectedType connected;
SecurityType security_type;
};
class WifiManager : public QWidget {
Q_OBJECT
public:
explicit WifiManager(QWidget* parent);
void request_scan();
QVector<Network> seen_networks;
QString ipv4_address;
void refreshNetworks();
void connect(Network ssid);
void connect(Network ssid, QString password);
void connect(Network ssid, QString username, QString password);
void disconnect();
// Tethering functions
void enableTethering();
void disableTethering();
bool tetheringEnabled();
bool activate_tethering_connection();
void addTetheringConnection();
bool activate_wifi_connection(QString ssid);
void changeTetheringPassword(QString newPassword);
private:
QVector<QByteArray> seen_ssids;
QString adapter;//Path to network manager wifi-device
QDBusConnection bus = QDBusConnection::systemBus();
unsigned int raw_adapter_state;//Connection status https://developer.gnome.org/NetworkManager/1.26/nm-dbus-types.html#NMDeviceState
QString connecting_to_network;
QString tethering_ssid;
QString tetheringPassword = "swagswagcommma";
QString get_adapter();
QString get_ipv4_address();
QList<Network> get_networks();
void connect(QByteArray ssid, QString username, QString password, SecurityType security_type);
QString get_active_ap();
void deactivate_connections(QString ssid);
void clear_connections(QString ssid);
QVector<QDBusObjectPath> get_active_connections();
uint get_wifi_device_state();
QByteArray get_property(QString network_path, QString property);
unsigned int get_ap_strength(QString network_path);
SecurityType getSecurityType(QString ssid);
QVector<QDBusObjectPath> list_connections();
private slots:
void change(unsigned int new_state, unsigned int previous_state, unsigned int change_reason);
signals:
void wrongPassword(QString ssid);
void successfulConnection(QString ssid);
void refresh();
};
-246
View File
@@ -1,246 +0,0 @@
#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/util.h"
#ifdef ENABLE_MAPS
#include "selfdrive/ui/qt/maps/map.h"
#endif
OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
layout = new QStackedLayout(this);
layout->setStackingMode(QStackedLayout::StackAll);
// old UI on bottom
nvg = new NvgWindow(this);
QObject::connect(this, &OnroadWindow::update, nvg, &NvgWindow::update);
split = new QHBoxLayout();
split->setContentsMargins(0, 0, 0, 0);
split->setSpacing(0);
split->addWidget(nvg);
QWidget * split_wrapper = new QWidget;
split_wrapper->setLayout(split);
layout->addWidget(split_wrapper);
alerts = new OnroadAlerts(this);
alerts->setAttribute(Qt::WA_TransparentForMouseEvents, true);
QObject::connect(this, &OnroadWindow::update, alerts, &OnroadAlerts::updateState);
QObject::connect(this, &OnroadWindow::offroadTransitionSignal, alerts, &OnroadAlerts::offroadTransition);
QObject::connect(this, &OnroadWindow::offroadTransitionSignal, this, &OnroadWindow::offroadTransition);
layout->addWidget(alerts);
// setup stacking order
alerts->raise();
setLayout(layout);
setAttribute(Qt::WA_OpaquePaintEvent);
}
void OnroadWindow::offroadTransition(bool offroad) {
#ifdef ENABLE_MAPS
if (!offroad) {
QString token = QString::fromStdString(Params().get("MapboxToken"));
if (map == nullptr && !token.isEmpty()){
QMapboxGLSettings settings;
settings.setCacheDatabasePath("/data/mbgl-cache.db");
settings.setCacheDatabaseMaximumSize(20 * 1024 * 1024);
settings.setAccessToken(token.trimmed());
MapWindow * m = new MapWindow(settings);
QObject::connect(this, &OnroadWindow::offroadTransitionSignal, m, &MapWindow::offroadTransition);
split->addWidget(m);
map = m;
}
}
#endif
}
// ***** onroad widgets *****
OnroadAlerts::OnroadAlerts(QWidget *parent) : QWidget(parent) {
for (auto &kv : sound_map) {
auto path = QUrl::fromLocalFile(kv.second.first);
sounds[kv.first].setSource(path);
}
}
void OnroadAlerts::updateState(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["deviceState"].getDeviceState().getStarted()) {
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 if ((sm.frame - s.scene.started_frame) > 10 * UI_FREQ) {
// Handle controls timeout
if (sm.rcv_frame("controlsState") < 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 - sm.rcv_frame("controlsState")) > 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;
}
}
}
// TODO: add blinking back if performant
//float alpha = 0.375 * cos((millis_since_boot() / 1000) * 2 * M_PI * blinking_rate) + 0.625;
bg = bg_colors[s.status];
}
void OnroadAlerts::offroadTransition(bool offroad) {
updateAlert("", "", 0, "", cereal::ControlsState::AlertSize::NONE, AudibleAlert::NONE);
}
void OnroadAlerts::updateAlert(const QString &t1, const QString &t2, float blink_rate,
const std::string &type, cereal::ControlsState::AlertSize size, AudibleAlert sound) {
if (alert_type.compare(type) == 0 && text1.compare(t1) == 0) {
return;
}
stopSounds();
if (sound != AudibleAlert::NONE) {
playSound(sound);
}
text1 = t1;
text2 = t2;
alert_type = type;
alert_size = size;
blinking_rate = blink_rate;
update();
}
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);
if (alert_size == cereal::ControlsState::AlertSize::NONE) {
return;
}
static std::map<cereal::ControlsState::AlertSize, const int> alert_sizes = {
{cereal::ControlsState::AlertSize::SMALL, 271},
{cereal::ControlsState::AlertSize::MID, 420},
{cereal::ControlsState::AlertSize::FULL, height()},
};
int h = alert_sizes[alert_size];
QRect r = QRect(0, height() - h, width(), h);
// draw background + gradient
p.setPen(Qt::NoPen);
p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
p.setBrush(QBrush(bg));
p.drawRect(r);
QLinearGradient g(0, r.y(), 0, r.bottom());
g.setColorAt(0, QColor::fromRgbF(0, 0, 0, 0.05));
g.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0.35));
p.setBrush(QBrush(g));
p.fillRect(r, g);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
// remove bottom border
r = QRect(0, height() - h, width(), h - 30);
// text
const QPoint c = r.center();
p.setPen(QColor(0xff, 0xff, 0xff));
p.setRenderHint(QPainter::TextAntialiasing);
if (alert_size == cereal::ControlsState::AlertSize::SMALL) {
configFont(p, "Open Sans", 74, "SemiBold");
p.drawText(r, Qt::AlignCenter, text1);
} else if (alert_size == cereal::ControlsState::AlertSize::MID) {
configFont(p, "Open Sans", 88, "Bold");
p.drawText(QRect(0, c.y() - 125, width(), 150), Qt::AlignHCenter | Qt::AlignTop, text1);
configFont(p, "Open Sans", 66, "Regular");
p.drawText(QRect(0, c.y() + 21, width(), 90), Qt::AlignHCenter, text2);
} else if (alert_size == cereal::ControlsState::AlertSize::FULL) {
bool l = text1.length() > 15;
configFont(p, "Open Sans", l ? 132 : 177, "Bold");
p.drawText(QRect(0, r.y() + (l ? 240 : 270), width(), 600), Qt::AlignHCenter | Qt::TextWordWrap, text1);
configFont(p, "Open Sans", 88, "Regular");
p.drawText(QRect(0, r.height() - (l ? 361 : 420), width(), 300), Qt::AlignHCenter | Qt::TextWordWrap, text2);
}
}
NvgWindow::NvgWindow(QWidget *parent) : QOpenGLWidget(parent) {
setAttribute(Qt::WA_OpaquePaintEvent);
}
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::resizeGL(int w, int h) {
ui_resize(&QUIState::ui_state, w, h);
}
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) {
// warn on sub 15fps
LOGW("slow frame time: %.2f", dt);
}
prev_draw_t = cur_draw_t;
}
-98
View File
@@ -1,98 +0,0 @@
#pragma once
#include <map>
#include <QSoundEffect>
#include <QtWidgets>
#include "cereal/gen/cpp/log.capnp.h"
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/qt_window.h"
typedef cereal::CarControl::HUDControl::AudibleAlert AudibleAlert;
// ***** onroad widgets *****
class OnroadAlerts : public QWidget {
Q_OBJECT
public:
OnroadAlerts(QWidget *parent = 0);
protected:
void paintEvent(QPaintEvent*) override;
private:
void stopSounds();
void playSound(AudibleAlert alert);
void updateAlert(const QString &t1, const QString &t2, float blink_rate,
const std::string &type, cereal::ControlsState::AlertSize size, AudibleAlert sound);
std::map<AudibleAlert, std::pair<QString, bool>> sound_map {
// AudibleAlert, (file path, inf loop)
{AudibleAlert::CHIME_DISENGAGE, {"../assets/sounds/disengaged.wav", false}},
{AudibleAlert::CHIME_ENGAGE, {"../assets/sounds/engaged.wav", false}},
{AudibleAlert::CHIME_WARNING1, {"../assets/sounds/warning_1.wav", false}},
{AudibleAlert::CHIME_WARNING2, {"../assets/sounds/warning_2.wav", false}},
{AudibleAlert::CHIME_WARNING2_REPEAT, {"../assets/sounds/warning_2.wav", true}},
{AudibleAlert::CHIME_WARNING_REPEAT, {"../assets/sounds/warning_repeat.wav", true}},
{AudibleAlert::CHIME_ERROR, {"../assets/sounds/error.wav", false}},
{AudibleAlert::CHIME_PROMPT, {"../assets/sounds/error.wav", false}}
};
QColor bg;
float volume = Hardware::MIN_VOLUME;
std::map<AudibleAlert, QSoundEffect> sounds;
float blinking_rate = 0;
QString text1, text2;
std::string alert_type;
cereal::ControlsState::AlertSize alert_size;
public slots:
void updateState(const UIState &s);
void offroadTransition(bool offroad);
};
// container window for the NVG UI
class NvgWindow : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT
public:
using QOpenGLWidget::QOpenGLWidget;
explicit NvgWindow(QWidget* parent = 0);
~NvgWindow();
protected:
void paintGL() override;
void initializeGL() override;
void resizeGL(int w, int h) override;
private:
double prev_draw_t = 0;
public slots:
void update(const UIState &s);
};
// container for all onroad widgets
class OnroadWindow : public QWidget {
Q_OBJECT
public:
OnroadWindow(QWidget* parent = 0);
QWidget *map = nullptr;
private:
OnroadAlerts *alerts;
NvgWindow *nvg;
QStackedLayout *layout;
QHBoxLayout* split;
signals:
void update(const UIState &s);
void offroadTransitionSignal(bool offroad);
private slots:
void offroadTransition(bool offroad);
};
-31
View File
@@ -1,31 +0,0 @@
#pragma once
#include <string>
#include <QApplication>
#include <QWidget>
#ifdef QCOM2
#include <qpa/qplatformnativeinterface.h>
#include <wayland-client-protocol.h>
#include <QPlatformSurfaceEvent>
#endif
#include "selfdrive/hardware/hw.h"
const int vwp_w = Hardware::TICI() ? 2160 : 1920;
const int vwp_h = 1080;
inline void setMainWindow(QWidget *w) {
const float scale = getenv("SCALE") != NULL ? std::stof(getenv("SCALE")) : 1.0;
w->setFixedSize(vwp_w*scale, vwp_h*scale);
w->show();
#ifdef QCOM2
QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();
wl_surface *s = reinterpret_cast<wl_surface*>(native->nativeResourceForWindow("surface", w->windowHandle()));
wl_surface_set_buffer_transform(s, WL_OUTPUT_TRANSFORM_270);
wl_surface_commit(s);
w->showFullScreen();
#endif
}
-13
View File
@@ -1,13 +0,0 @@
#include "request_repeater.h"
RequestRepeater::RequestRepeater(QObject *parent, const QString &requestURL, const QString &cacheKey,
int period) : HttpRequest(parent, requestURL, cacheKey) {
timer = new QTimer(this);
timer->setTimerType(Qt::VeryCoarseTimer);
QObject::connect(timer, &QTimer::timeout, [=](){
if (!QUIState::ui_state.scene.started && QUIState::ui_state.awake && reply == NULL) {
sendRequest(requestURL);
}
});
timer->start(period * 1000);
}
-12
View File
@@ -1,12 +0,0 @@
#pragma once
#include "selfdrive/ui/qt/api.h"
#include "selfdrive/ui/ui.h"
class RequestRepeater : public HttpRequest {
public:
RequestRepeater(QObject *parent, const QString &requestURL, const QString &cacheKey = "", int period = 0);
private:
QTimer *timer;
};
-115
View File
@@ -1,115 +0,0 @@
#include "selfdrive/ui/qt/sidebar.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/common/util.h"
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/qt/util.h"
void Sidebar::drawMetric(QPainter &p, const QString &label, const QString &val, QColor c, int y) {
const QRect rect = {30, y, 240, val.isEmpty() ? (label.contains("\n") ? 124 : 100) : 148};
p.setPen(Qt::NoPen);
p.setBrush(QBrush(c));
p.setClipRect(rect.x() + 6, rect.y(), 18, rect.height(), Qt::ClipOperation::ReplaceClip);
p.drawRoundedRect(QRect(rect.x() + 6, rect.y() + 6, 100, rect.height() - 12), 10, 10);
p.setClipping(false);
QPen pen = QPen(QColor(0xff, 0xff, 0xff, 0x55));
pen.setWidth(2);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
p.drawRoundedRect(rect, 20, 20);
p.setPen(QColor(0xff, 0xff, 0xff));
if (val.isEmpty()) {
configFont(p, "Open Sans", 35, "Bold");
const QRect r = QRect(rect.x() + 35, rect.y(), rect.width() - 50, rect.height());
p.drawText(r, Qt::AlignCenter, label);
} else {
configFont(p, "Open Sans", 58, "Bold");
p.drawText(rect.x() + 50, rect.y() + 71, val);
configFont(p, "Open Sans", 35, "Regular");
p.drawText(rect.x() + 50, rect.y() + 50 + 77, label);
}
}
Sidebar::Sidebar(QWidget *parent) : QFrame(parent) {
home_img = QImage("../assets/images/button_home.png").scaled(180, 180, Qt::KeepAspectRatio, Qt::SmoothTransformation);
settings_img = QImage("../assets/images/button_settings.png").scaled(settings_btn.width(), settings_btn.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);;
connect(this, &Sidebar::valueChanged, [=] { update(); });
setFixedWidth(300);
setMinimumHeight(vwp_h);
setStyleSheet("background-color: rgb(57, 57, 57);");
}
void Sidebar::mousePressEvent(QMouseEvent *event) {
if (settings_btn.contains(event->pos())) {
emit openSettings();
}
}
void Sidebar::updateState(const UIState &s) {
auto &sm = *(s.sm);
auto deviceState = sm["deviceState"].getDeviceState();
setProperty("netType", network_type[deviceState.getNetworkType()]);
setProperty("netStrength", signal_imgs[deviceState.getNetworkStrength()]);
auto last_ping = deviceState.getLastAthenaPingTime();
if (last_ping == 0) {
setProperty("connectStr", "OFFLINE");
setProperty("connectStatus", warning_color);
} else {
bool online = nanos_since_boot() - last_ping < 80e9;
setProperty("connectStr", online ? "ONLINE" : "ERROR");
setProperty("connectStatus", online ? good_color : danger_color);
}
QColor tempStatus = danger_color;
auto ts = deviceState.getThermalStatus();
if (ts == cereal::DeviceState::ThermalStatus::GREEN) {
tempStatus = good_color;
} else if (ts == cereal::DeviceState::ThermalStatus::YELLOW) {
tempStatus = warning_color;
}
setProperty("tempStatus", tempStatus);
setProperty("tempVal", (int)deviceState.getAmbientTempC());
QString pandaStr = "VEHICLE\nONLINE";
QColor pandaStatus = good_color;
if (s.scene.pandaType == cereal::PandaState::PandaType::UNKNOWN) {
pandaStatus = danger_color;
pandaStr = "NO\nPANDA";
} else if (Hardware::TICI() && s.scene.started) {
pandaStr = QString("SATS %1\nACC %2").arg(s.scene.satelliteCount).arg(fmin(10, s.scene.gpsAccuracy), 0, 'f', 2);
pandaStatus = sm["liveLocationKalman"].getLiveLocationKalman().getGpsOK() ? good_color : warning_color;
}
setProperty("pandaStr", pandaStr);
setProperty("pandaStatus", pandaStatus);
}
void Sidebar::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::Antialiasing);
// static imgs
p.setOpacity(0.65);
p.drawImage(settings_btn.x(), settings_btn.y(), settings_img);
p.setOpacity(1.0);
p.drawImage(60, 1080 - 180 - 40, home_img);
// network
p.drawImage(58, 196, net_strength);
configFont(p, "Open Sans", 35, "Regular");
p.setPen(QColor(0xff, 0xff, 0xff));
const QRect r = QRect(50, 247, 100, 50);
p.drawText(r, Qt::AlignCenter, net_type);
// metrics
drawMetric(p, "TEMP", QString("%1°C").arg(temp_val), temp_status, 338);
drawMetric(p, panda_str, "", panda_status, 518);
drawMetric(p, "CONNECT\n" + connect_str, "", connect_status, 676);
}
-65
View File
@@ -1,65 +0,0 @@
#pragma once
#include <QtWidgets>
#include "selfdrive/ui/ui.h"
class Sidebar : public QFrame {
Q_OBJECT
Q_PROPERTY(QString connectStr MEMBER connect_str NOTIFY valueChanged);
Q_PROPERTY(QColor connectStatus MEMBER connect_status NOTIFY valueChanged);
Q_PROPERTY(QString pandaStr MEMBER panda_str NOTIFY valueChanged);
Q_PROPERTY(QColor pandaStatus MEMBER panda_status NOTIFY valueChanged);
Q_PROPERTY(int tempVal MEMBER temp_val NOTIFY valueChanged);
Q_PROPERTY(QColor tempStatus MEMBER temp_status NOTIFY valueChanged);
Q_PROPERTY(QString netType MEMBER net_type NOTIFY valueChanged);
Q_PROPERTY(QImage netStrength MEMBER net_strength NOTIFY valueChanged);
public:
explicit Sidebar(QWidget* parent = 0);
signals:
void openSettings();
void valueChanged();
public slots:
void updateState(const UIState &s);
protected:
void paintEvent(QPaintEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
private:
void drawMetric(QPainter &p, const QString &label, const QString &val, QColor c, int y);
QImage home_img, settings_img;
const QMap<cereal::DeviceState::NetworkType, QString> network_type = {
{cereal::DeviceState::NetworkType::NONE, "--"},
{cereal::DeviceState::NetworkType::WIFI, "WiFi"},
{cereal::DeviceState::NetworkType::CELL2_G, "2G"},
{cereal::DeviceState::NetworkType::CELL3_G, "3G"},
{cereal::DeviceState::NetworkType::CELL4_G, "LTE"},
{cereal::DeviceState::NetworkType::CELL5_G, "5G"}
};
const QMap<cereal::DeviceState::NetworkStrength, QImage> signal_imgs = {
{cereal::DeviceState::NetworkStrength::UNKNOWN, QImage("../assets/images/network_0.png")},
{cereal::DeviceState::NetworkStrength::POOR, QImage("../assets/images/network_1.png")},
{cereal::DeviceState::NetworkStrength::MODERATE, QImage("../assets/images/network_2.png")},
{cereal::DeviceState::NetworkStrength::GOOD, QImage("../assets/images/network_3.png")},
{cereal::DeviceState::NetworkStrength::GREAT, QImage("../assets/images/network_4.png")},
};
const QRect settings_btn = QRect(50, 35, 200, 117);
const QColor good_color = QColor(255, 255, 255);
const QColor warning_color = QColor(218, 202, 37);
const QColor danger_color = QColor(201, 34, 49);
QString connect_str = "OFFLINE";
QColor connect_status = warning_color;
QString panda_str = "NO\nPANDA";
QColor panda_status = warning_color;
int temp_val = 0;
QColor temp_status = warning_color;
QString net_type;
QImage net_strength;
};
BIN
View File
Binary file not shown.
-134
View File
@@ -1,134 +0,0 @@
#include "spinner.h"
#include <stdio.h>
#include <iostream>
#include <string>
#include <QApplication>
#include <QGridLayout>
#include <QPainter>
#include <QString>
#include <QTransform>
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/qt/qt_window.h"
TrackWidget::TrackWidget(QWidget *parent) : QWidget(parent) {
setFixedSize(spinner_size);
setAutoFillBackground(false);
comma_img = QPixmap("../assets/img_spinner_comma.png").scaled(spinner_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
// pre-compute all the track imgs. make this a gif instead?
QTransform transform;
QPixmap track_img = QPixmap("../assets/img_spinner_track.png").scaled(spinner_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
for (auto &img : track_imgs) {
img = track_img.transformed(transform.rotate(360/spinner_fps), Qt::SmoothTransformation);
}
m_anim.setDuration(1000);
m_anim.setStartValue(0);
m_anim.setEndValue(int(track_imgs.size() -1));
m_anim.setLoopCount(-1);
m_anim.start();
connect(&m_anim, SIGNAL(valueChanged(QVariant)), SLOT(update()));
}
void TrackWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
QRect bg(0, 0, painter.device()->width(), painter.device()->height());
QBrush bgBrush("#000000");
painter.fillRect(bg, bgBrush);
int track_idx = m_anim.currentValue().toInt();
QRect rect(track_imgs[track_idx].rect());
rect.moveCenter(bg.center());
painter.drawPixmap(rect.topLeft(), track_imgs[track_idx]);
rect = comma_img.rect();
rect.moveCenter(bg.center());
painter.drawPixmap(rect.topLeft(), comma_img);
}
// Spinner
Spinner::Spinner(QWidget *parent) {
QGridLayout *main_layout = new QGridLayout();
main_layout->setSpacing(0);
main_layout->setMargin(200);
main_layout->addWidget(new TrackWidget(this), 0, 0, Qt::AlignHCenter | Qt::AlignVCenter);
text = new QLabel();
text->setVisible(false);
main_layout->addWidget(text, 1, 0, Qt::AlignHCenter);
progress_bar = new QProgressBar();
progress_bar->setRange(5, 100);
progress_bar->setTextVisible(false);
progress_bar->setVisible(false);
progress_bar->setFixedHeight(20);
main_layout->addWidget(progress_bar, 1, 0, Qt::AlignHCenter);
setLayout(main_layout);
setStyleSheet(R"(
Spinner {
background-color: black;
}
* {
background-color: transparent;
}
QLabel {
color: white;
font-size: 80px;
}
QProgressBar {
background-color: #373737;
width: 1000px;
border solid white;
border-radius: 10px;
}
QProgressBar::chunk {
border-radius: 10px;
background-color: white;
}
)");
notifier = new QSocketNotifier(fileno(stdin), QSocketNotifier::Read);
QObject::connect(notifier, &QSocketNotifier::activated, this, &Spinner::update);
};
void Spinner::update(int n) {
std::string line;
std::getline(std::cin, line);
if (line.length()) {
bool number = std::all_of(line.begin(), line.end(), ::isdigit);
text->setVisible(!number);
progress_bar->setVisible(number);
text->setText(QString::fromStdString(line));
if (number) {
progress_bar->setValue(std::stoi(line));
}
}
}
int main(int argc, char *argv[]) {
QSurfaceFormat fmt;
#ifdef __APPLE__
fmt.setVersion(3, 2);
fmt.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile);
fmt.setRenderableType(QSurfaceFormat::OpenGL);
#else
fmt.setRenderableType(QSurfaceFormat::OpenGLES);
#endif
QSurfaceFormat::setDefaultFormat(fmt);
Hardware::set_display_power(true);
Hardware::set_brightness(65);
QApplication a(argc, argv);
Spinner spinner;
setMainWindow(&spinner);
return a.exec();
}
-39
View File
@@ -1,39 +0,0 @@
#include <array>
#include <QLabel>
#include <QOpenGLWidget>
#include <QPixmap>
#include <QProgressBar>
#include <QSocketNotifier>
#include <QVariantAnimation>
#include <QWidget>
constexpr int spinner_fps = 30;
constexpr QSize spinner_size = QSize(360, 360);
class TrackWidget : public QWidget {
Q_OBJECT
public:
TrackWidget(QWidget *parent = nullptr);
private:
void paintEvent(QPaintEvent *event) override;
std::array<QPixmap, spinner_fps> track_imgs;
QPixmap comma_img;
QVariantAnimation m_anim;
};
class Spinner : public QWidget {
Q_OBJECT
public:
explicit Spinner(QWidget *parent = 0);
private:
QLabel *text;
QProgressBar *progress_bar;
QSocketNotifier *notifier;
public slots:
void update(int n);
};
BIN
View File
Binary file not shown.
-66
View File
@@ -1,66 +0,0 @@
#include <QApplication>
#include <QLabel>
#include <QPushButton>
#include <QScrollBar>
#include <QVBoxLayout>
#include <QWidget>
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QWidget window;
setMainWindow(&window);
Hardware::set_display_power(true);
Hardware::set_brightness(65);
QGridLayout *layout = new QGridLayout;
layout->setMargin(50);
QLabel *label = new QLabel(argv[1]);
label->setWordWrap(true);
label->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
ScrollView *scroll = new ScrollView(label);
scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
layout->addWidget(scroll, 0, 0, Qt::AlignTop);
// Scroll to the bottom
QObject::connect(scroll->verticalScrollBar(), &QAbstractSlider::rangeChanged, [=](){
scroll->verticalScrollBar()->setValue(scroll->verticalScrollBar()->maximum());
});
QPushButton *btn = new QPushButton();
#ifdef __aarch64__
btn->setText("Reboot");
QObject::connect(btn, &QPushButton::released, [=]() {
Hardware::reboot();
});
#else
btn->setText("Exit");
QObject::connect(btn, &QPushButton::released, &a, &QApplication::quit);
#endif
layout->addWidget(btn, 0, 0, Qt::AlignRight | Qt::AlignBottom);
window.setLayout(layout);
window.setStyleSheet(R"(
* {
outline: none;
color: white;
background-color: black;
font-size: 60px;
}
QPushButton {
padding: 50px;
padding-right: 100px;
padding-left: 100px;
border: 2px solid white;
border-radius: 20px;
margin-right: 40px;
}
)");
return a.exec();
}
-44
View File
@@ -1,44 +0,0 @@
#pragma once
#include <QtWidgets>
inline void configFont(QPainter &p, QString family, int size, const QString &style) {
QFont f(family);
f.setPixelSize(size);
f.setStyleName(style);
p.setFont(f);
}
inline void clearLayout(QLayout* layout) {
while (QLayoutItem* item = layout->takeAt(0)) {
if (QWidget* widget = item->widget()) {
widget->deleteLater();
}
if (QLayout* childLayout = item->layout()) {
clearLayout(childLayout);
}
delete item;
}
}
inline QString timeAgo(const QDateTime &date) {
int diff = date.secsTo(QDateTime::currentDateTimeUtc());
QString s;
if (diff < 60) {
s = "now";
} else if (diff < 60 * 60) {
int minutes = diff / 60;
s = QString("%1 minute%2 ago").arg(minutes).arg(minutes > 1 ? "s" : "");
} else if (diff < 60 * 60 * 24) {
int hours = diff / (60 * 60);
s = QString("%1 hour%2 ago").arg(hours).arg(hours > 1 ? "s" : "");
} else if (diff < 3600 * 24 * 7) {
int days = diff / (60 * 60 * 24);
s = QString("%1 day%2 ago").arg(days).arg(days > 1 ? "s" : "");
} else {
s = date.date().toString();
}
return s;
}
-217
View File
@@ -1,217 +0,0 @@
#include "selfdrive/ui/qt/widgets/cameraview.h"
#include "selfdrive/ui/qt/qt_window.h"
namespace {
const char frame_vertex_shader[] =
#ifdef NANOVG_GL3_IMPLEMENTATION
"#version 150 core\n"
#else
"#version 300 es\n"
#endif
"in vec4 aPosition;\n"
"in vec4 aTexCoord;\n"
"uniform mat4 uTransform;\n"
"out vec4 vTexCoord;\n"
"void main() {\n"
" gl_Position = uTransform * aPosition;\n"
" vTexCoord = aTexCoord;\n"
"}\n";
const char frame_fragment_shader[] =
#ifdef NANOVG_GL3_IMPLEMENTATION
"#version 150 core\n"
#else
"#version 300 es\n"
#endif
"precision mediump float;\n"
"uniform sampler2D uTexture;\n"
"in vec4 vTexCoord;\n"
"out vec4 colorOut;\n"
"void main() {\n"
" colorOut = texture(uTexture, vTexCoord.xy);\n"
#ifdef QCOM
" vec3 dz = vec3(0.0627f, 0.0627f, 0.0627f);\n"
" colorOut.rgb = ((vec3(1.0f, 1.0f, 1.0f) - dz) * colorOut.rgb / vec3(1.0f, 1.0f, 1.0f)) + dz;\n"
#endif
"}\n";
const mat4 device_transform = {{
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
}};
mat4 get_driver_view_transform() {
const float driver_view_ratio = 1.333;
mat4 transform;
if (Hardware::TICI()) {
// from dmonitoring.cc
const int full_width_tici = 1928;
const int full_height_tici = 1208;
const int adapt_width_tici = 668;
const int crop_x_offset = 32;
const int crop_y_offset = -196;
const float yscale = full_height_tici * driver_view_ratio / adapt_width_tici;
const float xscale = yscale*(1080)/(2160)*full_width_tici/full_height_tici;
transform = (mat4){{
xscale, 0.0, 0.0, xscale*crop_x_offset/full_width_tici*2,
0.0, yscale, 0.0, yscale*crop_y_offset/full_height_tici*2,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
}};
} else {
// frame from 4/3 to 16/9 display
transform = (mat4){{
driver_view_ratio*(1080)/(1920), 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
}};
}
return transform;
}
} // namespace
CameraViewWidget::CameraViewWidget(VisionStreamType stream_type, QWidget* parent) : stream_type(stream_type), QOpenGLWidget(parent) {
setAttribute(Qt::WA_OpaquePaintEvent);
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &CameraViewWidget::updateFrame);
}
CameraViewWidget::~CameraViewWidget() {
makeCurrent();
doneCurrent();
glDeleteVertexArrays(1, &frame_vao);
glDeleteBuffers(1, &frame_vbo);
glDeleteBuffers(1, &frame_ibo);
}
void CameraViewWidget::initializeGL() {
initializeOpenGLFunctions();
gl_shader = std::make_unique<GLShader>(frame_vertex_shader, frame_fragment_shader);
GLint frame_pos_loc = glGetAttribLocation(gl_shader->prog, "aPosition");
GLint frame_texcoord_loc = glGetAttribLocation(gl_shader->prog, "aTexCoord");
auto [x1, x2, y1, y2] = stream_type == VISION_STREAM_RGB_FRONT ? std::tuple(0.f, 1.f, 1.f, 0.f) : std::tuple(1.f, 0.f, 1.f, 0.f);
const uint8_t frame_indicies[] = {0, 1, 2, 0, 2, 3};
const float frame_coords[4][4] = {
{-1.0, -1.0, x2, y1}, //bl
{-1.0, 1.0, x2, y2}, //tl
{ 1.0, 1.0, x1, y2}, //tr
{ 1.0, -1.0, x1, y1}, //br
};
glGenVertexArrays(1, &frame_vao);
glBindVertexArray(frame_vao);
glGenBuffers(1, &frame_vbo);
glBindBuffer(GL_ARRAY_BUFFER, frame_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(frame_coords), frame_coords, GL_STATIC_DRAW);
glEnableVertexAttribArray(frame_pos_loc);
glVertexAttribPointer(frame_pos_loc, 2, GL_FLOAT, GL_FALSE,
sizeof(frame_coords[0]), (const void *)0);
glEnableVertexAttribArray(frame_texcoord_loc);
glVertexAttribPointer(frame_texcoord_loc, 2, GL_FLOAT, GL_FALSE,
sizeof(frame_coords[0]), (const void *)(sizeof(float) * 2));
glGenBuffers(1, &frame_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, frame_ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(frame_indicies), frame_indicies, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
if (stream_type == VISION_STREAM_RGB_FRONT) {
frame_mat = matmul(device_transform, get_driver_view_transform());
} else {
auto intrinsic_matrix = stream_type == VISION_STREAM_RGB_WIDE ? ecam_intrinsic_matrix : fcam_intrinsic_matrix;
float zoom_ = zoom / intrinsic_matrix.v[0];
if (stream_type == VISION_STREAM_RGB_WIDE) {
zoom_ *= 0.5;
}
float zx = zoom_ * 2 * intrinsic_matrix.v[2] / width();
float zy = zoom_ * 2 * intrinsic_matrix.v[5] / height();
const mat4 frame_transform = {{
zx, 0.0, 0.0, 0.0,
0.0, zy, 0.0, -y_offset / height() * 2,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
}};
frame_mat = matmul(device_transform, frame_transform);
}
vipc_client = std::make_unique<VisionIpcClient>("camerad", stream_type, true);
}
void CameraViewWidget::showEvent(QShowEvent *event) {
timer->start(0);
}
void CameraViewWidget::hideEvent(QHideEvent *event) {
timer->stop();
vipc_client->connected = false;
latest_frame = nullptr;
}
void CameraViewWidget::paintGL() {
if (!latest_frame) {
glClearColor(0, 0, 0, 1.0);
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
return;
}
glViewport(0, 0, width(), height());
glBindVertexArray(frame_vao);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture[latest_frame->idx]->frame_tex);
if (!Hardware::EON()) {
// this is handled in ion on QCOM
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, latest_frame->width, latest_frame->height,
0, GL_RGB, GL_UNSIGNED_BYTE, latest_frame->addr);
}
glUseProgram(gl_shader->prog);
glUniform1i(gl_shader->getUniformLocation("uTexture"), 0);
glUniformMatrix4fv(gl_shader->getUniformLocation("uTransform"), 1, GL_TRUE, frame_mat.v);
assert(glGetError() == GL_NO_ERROR);
glEnableVertexAttribArray(0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (const void *)0);
glDisableVertexAttribArray(0);
glBindVertexArray(0);
}
void CameraViewWidget::updateFrame() {
if (!vipc_client->connected && vipc_client->connect(false)) {
// init vision
for (int i = 0; i < vipc_client->num_buffers; i++) {
texture[i].reset(new EGLImageTexture(&vipc_client->buffers[i]));
glBindTexture(GL_TEXTURE_2D, texture[i]->frame_tex);
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);
}
latest_frame = nullptr;
}
if (vipc_client->connected) {
VisionBuf *buf = vipc_client->recv();
if (buf != nullptr) {
latest_frame = buf;
update();
emit frameUpdated();
} else {
LOGE("visionIPC receive timeout");
}
}
}
-44
View File
@@ -1,44 +0,0 @@
#pragma once
#include <memory>
#include <QOpenGLFunctions>
#include <QOpenGLWidget>
#include "cereal/visionipc/visionipc_client.h"
#include "selfdrive/common/glutil.h"
#include "selfdrive/common/mat.h"
#include "selfdrive/common/visionimg.h"
#include "selfdrive/ui/ui.h"
class CameraViewWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT
public:
using QOpenGLWidget::QOpenGLWidget;
explicit CameraViewWidget(VisionStreamType stream_type, QWidget* parent = nullptr);
~CameraViewWidget();
signals:
void frameUpdated();
protected:
void paintGL() override;
void initializeGL() override;
void showEvent(QShowEvent *event) override;
void hideEvent(QHideEvent *event) override;
protected slots:
void updateFrame();
private:
VisionBuf *latest_frame = nullptr;
GLuint frame_vao, frame_vbo, frame_ibo;
mat4 frame_mat;
std::unique_ptr<VisionIpcClient> vipc_client;
std::unique_ptr<EGLImageTexture> texture[UI_BUF_COUNT];
std::unique_ptr<GLShader> gl_shader;
VisionStreamType stream_type;
QTimer* timer;
};
-66
View File
@@ -1,66 +0,0 @@
#include "controls.h"
QFrame *horizontal_line(QWidget *parent) {
QFrame *line = new QFrame(parent);
line->setFrameShape(QFrame::StyledPanel);
line->setStyleSheet(R"(
margin-left: 40px;
margin-right: 40px;
border-width: 1px;
border-bottom-style: solid;
border-color: gray;
)");
line->setFixedHeight(2);
return line;
}
AbstractControl::AbstractControl(const QString &title, const QString &desc, const QString &icon, QWidget *parent) : QFrame(parent) {
QVBoxLayout *vlayout = new QVBoxLayout();
vlayout->setMargin(0);
hlayout = new QHBoxLayout;
hlayout->setMargin(0);
hlayout->setSpacing(20);
// left icon
if (!icon.isEmpty()) {
QPixmap pix(icon);
QLabel *icon = new QLabel();
icon->setPixmap(pix.scaledToWidth(80, Qt::SmoothTransformation));
icon->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
hlayout->addWidget(icon);
}
// title
title_label = new QPushButton(title);
title_label->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left;");
hlayout->addWidget(title_label);
vlayout->addLayout(hlayout);
// description
if (!desc.isEmpty()) {
description = new QLabel(desc);
description->setContentsMargins(40, 20, 40, 20);
description->setStyleSheet("font-size: 40px; color:grey");
description->setWordWrap(true);
description->setVisible(false);
vlayout->addWidget(description);
connect(title_label, &QPushButton::clicked, [=]() {
if (!description->isVisible()) {
emit showDescription();
}
description->setVisible(!description->isVisible());
});
}
setLayout(vlayout);
setStyleSheet("background-color: transparent;");
}
void AbstractControl::hideEvent(QHideEvent *e){
if(description != nullptr){
description->hide();
}
}
-130
View File
@@ -1,130 +0,0 @@
#pragma once
#include <QFrame>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include "selfdrive/common/params.h"
#include "selfdrive/ui/qt/widgets/toggle.h"
QFrame *horizontal_line(QWidget *parent = nullptr);
class AbstractControl : public QFrame {
Q_OBJECT
public:
void setDescription(const QString &desc) {
if(description) description->setText(desc);
}
signals:
void showDescription();
protected:
AbstractControl(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr);
void hideEvent(QHideEvent *e) override;
QSize minimumSizeHint() const override {
QSize size = QFrame::minimumSizeHint();
size.setHeight(120);
return size;
};
QHBoxLayout *hlayout;
QPushButton *title_label;
QLabel *description = nullptr;
};
// widget to display a value
class LabelControl : public AbstractControl {
Q_OBJECT
public:
LabelControl(const QString &title, const QString &text, const QString &desc = "", QWidget *parent = nullptr) : AbstractControl(title, desc, "", parent) {
label.setText(text);
label.setAlignment(Qt::AlignRight | Qt::AlignVCenter);
hlayout->addWidget(&label);
}
void setText(const QString &text) { label.setText(text); }
private:
QLabel label;
};
// widget for a button with a label
class ButtonControl : public AbstractControl {
Q_OBJECT
public:
template <typename Functor>
ButtonControl(const QString &title, const QString &text, const QString &desc, Functor functor, const QString &icon = "", QWidget *parent = nullptr) : AbstractControl(title, desc, icon, parent) {
btn.setText(text);
btn.setStyleSheet(R"(
QPushButton {
padding: 0;
border-radius: 50px;
font-size: 35px;
font-weight: 500;
color: #E4E4E4;
background-color: #393939;
}
QPushButton:disabled {
color: #33E4E4E4;
}
)");
btn.setFixedSize(250, 100);
QObject::connect(&btn, &QPushButton::released, functor);
hlayout->addWidget(&btn);
}
void setText(const QString &text) { btn.setText(text); }
public slots:
void setEnabled(bool enabled) {
btn.setEnabled(enabled);
};
private:
QPushButton btn;
};
class ToggleControl : public AbstractControl {
Q_OBJECT
public:
ToggleControl(const QString &title, const QString &desc = "", const QString &icon = "", const bool state = false, QWidget *parent = nullptr) : AbstractControl(title, desc, icon, parent) {
toggle.setFixedSize(150, 100);
if (state) {
toggle.togglePosition();
}
hlayout->addWidget(&toggle);
QObject::connect(&toggle, &Toggle::stateChanged, this, &ToggleControl::toggleFlipped);
}
void setEnabled(bool enabled) { toggle.setEnabled(enabled); }
signals:
void toggleFlipped(bool state);
protected:
Toggle toggle;
};
// widget to toggle params
class ParamControl : public ToggleControl {
Q_OBJECT
public:
ParamControl(const QString &param, const QString &title, const QString &desc, const QString &icon, QWidget *parent = nullptr) : ToggleControl(title, desc, icon, false, parent) {
if (params.getBool(param.toStdString().c_str())) {
toggle.togglePosition();
}
QObject::connect(this, &ToggleControl::toggleFlipped, [=](bool state) {
params.putBool(param.toStdString().c_str(), state);
});
}
private:
Params params;
};
-70
View File
@@ -1,70 +0,0 @@
#include "drive_stats.h"
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QVBoxLayout>
#include "selfdrive/common/params.h"
#include "selfdrive/ui/qt/request_repeater.h"
const double MILE_TO_KM = 1.60934;
static QLayout* build_stat_layout(QLabel** metric, const QString& name) {
QVBoxLayout* layout = new QVBoxLayout;
layout->setMargin(0);
*metric = new QLabel("0");
(*metric)->setStyleSheet("font-size: 80px; font-weight: 600;");
layout->addWidget(*metric, 0, Qt::AlignLeft);
QLabel* label = new QLabel(name);
label->setStyleSheet("font-size: 45px; font-weight: 500;");
layout->addWidget(label, 0, Qt::AlignLeft);
return layout;
}
void DriveStats::parseResponse(const QString& response) {
QJsonDocument doc = QJsonDocument::fromJson(response.trimmed().toUtf8());
if (doc.isNull()) {
qDebug() << "JSON Parse failed on getting past drives statistics";
return;
}
auto update = [](const QJsonObject &obj, StatsLabels& labels, bool metric) {
labels.routes->setText(QString::number((int)obj["routes"].toDouble()));
labels.distance->setText(QString::number(int(obj["distance"].toDouble() * (metric ? MILE_TO_KM : 1))));
labels.hours->setText(QString::number((int)(obj["minutes"].toDouble() / 60)));
};
QJsonObject json = doc.object();
update(json["all"].toObject(), all_, metric);
update(json["week"].toObject(), week_, metric);
}
DriveStats::DriveStats(QWidget* parent) : QWidget(parent) {
metric = Params().getBool("IsMetric");
QString distance_unit = metric ? "KM" : "MILES";
auto add_stats_layouts = [&](QGridLayout* gl, StatsLabels& labels, int row, const QString &distance_unit) {
gl->addLayout(build_stat_layout(&labels.routes, "DRIVES"), row, 0, 3, 1);
gl->addLayout(build_stat_layout(&labels.distance, distance_unit), row, 1, 3, 1);
gl->addLayout(build_stat_layout(&labels.hours, "HOURS"), row, 2, 3, 1);
};
QGridLayout* gl = new QGridLayout(this);
gl->setMargin(0);
gl->addWidget(new QLabel("ALL TIME"), 0, 0, 1, 3);
add_stats_layouts(gl, all_, 1, distance_unit);
gl->addWidget(new QLabel("PAST WEEK"), 6, 0, 1, 3);
add_stats_layouts(gl, week_, 7, distance_unit);
QString dongleId = QString::fromStdString(Params().get("DongleId"));
QString url = "https://api.commadotai.com/v1.1/devices/" + dongleId + "/stats";
RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_DriveStats", 30);
QObject::connect(repeater, &RequestRepeater::receivedResponse, this, &DriveStats::parseResponse);
setLayout(gl);
setStyleSheet(R"(QLabel {font-size: 48px; font-weight: 500;})");
}
-19
View File
@@ -1,19 +0,0 @@
#pragma once
#include <QLabel>
class DriveStats : public QWidget {
Q_OBJECT
public:
explicit DriveStats(QWidget* parent = 0);
private:
bool metric;
struct StatsLabels {
QLabel *routes, *distance, *hours;
} all_, week_;
private slots:
void parseResponse(const QString &response);
};
-181
View File
@@ -1,181 +0,0 @@
#include "input.h"
#include <QPushButton>
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/hardware/hw.h"
InputDialog::InputDialog(const QString &prompt_text, QWidget *parent) : QDialog(parent) {
layout = new QVBoxLayout();
layout->setContentsMargins(50, 50, 50, 50);
layout->setSpacing(20);
// build header
QHBoxLayout *header_layout = new QHBoxLayout();
label = new QLabel(prompt_text, this);
label->setStyleSheet(R"(font-size: 75px; font-weight: 500;)");
header_layout->addWidget(label, 1, Qt::AlignLeft);
QPushButton* cancel_btn = new QPushButton("Cancel");
cancel_btn->setStyleSheet(R"(
padding: 30px;
padding-right: 45px;
padding-left: 45px;
border-radius: 7px;
font-size: 45px;
background-color: #444444;
)");
header_layout->addWidget(cancel_btn, 0, Qt::AlignRight);
QObject::connect(cancel_btn, &QPushButton::released, this, &InputDialog::reject);
QObject::connect(cancel_btn, &QPushButton::released, this, &InputDialog::cancel);
layout->addLayout(header_layout);
// text box
layout->addSpacing(20);
line = new QLineEdit();
line->setStyleSheet(R"(
border: none;
background-color: #444444;
font-size: 80px;
font-weight: 500;
padding: 10px;
)");
layout->addWidget(line, 1, Qt::AlignTop);
k = new Keyboard(this);
QObject::connect(k, &Keyboard::emitButton, this, &InputDialog::handleInput);
layout->addWidget(k, 2, Qt::AlignBottom);
setStyleSheet(R"(
* {
color: white;
background-color: black;
}
)");
setLayout(layout);
}
QString InputDialog::getText(const QString &prompt, int minLength) {
InputDialog d = InputDialog(prompt);
d.setMinLength(minLength);
const int ret = d.exec();
return ret ? d.text() : QString();
}
QString InputDialog::text() {
return line->text();
}
int InputDialog::exec() {
setMainWindow(this);
return QDialog::exec();
}
void InputDialog::show(){
setMainWindow(this);
}
void InputDialog::handleInput(const QString &s) {
if (!QString::compare(s,"")) {
line->backspace();
}
if (!QString::compare(s,"")) {
if (line->text().length() >= minLength){
done(QDialog::Accepted);
emitText(line->text());
} else {
setMessage("Need at least "+QString::number(minLength)+" characters!", false);
}
}
QVector<QString> control_buttons {"", "", "ABC", "", "#+=", "", "123"};
for(QString c : control_buttons) {
if (!QString::compare(s, c)) {
return;
}
}
line->insert(s.left(1));
}
void InputDialog::setMessage(const QString &message, bool clearInputField){
label->setText(message);
if (clearInputField){
line->setText("");
}
}
void InputDialog::setMinLength(int length){
minLength = length;
}
ConfirmationDialog::ConfirmationDialog(const QString &prompt_text, const QString &confirm_text, const QString &cancel_text,
QWidget *parent):QDialog(parent) {
setWindowFlags(Qt::Popup);
layout = new QVBoxLayout();
layout->setMargin(25);
prompt = new QLabel(prompt_text, this);
prompt->setWordWrap(true);
prompt->setAlignment(Qt::AlignHCenter);
prompt->setStyleSheet(R"(font-size: 55px; font-weight: 400;)");
layout->addWidget(prompt, 1, Qt::AlignTop | Qt::AlignHCenter);
// cancel + confirm buttons
QHBoxLayout *btn_layout = new QHBoxLayout();
btn_layout->setSpacing(20);
btn_layout->addStretch(1);
layout->addLayout(btn_layout);
if (cancel_text.length()) {
QPushButton* cancel_btn = new QPushButton(cancel_text);
btn_layout->addWidget(cancel_btn, 0, Qt::AlignRight);
QObject::connect(cancel_btn, &QPushButton::released, this, &ConfirmationDialog::reject);
}
if (confirm_text.length()) {
QPushButton* confirm_btn = new QPushButton(confirm_text);
btn_layout->addWidget(confirm_btn, 0, Qt::AlignRight);
QObject::connect(confirm_btn, &QPushButton::released, this, &ConfirmationDialog::accept);
}
setFixedSize(900, 350);
setStyleSheet(R"(
* {
color: black;
background-color: white;
}
QPushButton {
font-size: 40px;
padding: 30px;
padding-right: 45px;
padding-left: 45px;
border-radius: 7px;
background-color: #44444400;
}
)");
setLayout(layout);
}
bool ConfirmationDialog::alert(const QString &prompt_text, QWidget *parent) {
ConfirmationDialog d = ConfirmationDialog(prompt_text, "Ok", "", parent);
return d.exec();
}
bool ConfirmationDialog::confirm(const QString &prompt_text, QWidget *parent) {
ConfirmationDialog d = ConfirmationDialog(prompt_text, "Ok", "Cancel", parent);
return d.exec();
}
int ConfirmationDialog::exec() {
// TODO: make this work without fullscreen
if (Hardware::TICI()) {
setMainWindow(this);
}
return QDialog::exec();
}
-56
View File
@@ -1,56 +0,0 @@
#pragma once
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QString>
#include <QVBoxLayout>
#include <QWidget>
#include "selfdrive/ui/qt/widgets/keyboard.h"
class InputDialog : public QDialog {
Q_OBJECT
public:
explicit InputDialog(const QString &prompt_text, QWidget* parent = 0);
static QString getText(const QString &prompt, int minLength = -1);
QString text();
void setMessage(const QString &message, bool clearInputField = true);
void setMinLength(int length);
void show();
private:
int minLength;
QLineEdit *line;
Keyboard *k;
QLabel *label;
QVBoxLayout *layout;
public slots:
int exec() override;
private slots:
void handleInput(const QString &s);
signals:
void cancel();
void emitText(const QString &text);
};
class ConfirmationDialog : public QDialog {
Q_OBJECT
public:
explicit ConfirmationDialog(const QString &prompt_text, const QString &confirm_text = "Ok",
const QString &cancel_text = "Cancel", QWidget* parent = 0);
static bool alert(const QString &prompt_text, QWidget *parent = 0);
static bool confirm(const QString &prompt_text, QWidget *parent = 0);
private:
QLabel *prompt;
QVBoxLayout *layout;
public slots:
int exec() override;
};
-127
View File
@@ -1,127 +0,0 @@
#include "keyboard.h"
#include <QButtonGroup>
#include <QDebug>
#include <QHBoxLayout>
#include <QPushButton>
#include <QStackedLayout>
#include <QVBoxLayout>
const int DEFAULT_STRETCH = 1;
const int SPACEBAR_STRETCH = 3;
KeyboardLayout::KeyboardLayout(QWidget* parent, const std::vector<QVector<QString>>& layout) : QWidget(parent) {
QVBoxLayout* vlayout = new QVBoxLayout;
vlayout->setMargin(0);
vlayout->setSpacing(35);
QButtonGroup* btn_group = new QButtonGroup(this);
QObject::connect(btn_group, SIGNAL(buttonClicked(QAbstractButton*)), parent, SLOT(handleButton(QAbstractButton*)));
for (const auto &s : layout) {
QHBoxLayout *hlayout = new QHBoxLayout;
hlayout->setSpacing(25);
if (vlayout->count() == 1) {
hlayout->addSpacing(90);
}
for (const QString &p : s) {
QPushButton* btn = new QPushButton(p);
btn->setFixedHeight(135);
btn_group->addButton(btn);
hlayout->addWidget(btn, p == QString(" ") ? SPACEBAR_STRETCH : DEFAULT_STRETCH);
}
if (vlayout->count() == 1) {
hlayout->addSpacing(90);
}
vlayout->addLayout(hlayout);
}
setStyleSheet(R"(
* {
outline: none;
}
QPushButton {
font-size: 65px;
margin: 0px;
padding: 0px;
border-radius: 30px;
color: #dddddd;
background-color: #444444;
}
QPushButton:pressed {
background-color: #000000;
}
)");
setLayout(vlayout);
}
Keyboard::Keyboard(QWidget *parent) : QFrame(parent) {
main_layout = new QStackedLayout;
main_layout->setMargin(0);
// lowercase
std::vector<QVector<QString>> lowercase = {
{"q","w","e","r","t","y","u","i","o","p"},
{"a","s","d","f","g","h","j","k","l"},
{"","z","x","c","v","b","n","m",""},
{"123"," ",""},
};
main_layout->addWidget(new KeyboardLayout(this, lowercase));
// uppercase
std::vector<QVector<QString>> uppercase = {
{"Q","W","E","R","T","Y","U","I","O","P"},
{"A","S","D","F","G","H","J","K","L"},
{"","Z","X","C","V","B","N","M",""},
{"123"," ",""},
};
main_layout->addWidget(new KeyboardLayout(this, uppercase));
// numbers + specials
std::vector<QVector<QString>> numbers = {
{"1","2","3","4","5","6","7","8","9","0"},
{"-","/",":",";","(",")","$","&&","@","\""},
{"#+=",".",",","?","!","`",""},
{"ABC"," ",""},
};
main_layout->addWidget(new KeyboardLayout(this, numbers));
// extra specials
std::vector<QVector<QString>> specials = {
{"[","]","{","}","#","%","^","*","+","="},
{"_","\\","|","~","<",">","","£","¥",""},
{"123",".",",","?","!","`",""},
{"ABC"," ",""},
};
main_layout->addWidget(new KeyboardLayout(this, specials));
setLayout(main_layout);
main_layout->setCurrentIndex(0);
}
void Keyboard::handleButton(QAbstractButton* m_button) {
QString id = m_button->text();
if (!QString::compare(m_button->text(), "") || !QString::compare(m_button->text(), "ABC")) {
main_layout->setCurrentIndex(0);
}
if (!QString::compare(m_button->text(), "")) {
main_layout->setCurrentIndex(1);
}
if (!QString::compare(m_button->text(), "123")) {
main_layout->setCurrentIndex(2);
}
if (!QString::compare(m_button->text(), "#+=")) {
main_layout->setCurrentIndex(3);
}
if (!QString::compare(m_button->text(), "")) {
main_layout->setCurrentIndex(0);
}
if ("A" <= id && id <= "Z") {
main_layout->setCurrentIndex(0);
}
emit emitButton(m_button->text());
}
-31
View File
@@ -1,31 +0,0 @@
#pragma once
#include <vector>
#include <QAbstractButton>
#include <QFrame>
#include <QStackedLayout>
#include <QString>
#include <QWidget>
class KeyboardLayout : public QWidget {
Q_OBJECT
public:
explicit KeyboardLayout(QWidget* parent, const std::vector<QVector<QString>>& layout);
};
class Keyboard : public QFrame {
Q_OBJECT
public:
explicit Keyboard(QWidget *parent = 0);
private:
QStackedLayout* main_layout;
private slots:
void handleButton(QAbstractButton* m_button);
signals:
void emitButton(const QString &s);
};
-114
View File
@@ -1,114 +0,0 @@
#include "offroad_alerts.h"
#include <QHBoxLayout>
#include <QJsonDocument>
#include <QJsonObject>
#include <QVBoxLayout>
#include "selfdrive/common/util.h"
#include "selfdrive/hardware/hw.h"
OffroadAlert::OffroadAlert(QWidget* parent) : QFrame(parent) {
QVBoxLayout *layout = new QVBoxLayout;
layout->setMargin(50);
layout->setSpacing(30);
QWidget *alerts_widget = new QWidget(this);
alerts_layout = new QVBoxLayout;
alerts_layout->setMargin(0);
alerts_layout->setSpacing(30);
alerts_widget->setLayout(alerts_layout);
alerts_widget->setStyleSheet("background-color: transparent;");
// release notes
releaseNotes.setWordWrap(true);
releaseNotes.setVisible(false);
releaseNotes.setStyleSheet("font-size: 48px;");
releaseNotes.setAlignment(Qt::AlignTop);
releaseNotesScroll = new ScrollView(&releaseNotes, this);
layout->addWidget(releaseNotesScroll);
alertsScroll = new ScrollView(alerts_widget, this);
layout->addWidget(alertsScroll);
// bottom footer, dismiss + reboot buttons
QHBoxLayout *footer_layout = new QHBoxLayout();
layout->addLayout(footer_layout);
QPushButton *dismiss_btn = new QPushButton("Dismiss");
dismiss_btn->setFixedSize(400, 125);
footer_layout->addWidget(dismiss_btn, 0, Qt::AlignBottom | Qt::AlignLeft);
QObject::connect(dismiss_btn, &QPushButton::released, this, &OffroadAlert::closeAlerts);
rebootBtn.setText("Reboot and Update");
rebootBtn.setFixedSize(600, 125);
rebootBtn.setVisible(false);
footer_layout->addWidget(&rebootBtn, 0, Qt::AlignBottom | Qt::AlignRight);
QObject::connect(&rebootBtn, &QPushButton::released, [=]() { Hardware::reboot(); });
setLayout(layout);
setStyleSheet(R"(
* {
font-size: 48px;
color: white;
}
QFrame {
border-radius: 30px;
background-color: #393939;
}
QPushButton {
color: black;
font-weight: 500;
border-radius: 30px;
background-color: white;
}
)");
}
void OffroadAlert::refresh() {
if (alerts.empty()) {
// setup labels for each alert
QString json = QString::fromStdString(util::read_file("../controls/lib/alerts_offroad.json"));
QJsonObject obj = QJsonDocument::fromJson(json.toUtf8()).object();
for (auto &k : obj.keys()) {
QLabel *l = new QLabel(this);
alerts[k.toStdString()] = l;
int severity = obj[k].toObject()["severity"].toInt();
l->setMargin(60);
l->setWordWrap(true);
l->setStyleSheet("background-color: " + QString(severity ? "#E22C2C" : "#292929"));
l->setVisible(false);
alerts_layout->addWidget(l);
}
alerts_layout->addStretch(1);
}
updateAlerts();
rebootBtn.setVisible(updateAvailable);
releaseNotesScroll->setVisible(updateAvailable);
releaseNotes.setText(QString::fromStdString(params.get("ReleaseNotes")));
alertsScroll->setVisible(!updateAvailable);
for (const auto& [k, label] : alerts) {
label->setVisible(!label->text().isEmpty());
}
}
void OffroadAlert::updateAlerts() {
alertCount = 0;
updateAvailable = params.getBool("UpdateAvailable");
for (const auto& [key, label] : alerts) {
auto bytes = params.get(key.c_str());
if (bytes.size()) {
QJsonDocument doc_par = QJsonDocument::fromJson(QByteArray(bytes.data(), bytes.size()));
QJsonObject obj = doc_par.object();
label->setText(obj.value("text").toString());
alertCount++;
} else {
label->setText("");
}
}
}
-38
View File
@@ -1,38 +0,0 @@
#pragma once
#include <map>
#include <QFrame>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include "selfdrive/common/params.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
class OffroadAlert : public QFrame {
Q_OBJECT
public:
explicit OffroadAlert(QWidget *parent = 0);
int alertCount = 0;
bool updateAvailable;
private:
void updateAlerts();
Params params;
std::map<std::string, QLabel*> alerts;
QLabel releaseNotes;
QPushButton rebootBtn;
ScrollView *alertsScroll;
ScrollView *releaseNotesScroll;
QVBoxLayout *alerts_layout;
signals:
void closeAlerts();
public slots:
void refresh();
};
-47
View File
@@ -1,47 +0,0 @@
#include "scrollview.h"
#include <QScrollBar>
ScrollView::ScrollView(QWidget *w, QWidget *parent) : QScrollArea(parent){
setWidget(w);
setWidgetResizable(true);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setStyleSheet("ScrollView { background-color:transparent; }");
QString style = R"(
QScrollBar:vertical {
border: none;
background: transparent;
width:10px;
margin: 0;
}
QScrollBar::handle:vertical {
min-height: 0px;
border-radius: 4px;
background-color: white;
}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
height: 0px;
}
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
background: none;
}
)";
verticalScrollBar()->setStyleSheet(style);
horizontalScrollBar()->setStyleSheet(style);
QScroller *scroller = QScroller::scroller(this->viewport());
QScrollerProperties sp = scroller->scrollerProperties();
sp.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QVariant::fromValue<QScrollerProperties::OvershootPolicy>(QScrollerProperties::OvershootAlwaysOff));
sp.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QVariant::fromValue<QScrollerProperties::OvershootPolicy>(QScrollerProperties::OvershootAlwaysOff));
scroller->grabGesture(this->viewport(), QScroller::LeftMouseButtonGesture);
scroller->setScrollerProperties(sp);
}
void ScrollView::hideEvent(QHideEvent *e){
verticalScrollBar()->setValue(0);
}
-13
View File
@@ -1,13 +0,0 @@
#pragma once
#include <QScrollArea>
#include <QScroller>
class ScrollView : public QScrollArea {
Q_OBJECT
public:
explicit ScrollView(QWidget *w = nullptr, QWidget *parent = nullptr);
protected:
void hideEvent(QHideEvent *e) override;
};
-283
View File
@@ -1,283 +0,0 @@
#include "setup.h"
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLabel>
#include <QPushButton>
#include <QStackedWidget>
#include <QTimer>
#include <QVBoxLayout>
#include <QrCode.hpp>
#include "selfdrive/common/params.h"
#include "selfdrive/ui/qt/request_repeater.h"
using qrcodegen::QrCode;
PairingQRWidget::PairingQRWidget(QWidget* parent) : QWidget(parent) {
qrCode = new QLabel;
qrCode->setScaledContents(true);
QVBoxLayout* v = new QVBoxLayout;
v->addWidget(qrCode, 0, Qt::AlignCenter);
setLayout(v);
QTimer* timer = new QTimer(this);
timer->start(30 * 1000);
connect(timer, &QTimer::timeout, this, &PairingQRWidget::refresh);
}
void PairingQRWidget::showEvent(QShowEvent *event){
refresh();
}
void PairingQRWidget::refresh(){
Params params;
QString IMEI = QString::fromStdString(params.get("IMEI"));
QString serial = QString::fromStdString(params.get("HardwareSerial"));
if (std::min(IMEI.length(), serial.length()) <= 5) {
qrCode->setText("Error getting serial: contact support");
qrCode->setWordWrap(true);
qrCode->setStyleSheet(R"(font-size: 60px;)");
return;
}
QString pairToken = CommaApi::create_jwt({{"pair", true}});
QString qrString = IMEI + "--" + serial + "--" + pairToken;
this->updateQrCode(qrString);
}
void PairingQRWidget::updateQrCode(QString text) {
QrCode qr = QrCode::encodeText(text.toUtf8().data(), QrCode::Ecc::LOW);
qint32 sz = qr.getSize();
// make the image larger so we can have a white border
QImage im(sz + 2, sz + 2, QImage::Format_RGB32);
QRgb black = qRgb(0, 0, 0);
QRgb white = qRgb(255, 255, 255);
for (int y = 0; y < sz + 2; y++) {
for (int x = 0; x < sz + 2; x++) {
im.setPixel(x, y, white);
}
}
for (int y = 0; y < sz; y++) {
for (int x = 0; x < sz; x++) {
im.setPixel(x + 1, y + 1, qr.getModule(x, y) ? black : white);
}
}
// Integer division to prevent anti-aliasing
int approx500 = (500 / (sz + 2)) * (sz + 2);
qrCode->setPixmap(QPixmap::fromImage(im.scaled(approx500, approx500, Qt::KeepAspectRatio, Qt::FastTransformation), Qt::MonoOnly));
qrCode->setFixedSize(approx500, approx500);
}
PrimeUserWidget::PrimeUserWidget(QWidget* parent) : QWidget(parent) {
mainLayout = new QVBoxLayout;
mainLayout->setMargin(30);
QLabel* commaPrime = new QLabel("COMMA PRIME");
mainLayout->addWidget(commaPrime, 0, Qt::AlignTop);
username = new QLabel();
username->setStyleSheet("font-size: 55px;"); // TODO: fit width
mainLayout->addWidget(username, 0, Qt::AlignTop);
mainLayout->addSpacing(100);
QLabel* commaPoints = new QLabel("COMMA POINTS");
commaPoints->setStyleSheet(R"(
color: #b8b8b8;
)");
mainLayout->addWidget(commaPoints, 0, Qt::AlignTop);
points = new QLabel();
mainLayout->addWidget(points, 0, Qt::AlignTop);
setLayout(mainLayout);
setStyleSheet(R"(
QLabel {
font-size: 70px;
font-weight: 500;
}
)");
// set up API requests
QString dongleId = QString::fromStdString(Params().get("DongleId"));
if (!dongleId.length()) {
return;
}
QString url = "https://api.commadotai.com/v1/devices/" + dongleId + "/owner";
RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_Owner", 6);
QObject::connect(repeater, &RequestRepeater::receivedResponse, this, &PrimeUserWidget::replyFinished);
}
void PrimeUserWidget::replyFinished(const QString &response) {
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (doc.isNull()) {
qDebug() << "JSON Parse failed on getting username and points";
return;
}
QJsonObject json = doc.object();
QString points_str = QString::number(json["points"].toInt());
QString username_str = json["username"].toString();
if (username_str.length()) {
username_str = "@" + username_str;
}
username->setText(username_str);
points->setText(points_str);
}
PrimeAdWidget::PrimeAdWidget(QWidget* parent) : QWidget(parent) {
QVBoxLayout* vlayout = new QVBoxLayout;
vlayout->setMargin(30);
vlayout->setSpacing(15);
vlayout->addWidget(new QLabel("Upgrade now"), 1, Qt::AlignTop);
QLabel* description = new QLabel("Become a comma prime member at my.comma.ai and get premium features!");
description->setStyleSheet(R"(
font-size: 50px;
color: #b8b8b8;
)");
description->setWordWrap(true);
vlayout->addWidget(description, 2, Qt::AlignTop);
QVector<QString> features = {"✓ REMOTE ACCESS", "✓ 14 DAYS OF STORAGE", "✓ DEVELOPER PERKS"};
for (auto &f: features) {
QLabel* feature = new QLabel(f);
feature->setStyleSheet(R"(font-size: 40px;)");
vlayout->addWidget(feature, 0, Qt::AlignBottom);
}
setLayout(vlayout);
}
SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
mainLayout = new QStackedWidget;
// Unpaired, registration prompt layout
QVBoxLayout* finishRegistationLayout = new QVBoxLayout;
finishRegistationLayout->setMargin(30);
QLabel* registrationDescription = new QLabel("Pair your device with the comma connect app");
registrationDescription->setWordWrap(true);
registrationDescription->setAlignment(Qt::AlignCenter);
registrationDescription->setStyleSheet(R"(
font-size: 55px;
font-weight: 400;
)");
finishRegistationLayout->addWidget(registrationDescription);
QPushButton* finishButton = new QPushButton("Finish setup");
finishButton->setFixedHeight(200);
finishButton->setStyleSheet(R"(
border-radius: 30px;
font-size: 55px;
font-weight: 500;
background: #585858;
)");
finishRegistationLayout->addWidget(finishButton);
QObject::connect(finishButton, &QPushButton::released, this, &SetupWidget::showQrCode);
QWidget* finishRegistration = new QWidget;
finishRegistration->setLayout(finishRegistationLayout);
mainLayout->addWidget(finishRegistration);
// Pairing QR code layout
QVBoxLayout* qrLayout = new QVBoxLayout;
qrLayout->addSpacing(30);
QLabel* qrLabel = new QLabel("Scan with comma connect!");
qrLabel->setWordWrap(true);
qrLabel->setAlignment(Qt::AlignHCenter);
qrLabel->setStyleSheet(R"(
font-size: 55px;
font-weight: 400;
)");
qrLayout->addWidget(qrLabel, 0, Qt::AlignTop);
qrLayout->addWidget(new PairingQRWidget, 1);
QWidget* q = new QWidget;
q->setLayout(qrLayout);
mainLayout->addWidget(q);
primeAd = new PrimeAdWidget;
mainLayout->addWidget(primeAd);
primeUser = new PrimeUserWidget;
mainLayout->addWidget(primeUser);
mainLayout->setCurrentWidget(primeAd);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(mainLayout);
setLayout(layout);
setStyleSheet(R"(
SetupWidget {
background-color: #292929;
}
* {
font-size: 90px;
font-weight: 500;
border-radius: 40px;
}
)");
// Retain size while hidden
QSizePolicy sp_retain = sizePolicy();
sp_retain.setRetainSizeWhenHidden(true);
setSizePolicy(sp_retain);
// set up API requests
QString dongleId = QString::fromStdString(Params().get("DongleId"));
QString url = "https://api.commadotai.com/v1.1/devices/" + dongleId + "/";
RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_Device", 5);
QObject::connect(repeater, &RequestRepeater::receivedResponse, this, &SetupWidget::replyFinished);
QObject::connect(repeater, &RequestRepeater::failedResponse, this, &SetupWidget::parseError);
hide(); // Only show when first request comes back
}
void SetupWidget::parseError(const QString &response) {
show();
showQr = false;
mainLayout->setCurrentIndex(0);
}
void SetupWidget::showQrCode(){
showQr = true;
mainLayout->setCurrentIndex(1);
}
void SetupWidget::replyFinished(const QString &response) {
show();
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (doc.isNull()) {
qDebug() << "JSON Parse failed on getting pairing and prime status";
return;
}
QJsonObject json = doc.object();
bool is_paired = json["is_paired"].toBool();
bool is_prime = json["prime"].toBool();
if (!is_paired) {
mainLayout->setCurrentIndex(showQr);
} else if (!is_prime) {
showQr = false;
mainLayout->setCurrentWidget(primeAd);
} else {
showQr = false;
mainLayout->setCurrentWidget(primeUser);
}
}
-63
View File
@@ -1,63 +0,0 @@
#pragma once
#include <QLabel>
#include <QStackedWidget>
#include <QVBoxLayout>
#include <QWidget>
#include "selfdrive/ui/qt/api.h"
class PairingQRWidget : public QWidget {
Q_OBJECT
public:
explicit PairingQRWidget(QWidget* parent = 0);
private:
QLabel* qrCode;
void updateQrCode(QString text);
void showEvent(QShowEvent *event) override;
private slots:
void refresh();
};
class PrimeUserWidget : public QWidget {
Q_OBJECT
public:
explicit PrimeUserWidget(QWidget* parent = 0);
private:
QVBoxLayout* mainLayout;
QLabel* username;
QLabel* points;
CommaApi* api;
private slots:
void replyFinished(const QString &response);
};
class PrimeAdWidget : public QWidget {
Q_OBJECT
public:
explicit PrimeAdWidget(QWidget* parent = 0);
};
class SetupWidget : public QFrame {
Q_OBJECT
public:
explicit SetupWidget(QWidget* parent = 0);
private:
QStackedWidget* mainLayout;
CommaApi* api;
PrimeAdWidget *primeAd;
PrimeUserWidget *primeUser;
bool showQr = false;
private slots:
void parseError(const QString &response);
void replyFinished(const QString &response);
void showQrCode();
};
-83
View File
@@ -1,83 +0,0 @@
#include "ssh_keys.h"
#include <QHBoxLayout>
#include <QNetworkReply>
#include "selfdrive/common/params.h"
#include "selfdrive/ui/qt/api.h"
#include "selfdrive/ui/qt/widgets/input.h"
SshControl::SshControl() : AbstractControl("SSH Keys", "Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username.", "") {
// setup widget
hlayout->addStretch(1);
username_label.setAlignment(Qt::AlignVCenter);
username_label.setStyleSheet("color: #aaaaaa");
hlayout->addWidget(&username_label);
btn.setStyleSheet(R"(
padding: 0;
border-radius: 50px;
font-size: 35px;
font-weight: 500;
color: #E4E4E4;
background-color: #393939;
)");
btn.setFixedSize(250, 100);
hlayout->addWidget(&btn);
QObject::connect(&btn, &QPushButton::released, [=]() {
if (btn.text() == "ADD") {
QString username = InputDialog::getText("Enter your GitHub username");
if (username.length() > 0) {
btn.setText("LOADING");
btn.setEnabled(false);
getUserKeys(username);
}
} else {
params.remove("GithubUsername");
params.remove("GithubSshKeys");
refresh();
}
});
refresh();
}
void SshControl::refresh() {
QString param = QString::fromStdString(params.get("GithubSshKeys"));
if (param.length()) {
username_label.setText(QString::fromStdString(params.get("GithubUsername")));
btn.setText("REMOVE");
} else {
username_label.setText("");
btn.setText("ADD");
}
btn.setEnabled(true);
}
void SshControl::getUserKeys(const QString &username) {
HttpRequest *request = new HttpRequest(this, "https://github.com/" + username + ".keys", "", false);
QObject::connect(request, &HttpRequest::receivedResponse, [=](const QString &resp) {
if (!resp.isEmpty()) {
Params params;
params.put("GithubUsername", username.toStdString());
params.put("GithubSshKeys", resp.toStdString());
} else {
ConfirmationDialog::alert("Username '" + username + "' has no keys on GitHub");
}
refresh();
request->deleteLater();
});
QObject::connect(request, &HttpRequest::failedResponse, [=] {
ConfirmationDialog::alert("Username '" + username + "' doesn't exist on GitHub");
refresh();
request->deleteLater();
});
QObject::connect(request, &HttpRequest::timeoutResponse, [=] {
ConfirmationDialog::alert("Request timed out");
refresh();
request->deleteLater();
});
}
-35
View File
@@ -1,35 +0,0 @@
#pragma once
#include <QPushButton>
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/qt/widgets/controls.h"
// SSH enable toggle
class SshToggle : public ToggleControl {
Q_OBJECT
public:
SshToggle() : ToggleControl("Enable SSH", "", "", Hardware::get_ssh_enabled()) {
QObject::connect(this, &SshToggle::toggleFlipped, [=](bool state) {
Hardware::set_ssh_enabled(state);
});
}
};
// SSH key management widget
class SshControl : public AbstractControl {
Q_OBJECT
public:
SshControl();
private:
Params params;
QPushButton btn;
QLabel username_label;
void refresh();
void getUserKeys(const QString &username);
};
-81
View File
@@ -1,81 +0,0 @@
#include "toggle.h"
Toggle::Toggle(QWidget *parent) : QAbstractButton(parent),
_height(80),
_height_rect(60),
on(false),
_anim(new QPropertyAnimation(this, "offset_circle", this))
{
_radius = _height / 2;
_x_circle = _radius;
_y_circle = _radius;
_y_rect = (_height - _height_rect)/2;
circleColor = QColor(0xffffff); // placeholder
green = QColor(0xffffff); // placeholder
setEnabled(true);
}
void Toggle::paintEvent(QPaintEvent *e) {
this->setFixedHeight(_height);
QPainter p(this);
p.setPen(Qt::NoPen);
p.setRenderHint(QPainter::Antialiasing, true);
// Draw toggle background left
p.setBrush(green);
p.drawRoundedRect(QRect(0, _y_rect, _x_circle + _radius, _height_rect), _height_rect/2, _height_rect/2);
// Draw toggle background right
p.setBrush(QColor(0x393939));
p.drawRoundedRect(QRect(_x_circle - _radius, _y_rect, width() - (_x_circle - _radius), _height_rect), _height_rect/2, _height_rect/2);
// Draw toggle circle
p.setBrush(circleColor);
p.drawEllipse(QRectF(_x_circle - _radius, _y_circle - _radius, 2 * _radius, 2 * _radius));
}
void Toggle::mouseReleaseEvent(QMouseEvent *e) {
if(!enabled){
return;
}
const int left = _radius;
const int right = width() - _radius;
if(_x_circle != left && _x_circle != right){
//Don't parse touch events, while the animation is running
return;
}
if (e->button() & Qt::LeftButton) {
togglePosition();
emit stateChanged(on);
}
}
void Toggle::togglePosition() {
on = !on;
const int left = _radius;
const int right = width() - _radius;
_anim->setStartValue(on ? left + immediateOffset : right - immediateOffset);
_anim->setEndValue(on ? right : left);
_anim->setDuration(animation_duration);
_anim->start();
repaint();
}
void Toggle::enterEvent(QEvent *e) {
QAbstractButton::enterEvent(e);
}
bool Toggle::getEnabled(){
return enabled;
}
void Toggle::setEnabled(bool value){
enabled = value;
if(value){
circleColor.setRgb(0xfafafa);
green.setRgb(0x33ab4c);
}else{
circleColor.setRgb(0x888888);
green.setRgb(0x227722);
}
}
-42
View File
@@ -1,42 +0,0 @@
#pragma once
#include <QtWidgets>
class Toggle : public QAbstractButton {
Q_OBJECT
Q_PROPERTY(int offset_circle READ offset_circle WRITE set_offset_circle CONSTANT)
public:
Toggle(QWidget* parent = nullptr);
void togglePosition();
bool on;
int animation_duration = 150;
int immediateOffset = 0;
int offset_circle() const {
return _x_circle;
}
void set_offset_circle(int o) {
_x_circle = o;
update();
}
bool getEnabled();
void setEnabled(bool value);
protected:
void paintEvent(QPaintEvent*) override;
void mouseReleaseEvent(QMouseEvent*) override;
void enterEvent(QEvent*) override;
private:
QColor circleColor;
QColor green;
bool enabled = true;
int _x_circle, _y_circle;
int _height, _radius;
int _height_rect, _y_rect;
QPropertyAnimation *_anim = nullptr;
signals:
void stateChanged(bool new_state);
};
-97
View File
@@ -1,97 +0,0 @@
#include "window.h"
#include "selfdrive/hardware/hw.h"
MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
main_layout = new QStackedLayout;
main_layout->setMargin(0);
homeWindow = new HomeWindow(this);
main_layout->addWidget(homeWindow);
QObject::connect(homeWindow, &HomeWindow::openSettings, this, &MainWindow::openSettings);
QObject::connect(homeWindow, &HomeWindow::closeSettings, this, &MainWindow::closeSettings);
QObject::connect(&qs, &QUIState::uiUpdate, homeWindow, &HomeWindow::update);
QObject::connect(&qs, &QUIState::offroadTransition, homeWindow, &HomeWindow::offroadTransition);
QObject::connect(&qs, &QUIState::offroadTransition, homeWindow, &HomeWindow::offroadTransitionSignal);
QObject::connect(&device, &Device::displayPowerChanged, homeWindow, &HomeWindow::displayPowerChanged);
settingsWindow = new SettingsWindow(this);
main_layout->addWidget(settingsWindow);
QObject::connect(settingsWindow, &SettingsWindow::closeSettings, this, &MainWindow::closeSettings);
QObject::connect(&qs, &QUIState::offroadTransition, settingsWindow, &SettingsWindow::offroadTransition);
QObject::connect(settingsWindow, &SettingsWindow::reviewTrainingGuide, this, &MainWindow::reviewTrainingGuide);
QObject::connect(settingsWindow, &SettingsWindow::showDriverView, [=] {
homeWindow->showDriverView(true);
});
onboardingWindow = new OnboardingWindow(this);
onboardingDone = onboardingWindow->isOnboardingDone();
main_layout->addWidget(onboardingWindow);
main_layout->setCurrentWidget(onboardingWindow);
QObject::connect(onboardingWindow, &OnboardingWindow::onboardingDone, [=](){
onboardingDone = true;
closeSettings();
});
onboardingWindow->updateActiveScreen();
device.setAwake(true, true);
QObject::connect(&qs, &QUIState::uiUpdate, &device, &Device::update);
QObject::connect(&qs, &QUIState::offroadTransition, this, &MainWindow::offroadTransition);
QObject::connect(&device, &Device::displayPowerChanged, this, &MainWindow::closeSettings);
// load fonts
QFontDatabase::addApplicationFont("../assets/fonts/opensans_regular.ttf");
QFontDatabase::addApplicationFont("../assets/fonts/opensans_bold.ttf");
QFontDatabase::addApplicationFont("../assets/fonts/opensans_semibold.ttf");
// no outline to prevent the focus rectangle
setLayout(main_layout);
setStyleSheet(R"(
* {
font-family: Inter;
outline: none;
}
)");
}
void MainWindow::offroadTransition(bool offroad){
if(!offroad){
closeSettings();
}
}
void MainWindow::openSettings() {
main_layout->setCurrentWidget(settingsWindow);
}
void MainWindow::closeSettings() {
if(onboardingDone) {
main_layout->setCurrentWidget(homeWindow);
}
}
void MainWindow::reviewTrainingGuide() {
onboardingDone = false;
main_layout->setCurrentWidget(onboardingWindow);
onboardingWindow->updateActiveScreen();
}
bool MainWindow::eventFilter(QObject *obj, QEvent *event){
// wake screen on tap
if (event->type() == QEvent::MouseButtonPress) {
device.setAwake(true, true);
}
#ifdef QCOM
// filter out touches while in android activity
const static QSet<QEvent::Type> filter_events({QEvent::MouseButtonPress, QEvent::MouseMove, QEvent::TouchBegin, QEvent::TouchUpdate, QEvent::TouchEnd});
if (HardwareEon::launched_activity && filter_events.contains(event->type())) {
HardwareEon::check_activity();
if (HardwareEon::launched_activity) {
return true;
}
}
#endif
return false;
}
-35
View File
@@ -1,35 +0,0 @@
#pragma once
#include <QStackedLayout>
#include <QWidget>
#include "selfdrive/ui/qt/home.h"
#include "selfdrive/ui/qt/offroad/onboarding.h"
#include "selfdrive/ui/qt/offroad/settings.h"
#include "selfdrive/ui/ui.h"
class MainWindow : public QWidget {
Q_OBJECT
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
public:
explicit MainWindow(QWidget *parent = 0);
private:
Device device;
QUIState qs;
QStackedLayout *main_layout;
HomeWindow *homeWindow;
SettingsWindow *settingsWindow;
OnboardingWindow *onboardingWindow;
bool onboardingDone = false;
public slots:
void offroadTransition(bool offroad);
void openSettings();
void closeSettings();
void reviewTrainingGuide();
};
+2
View File
@@ -4,6 +4,8 @@ if [ -f /EON ] && [ ! -f qt/spinner ]; then
cp qt/spinner_aarch64 qt/spinner
elif [ -f /TICI ] && [ ! -f qt/spinner ]; then
cp qt/spinner_larch64 qt/spinner
elif [ -f /JETSON ] && [ ! -f qt/spinner ]; then
cp qt/spinner_larch64 qt/spinner
fi
export LD_LIBRARY_PATH="/system/lib64:$LD_LIBRARY_PATH"
+2
View File
@@ -4,6 +4,8 @@ if [ -f /EON ] && [ ! -f qt/text ]; then
cp qt/text_aarch64 qt/text
elif [ -f /TICI ] && [ ! -f qt/text ]; then
cp qt/text_larch64 qt/text
elif [ -f /JETSON ] && [ ! -f qt/text ]; then
cp qt/text_larch64 qt/text
fi
export LD_LIBRARY_PATH="/system/lib64:$LD_LIBRARY_PATH"
-382
View File
@@ -1,382 +0,0 @@
#include "selfdrive/ui/ui.h"
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <cmath>
#include "selfdrive/common/swaglog.h"
#include "selfdrive/common/util.h"
#include "selfdrive/common/visionimg.h"
#include "selfdrive/common/watchdog.h"
#include "selfdrive/hardware/hw.h"
#include "selfdrive/ui/paint.h"
#include "selfdrive/ui/qt/qt_window.h"
#define BACKLIGHT_DT 0.25
#define BACKLIGHT_TS 2.00
#define BACKLIGHT_OFFROAD 75
// Projects a point in car to space to the corresponding point in full frame
// image space.
static bool calib_frame_to_full_frame(const UIState *s, float in_x, float in_y, float in_z, vertex_data *out) {
const float margin = 500.0f;
const vec3 pt = (vec3){{in_x, in_y, in_z}};
const vec3 Ep = matvecmul3(s->scene.view_from_calib, pt);
const vec3 KEp = matvecmul3(s->wide_camera ? ecam_intrinsic_matrix : fcam_intrinsic_matrix, Ep);
// Project.
float x = KEp.v[0] / KEp.v[2];
float y = KEp.v[1] / KEp.v[2];
nvgTransformPoint(&out->x, &out->y, s->car_space_transform, x, y);
return out->x >= -margin && out->x <= s->fb_w + margin && out->y >= -margin && out->y <= s->fb_h + margin;
}
static void ui_init_vision(UIState *s) {
// Invisible until we receive a calibration message.
s->scene.world_objects_visible = false;
for (int i = 0; i < s->vipc_client->num_buffers; i++) {
s->texture[i].reset(new EGLImageTexture(&s->vipc_client->buffers[i]));
glBindTexture(GL_TEXTURE_2D, s->texture[i]->frame_tex);
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);
}
static int get_path_length_idx(const cereal::ModelDataV2::XYZTData::Reader &line, const float path_height) {
const auto line_x = line.getX();
int max_idx = 0;
for (int i = 0; i < TRAJECTORY_SIZE && line_x[i] < path_height; ++i) {
max_idx = i;
}
return max_idx;
}
static void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, std::optional<cereal::ModelDataV2::XYZTData::Reader> line) {
for (int i = 0; i < 2; ++i) {
auto lead_data = (i == 0) ? radar_state.getLeadOne() : radar_state.getLeadTwo();
if (lead_data.getStatus()) {
float z = line ? (*line).getZ()[get_path_length_idx(*line, lead_data.getDRel())] : 0.0;
// negative because radarState uses left positive convention
calib_frame_to_full_frame(s, lead_data.getDRel(), -lead_data.getYRel(), z + 1.22, &s->scene.lead_vertices[i]);
}
}
}
static void update_line_data(const UIState *s, const cereal::ModelDataV2::XYZTData::Reader &line,
float y_off, float z_off, line_vertices_data *pvd, int max_idx) {
const auto line_x = line.getX(), line_y = line.getY(), line_z = line.getZ();
vertex_data *v = &pvd->v[0];
for (int i = 0; i <= max_idx; i++) {
v += calib_frame_to_full_frame(s, line_x[i], line_y[i] - y_off, line_z[i] + z_off, v);
}
for (int i = max_idx; i >= 0; i--) {
v += calib_frame_to_full_frame(s, line_x[i], line_y[i] + y_off, line_z[i] + z_off, v);
}
pvd->cnt = v - pvd->v;
assert(pvd->cnt < std::size(pvd->v));
}
static void update_model(UIState *s, const cereal::ModelDataV2::Reader &model) {
UIScene &scene = s->scene;
auto model_position = model.getPosition();
float max_distance = std::clamp(model_position.getX()[TRAJECTORY_SIZE - 1],
MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE);
// update lane lines
const auto lane_lines = model.getLaneLines();
const auto lane_line_probs = model.getLaneLineProbs();
int max_idx = get_path_length_idx(lane_lines[0], max_distance);
for (int i = 0; i < std::size(scene.lane_line_vertices); i++) {
scene.lane_line_probs[i] = lane_line_probs[i];
update_line_data(s, lane_lines[i], 0.025 * scene.lane_line_probs[i], 0, &scene.lane_line_vertices[i], max_idx);
}
// update road edges
const auto road_edges = model.getRoadEdges();
const auto road_edge_stds = model.getRoadEdgeStds();
for (int i = 0; i < std::size(scene.road_edge_vertices); i++) {
scene.road_edge_stds[i] = road_edge_stds[i];
update_line_data(s, road_edges[i], 0.025, 0, &scene.road_edge_vertices[i], max_idx);
}
// update path
auto lead_one = (*s->sm)["radarState"].getRadarState().getLeadOne();
if (lead_one.getStatus()) {
const float lead_d = lead_one.getDRel() * 2.;
max_distance = std::clamp((float)(lead_d - fmin(lead_d * 0.35, 10.)), 0.0f, max_distance);
}
max_idx = get_path_length_idx(model_position, max_distance);
update_line_data(s, model_position, 0.5, 1.22, &scene.track_vertices, max_idx);
}
static void update_sockets(UIState *s){
s->sm->update(0);
}
static void update_state(UIState *s) {
SubMaster &sm = *(s->sm);
UIScene &scene = s->scene;
// update engageability and DM icons at 2Hz
if (sm.frame % (UI_FREQ / 2) == 0) {
scene.engageable = sm["controlsState"].getControlsState().getEngageable();
scene.dm_active = sm["driverMonitoringState"].getDriverMonitoringState().getIsActiveMode();
}
if (sm.updated("radarState")) {
std::optional<cereal::ModelDataV2::XYZTData::Reader> line;
if (sm.rcv_frame("modelV2") > 0) {
line = sm["modelV2"].getModelV2().getPosition();
}
update_leads(s, sm["radarState"].getRadarState(), line);
}
if (sm.updated("liveCalibration")) {
scene.world_objects_visible = true;
auto rpy_list = sm["liveCalibration"].getLiveCalibration().getRpyCalib();
Eigen::Vector3d rpy;
rpy << rpy_list[0], rpy_list[1], rpy_list[2];
Eigen::Matrix3d device_from_calib = euler2rot(rpy);
Eigen::Matrix3d view_from_device;
view_from_device << 0,1,0,
0,0,1,
1,0,0;
Eigen::Matrix3d view_from_calib = view_from_device * device_from_calib;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
scene.view_from_calib.v[i*3 + j] = view_from_calib(i,j);
}
}
}
if (sm.updated("modelV2")) {
update_model(s, sm["modelV2"].getModelV2());
}
if (sm.updated("pandaState")) {
auto pandaState = sm["pandaState"].getPandaState();
scene.pandaType = pandaState.getPandaType();
scene.ignition = pandaState.getIgnitionLine() || pandaState.getIgnitionCan();
} else if ((s->sm->frame - s->sm->rcv_frame("pandaState")) > 5*UI_FREQ) {
scene.pandaType = cereal::PandaState::PandaType::UNKNOWN;
}
if (sm.updated("ubloxGnss")) {
auto data = sm["ubloxGnss"].getUbloxGnss();
if (data.which() == cereal::UbloxGnss::MEASUREMENT_REPORT) {
scene.satelliteCount = data.getMeasurementReport().getNumMeas();
}
}
if (sm.updated("gpsLocationExternal")) {
scene.gpsAccuracy = sm["gpsLocationExternal"].getGpsLocationExternal().getAccuracy();
}
if (sm.updated("carParams")) {
scene.longitudinal_control = sm["carParams"].getCarParams().getOpenpilotLongitudinalControl();
}
if (sm.updated("sensorEvents")) {
for (auto sensor : sm["sensorEvents"].getSensorEvents()) {
if (!scene.started && sensor.which() == cereal::SensorEventData::ACCELERATION) {
auto accel = sensor.getAcceleration().getV();
if (accel.totalSize().wordCount){ // TODO: sometimes empty lists are received. Figure out why
scene.accel_sensor = accel[2];
}
} else if (!scene.started && sensor.which() == cereal::SensorEventData::GYRO_UNCALIBRATED) {
auto gyro = sensor.getGyroUncalibrated().getV();
if (gyro.totalSize().wordCount){
scene.gyro_sensor = gyro[1];
}
}
}
}
if (sm.updated("roadCameraState")) {
auto camera_state = sm["roadCameraState"].getRoadCameraState();
float max_lines = Hardware::EON() ? 5408 : 1757;
float gain = camera_state.getGainFrac();
if (Hardware::TICI()) {
// gainFrac can go up to 4, with another 2.5x multiplier based on globalGain. Scale back to 0 - 1
gain *= (camera_state.getGlobalGain() > 100 ? 2.5 : 1.0) / 10.0;
}
scene.light_sensor = std::clamp<float>((1023.0 / max_lines) * (max_lines - camera_state.getIntegLines() * gain), 0.0, 1023.0);
}
scene.started = sm["deviceState"].getDeviceState().getStarted();
}
static void update_params(UIState *s) {
const uint64_t frame = s->sm->frame;
UIScene &scene = s->scene;
if (frame % (5*UI_FREQ) == 0) {
scene.is_metric = Params().getBool("IsMetric");
}
}
static void update_vision(UIState *s) {
if (!s->vipc_client->connected && s->scene.started) {
if (s->vipc_client->connect(false)){
ui_init_vision(s);
}
}
if (s->vipc_client->connected){
VisionBuf * buf = s->vipc_client->recv();
if (buf != nullptr){
s->last_frame = buf;
} else if (!Hardware::PC()) {
LOGE("visionIPC receive timeout");
}
}
}
static void update_status(UIState *s) {
if (s->scene.started && s->sm->updated("controlsState")) {
auto controls_state = (*s->sm)["controlsState"].getControlsState();
auto alert_status = controls_state.getAlertStatus();
if (alert_status == cereal::ControlsState::AlertStatus::USER_PROMPT) {
s->status = STATUS_WARNING;
} else if (alert_status == cereal::ControlsState::AlertStatus::CRITICAL) {
s->status = STATUS_ALERT;
} else {
s->status = controls_state.getEnabled() ? STATUS_ENGAGED : STATUS_DISENGAGED;
}
}
// Handle onroad/offroad transition
static bool started_prev = false;
if (s->scene.started != started_prev) {
if (s->scene.started) {
s->status = STATUS_DISENGAGED;
s->scene.started_frame = s->sm->frame;
s->scene.end_to_end = Params().getBool("EndToEndToggle");
s->wide_camera = Hardware::TICI() ? Params().getBool("EnableWideCamera") : false;
// Update intrinsics matrix after possible wide camera toggle change
if (s->vg) {
ui_resize(s, s->fb_w, s->fb_h);
}
// Choose vision ipc client
if (s->wide_camera){
s->vipc_client = s->vipc_client_wide;
} else {
s->vipc_client = s->vipc_client_rear;
}
} else {
s->vipc_client->connected = false;
}
}
started_prev = s->scene.started;
}
QUIState::QUIState(QObject *parent) : QObject(parent) {
ui_state.sm = std::make_unique<SubMaster, const std::initializer_list<const char *>>({
"modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "liveLocationKalman",
"pandaState", "carParams", "driverMonitoringState", "sensorEvents", "carState", "ubloxGnss",
"gpsLocationExternal", "roadCameraState",
});
ui_state.fb_w = vwp_w;
ui_state.fb_h = vwp_h;
ui_state.scene.started = false;
ui_state.last_frame = nullptr;
ui_state.wide_camera = Hardware::TICI() ? Params().getBool("EnableWideCamera") : false;
ui_state.vipc_client_rear = new VisionIpcClient("camerad", VISION_STREAM_RGB_BACK, true);
ui_state.vipc_client_wide = new VisionIpcClient("camerad", VISION_STREAM_RGB_WIDE, true);
ui_state.vipc_client = ui_state.vipc_client_rear;
// update timer
timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, this, &QUIState::update);
timer->start(0);
}
void QUIState::update() {
update_params(&ui_state);
update_sockets(&ui_state);
update_state(&ui_state);
update_status(&ui_state);
update_vision(&ui_state);
if (ui_state.scene.started != started_prev || ui_state.sm->frame == 1) {
started_prev = ui_state.scene.started;
emit offroadTransition(!ui_state.scene.started);
// Change timeout to 0 when onroad, this will call update continously.
// This puts visionIPC in charge of update frequency, reducing video latency
timer->start(ui_state.scene.started ? 0 : 1000 / UI_FREQ);
}
watchdog_kick();
emit uiUpdate(ui_state);
}
Device::Device(QObject *parent) : brightness_filter(BACKLIGHT_OFFROAD, BACKLIGHT_TS, BACKLIGHT_DT), QObject(parent) {
}
void Device::update(const UIState &s) {
updateBrightness(s);
updateWakefulness(s);
// TODO: remove from UIState and use signals
QUIState::ui_state.awake = awake;
}
void Device::setAwake(bool on, bool reset) {
if (on != awake) {
awake = on;
Hardware::set_display_power(awake);
LOGD("setting display power %d", awake);
emit displayPowerChanged(awake);
}
if (reset) {
awake_timeout = 30 * UI_FREQ;
}
}
void Device::updateBrightness(const UIState &s) {
float brightness_b = 10;
float brightness_m = 0.1;
float clipped_brightness = std::min(100.0f, (s.scene.light_sensor * brightness_m) + brightness_b);
if (!s.scene.started) {
clipped_brightness = BACKLIGHT_OFFROAD;
}
int brightness = brightness_filter.update(clipped_brightness);
if (!awake) {
brightness = 0;
}
if (brightness != last_brightness) {
std::thread{Hardware::set_brightness, brightness}.detach();
}
last_brightness = brightness;
}
void Device::updateWakefulness(const UIState &s) {
awake_timeout = std::max(awake_timeout - 1, 0);
bool should_wake = s.scene.started || s.scene.ignition;
if (!should_wake) {
// tap detection while display is off
bool accel_trigger = abs(s.scene.accel_sensor - accel_prev) > 0.2;
bool gyro_trigger = abs(s.scene.gyro_sensor - gyro_prev) > 0.15;
should_wake = accel_trigger && gyro_trigger;
gyro_prev = s.scene.gyro_sensor;
accel_prev = (accel_prev * (accel_samples - 1) + s.scene.accel_sensor) / accel_samples;
}
setAwake(awake_timeout, should_wake);
}
-195
View File
@@ -1,195 +0,0 @@
#pragma once
#include <atomic>
#include <map>
#include <memory>
#include <string>
#include <QObject>
#include <QTimer>
#include <QColor>
#include "nanovg.h"
#include "cereal/messaging/messaging.h"
#include "cereal/visionipc/visionipc.h"
#include "cereal/visionipc/visionipc_client.h"
#include "common/transformations/orientation.hpp"
#include "selfdrive/camerad/cameras/camera_common.h"
#include "selfdrive/common/glutil.h"
#include "selfdrive/common/mat.h"
#include "selfdrive/common/modeldata.h"
#include "selfdrive/common/params.h"
#include "selfdrive/common/util.h"
#include "selfdrive/common/visionimg.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)
// TODO: this is also hardcoded in common/transformations/camera.py
// TODO: choose based on frame input size
const float y_offset = Hardware::TICI() ? 150.0 : 0.0;
const float zoom = Hardware::TICI() ? 2912.8 : 2138.5;
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;
const int bdr_s = 30;
const int header_h = 420;
const int footer_h = 280;
const int UI_FREQ = 20; // Hz
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;
// gps
int satelliteCount;
float gpsAccuracy;
// 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 {
VisionIpcClient * vipc_client;
VisionIpcClient * vipc_client_rear;
VisionIpcClient * vipc_client_wide;
VisionBuf * last_frame;
// framebuffer
int fb_w, fb_h;
// NVG
NVGcontext *vg;
// images
std::map<std::string, int> images;
std::unique_ptr<SubMaster> sm;
UIStatus status;
UIScene scene;
// graphics
std::unique_ptr<GLShader> gl_shader;
std::unique_ptr<EGLImageTexture> texture[UI_BUF_COUNT];
GLuint frame_vao, frame_vbo, frame_ibo;
mat4 rear_frame_mat;
bool awake;
Rect video_rect, viz_rect;
float car_space_transform[6];
bool wide_camera;
float zoom;
} 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;
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);
};