From 1e378d1093a1db780321a9edff5dae8a18f76000 Mon Sep 17 00:00:00 2001
From: Panagiotis Tomer Karagiannis
Date: Sat, 9 Jul 2022 11:50:10 +0200
Subject: [PATCH] working demo driver view
---
CMakeLists.txt | 7 ++-
include/App.h | 2 +
include/AppState.h | 4 ++
include/BaseDataSource.h | 34 +++++++++++
include/DemoDataSource.h | 13 ++++
include/DriverView.h | 30 ++++++++++
include/widgets.h | 3 +-
src/App.cpp | 82 +++++++++++++++-----------
src/AppState.cpp | 5 +-
src/BaseDataSource.cpp | 35 +++++++++++
src/DemoDataSource.cpp | 33 +++++++++++
src/DriverView.cpp | 124 +++++++++++++++++++++++++++++++++++++++
src/MissionSelect.cpp | 1 +
src/widgets.cpp | 11 +++-
14 files changed, 345 insertions(+), 39 deletions(-)
create mode 100644 include/BaseDataSource.h
create mode 100644 include/DemoDataSource.h
create mode 100644 include/DriverView.h
create mode 100644 src/BaseDataSource.cpp
create mode 100644 src/DemoDataSource.cpp
create mode 100644 src/DriverView.cpp
diff --git a/CMakeLists.txt b/CMakeLists.txt
index abd3cc7..fe5adea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.18)
+cmake_minimum_required(VERSION 3.16)
project("FT22 Steering Wheel Display")
@@ -60,6 +60,9 @@ add_executable(
src/AMI.cpp
src/widgets.cpp
src/util.cpp
+ src/DriverView.cpp
+ src/BaseDataSource.cpp
+ src/DemoDataSource.cpp
)
target_include_directories(
stw-display PUBLIC
@@ -78,4 +81,4 @@ target_link_libraries(
${I2C_LIBRARY}
fmt
spdlog::spdlog)
-add_dependencies(stw-display fmt)
\ No newline at end of file
+add_dependencies(stw-display fmt)
diff --git a/include/App.h b/include/App.h
index 685cf8f..c88430e 100644
--- a/include/App.h
+++ b/include/App.h
@@ -2,6 +2,7 @@
#include "AMI.h"
#include "AppState.h"
+#include "DriverView.h"
#include "MissionSelect.h"
#include "defines.h"
@@ -44,6 +45,7 @@ private:
std::unique_ptr state;
std::unique_ptr mission_select;
std::unique_ptr ami;
+ std::unique_ptr driver_view;
bool running;
diff --git a/include/AppState.h b/include/AppState.h
index 9bbb8ac..763b424 100644
--- a/include/AppState.h
+++ b/include/AppState.h
@@ -1,5 +1,7 @@
#pragma once
+#include "DemoDataSource.h"
+
#include
#include
@@ -32,6 +34,7 @@ public:
AppView get_view() const;
Mission get_mission() const;
+ DemoDataSource get_data_source() const;
private:
void unmarshal_mission_select(uint8_t* data);
@@ -42,6 +45,7 @@ private:
AppView view;
Mission mission;
+ DemoDataSource data_source;
#ifndef NDEBUG
friend class KeyboardHandler;
diff --git a/include/BaseDataSource.h b/include/BaseDataSource.h
new file mode 100644
index 0000000..ed62533
--- /dev/null
+++ b/include/BaseDataSource.h
@@ -0,0 +1,34 @@
+#pragma once
+
+class BaseDataSource {
+public:
+ double get_speed(); // in km/h
+ double get_brake_balance(); // 0.0 - 1.0
+ double get_hv_voltage(); // in V
+ double get_hv_voltage_ratio(); // 0.0 - 1.0
+ double get_lv_voltage(); // in V
+ double get_battery_temperature(); // in °C
+ double get_throttle_ratio(); // 0.0 - 1.0
+ double get_brake_pressure_front_bar(); // in bar
+ double get_brake_pressure_rear_bar(); // in bar
+
+ bool brake_balance_change_is_recent(); // true if the last change was less
+ // than 1 second ago
+
+ virtual void poll() = 0;
+
+protected:
+ void set_brake_balance(double value);
+
+ double speed;
+ double brake_balance;
+ double hv_voltage;
+ double hv_voltage_ratio;
+ double lv_voltage;
+ double battery_temperature;
+ double throttle_ratio;
+ double brake_pressure_front_bar;
+ double brake_pressure_rear_bar;
+
+ int time_of_last_brake_balance_change;
+};
diff --git a/include/DemoDataSource.h b/include/DemoDataSource.h
new file mode 100644
index 0000000..5ede51a
--- /dev/null
+++ b/include/DemoDataSource.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "BaseDataSource.h"
+
+class DemoDataSource final : public BaseDataSource {
+public:
+ DemoDataSource();
+ ~DemoDataSource();
+
+ void poll() override;
+ void bump_brake_balance_up();
+ void bump_brake_balance_down();
+};
diff --git a/include/DriverView.h b/include/DriverView.h
new file mode 100644
index 0000000..595149e
--- /dev/null
+++ b/include/DriverView.h
@@ -0,0 +1,30 @@
+#pragma once
+#include "View.h"
+#include "defines.h"
+#include "util.h"
+#include "widgets.h"
+
+class DriverView final : public View {
+public:
+ DriverView(SDL_Renderer* renderer);
+ ~DriverView();
+
+ void draw(const AppState& state) override;
+
+private:
+ TTF_Font* font_large;
+ TTF_Font* font_detail;
+ TTF_Font* font_tiny;
+ TTF_Font* font_giant;
+ TTF_Font* font_medium;
+ std::unique_ptr speed_widget;
+ std::unique_ptr speed_hint_widget;
+
+ std::unique_ptr brake_balance_widget;
+ std::unique_ptr brake_balance_hint_widget;
+
+ std::unique_ptr general_info_widget;
+
+ std::unique_ptr focus_widget;
+ std::unique_ptr focus_hint_widget;
+};
\ No newline at end of file
diff --git a/include/widgets.h b/include/widgets.h
index cd48cf2..2186b10 100644
--- a/include/widgets.h
+++ b/include/widgets.h
@@ -79,12 +79,13 @@ public:
~TextWidget();
void update_text(const std::string& new_text);
+ void set_wrap_width(int width);
protected:
TTF_Font* font;
std::string text;
-
+ int wrap_width = -1; // -1 means no wrapping (default behavior)
SDL_Texture* generate_text(const std::string& text);
};
diff --git a/src/App.cpp b/src/App.cpp
index 0c8678d..44bba77 100644
--- a/src/App.cpp
+++ b/src/App.cpp
@@ -2,6 +2,8 @@
#include "AMI.h"
#include "AppState.h"
+#include "DemoDataSource.h"
+#include "DriverView.h"
#include "MissionSelect.h"
#include "events.h"
@@ -15,6 +17,7 @@
#include
#include
+#include
#include
#include
#include
@@ -60,6 +63,11 @@ void App::init_state() {
int App::run() {
mission_select = std::make_unique(renderer);
ami = std::make_unique(renderer);
+ driver_view = std::make_unique(renderer);
+
+#ifndef NDEBUG
+ std::cout << "Change brake balance with W (+) and S (-)" << std::endl;
+#endif
running = true;
@@ -110,6 +118,9 @@ void App::render() {
case AppView::AMI:
ami->draw(*state);
break;
+ case AppView::DRIVER:
+ driver_view->draw(*state);
+ break;
default:
throw std::runtime_error(fmt::format("Unknown view: {}", view));
}
@@ -138,39 +149,44 @@ SDLManager::~SDLManager() {
SDL_Quit();
}
+/*
+Handle key presses
+*/
void KeyboardHandler::handle_keyup(const SDL_Event& ev, AppState& state) {
- switch (ev.key.keysym.sym) {
- case SDLK_DOWN:
- case SDLK_RIGHT:
- if (state.mission == Mission::MANUAL) {
- state.mission = Mission::ACCELERATION;
- } else {
- state.mission = static_cast(static_cast(state.mission) + 1);
+ auto pressed_key = ev.key.keysym.sym;
+ if (state.view == AppView::MISSION_SELECT) {
+ if (pressed_key == SDLK_DOWN || pressed_key == SDLK_RIGHT) {
+ auto new_mission = static_cast(state.mission) + 1;
+ if (new_mission > 7) {
+ new_mission = 1;
+ }
+ std::cout << "New mission: " << new_mission << std::endl;
+ state.mission = static_cast(new_mission);
+ } else if (pressed_key == SDLK_UP || pressed_key == SDLK_LEFT) {
+ auto new_mission = static_cast(state.mission) - 1;
+ if (new_mission < 1) {
+ new_mission = 7;
+ }
+ state.mission = static_cast(new_mission);
+ } else if (pressed_key == SDLK_RETURN) {
+
+ if (state.mission == Mission::MANUAL) {
+ state.view = AppView::DRIVER;
+ } else {
+ state.view = AppView::AMI;
+ }
}
- break;
- case SDLK_UP:
- case SDLK_LEFT:
- if (state.mission == Mission::NONE ||
- state.mission == Mission::ACCELERATION) {
- state.mission = Mission::MANUAL;
- } else {
- state.mission = static_cast(static_cast(state.mission) - 1);
- }
- break;
- case SDLK_RETURN:
- case SDLK_KP_ENTER:
- switch (state.view) {
- case AppView::MISSION_SELECT:
- state.view = AppView::AMI;
- break;
- case AppView::AMI:
- case AppView::DRIVER:
- case AppView::TESTING:
- break;
- default:
- throw std::runtime_error(fmt::format("Unknown view: {}", state.view));
- }
- break;
- // We can just ignore other keypresses, so no need for a default clause
}
-}
\ No newline at end of file
+
+ if (state.view == AppView::AMI || state.view == AppView::DRIVER) {
+ if (pressed_key == SDLK_ESCAPE) {
+ state.view = AppView::MISSION_SELECT;
+ }
+ }
+
+ if (pressed_key == SDLK_w) {
+ state.data_source.bump_brake_balance_up();
+ } else if (pressed_key == SDLK_s) {
+ state.data_source.bump_brake_balance_down();
+ }
+}
diff --git a/src/AppState.cpp b/src/AppState.cpp
index ab8a611..ce37386 100644
--- a/src/AppState.cpp
+++ b/src/AppState.cpp
@@ -76,6 +76,7 @@ AppState::~AppState() {
}
void AppState::poll() {
+ data_source.poll();
if (!i2c_dev_file) {
return;
}
@@ -112,4 +113,6 @@ void AppState::unmarshal_mission_select(uint8_t* data) {
void AppState::unmarshal_ami(uint8_t* data) {
mission = static_cast(data[1]);
spdlog::info("Mission after poll: {}", mission);
-}
\ No newline at end of file
+}
+
+DemoDataSource AppState::get_data_source() const { return data_source; }
\ No newline at end of file
diff --git a/src/BaseDataSource.cpp b/src/BaseDataSource.cpp
new file mode 100644
index 0000000..c2327a1
--- /dev/null
+++ b/src/BaseDataSource.cpp
@@ -0,0 +1,35 @@
+#include "BaseDataSource.h"
+
+#include
+double BaseDataSource::get_speed() { return speed; }
+
+double BaseDataSource::get_brake_balance() { return brake_balance; }
+
+double BaseDataSource::get_hv_voltage() { return hv_voltage; }
+
+double BaseDataSource::get_hv_voltage_ratio() { return hv_voltage_ratio; }
+
+double BaseDataSource::get_lv_voltage() { return lv_voltage; }
+
+double BaseDataSource::get_battery_temperature() { return battery_temperature; }
+
+double BaseDataSource::get_throttle_ratio() { return throttle_ratio; }
+
+double BaseDataSource::get_brake_pressure_front_bar() {
+ return brake_pressure_front_bar;
+}
+
+double BaseDataSource::get_brake_pressure_rear_bar() {
+ return brake_pressure_rear_bar;
+}
+
+void BaseDataSource::set_brake_balance(double value) {
+ brake_balance = value;
+ time_of_last_brake_balance_change = SDL_GetTicks();
+}
+
+bool BaseDataSource::brake_balance_change_is_recent() {
+
+ return time_of_last_brake_balance_change != 0 &&
+ SDL_GetTicks() - time_of_last_brake_balance_change < 500;
+}
diff --git a/src/DemoDataSource.cpp b/src/DemoDataSource.cpp
new file mode 100644
index 0000000..206f4d0
--- /dev/null
+++ b/src/DemoDataSource.cpp
@@ -0,0 +1,33 @@
+#include "DemoDataSource.h"
+
+#include
+#include
+DemoDataSource::DemoDataSource() {
+ brake_balance = 0.5;
+
+ lv_voltage = 13.1;
+ battery_temperature = 20.1;
+ throttle_ratio = 0.01;
+ brake_pressure_front_bar = 1;
+ brake_pressure_rear_bar = 1;
+}
+
+DemoDataSource::~DemoDataSource() {}
+
+void DemoDataSource::poll() {
+ int min_voltage = 150;
+ int max_voltage = 480;
+ static int count = 0;
+ count++;
+ hv_voltage = 220.1 + std::cos(count / 1000.0 + 2) * 50;
+ hv_voltage_ratio = (hv_voltage - min_voltage) / (max_voltage - min_voltage);
+ speed = 10 + std::sin(count / 100.0) * 5;
+}
+
+void DemoDataSource::bump_brake_balance_up() {
+ set_brake_balance(brake_balance + 0.01);
+}
+
+void DemoDataSource::bump_brake_balance_down() {
+ set_brake_balance(brake_balance - 0.01);
+}
\ No newline at end of file
diff --git a/src/DriverView.cpp b/src/DriverView.cpp
new file mode 100644
index 0000000..327a0c3
--- /dev/null
+++ b/src/DriverView.cpp
@@ -0,0 +1,124 @@
+#include "DriverView.h"
+
+#include "DemoDataSource.h"
+#include "defines.h"
+
+#include
+
+#include
+
+#define LARGE_FONT_SIZE_POINTS 170
+#define GIANT_FONT_SIZE_POINTS 200
+#define MEDIUM_FONT_SIZE_POINTS 50
+#define SOC_BAR_HEIGHT 80
+#define SPEED_TEXT_X 22
+#define SPEED_TEXT_Y 50
+
+DriverView::DriverView(SDL_Renderer* renderer)
+ : View{renderer}, font_large{util::load_font("resources/Avenir-Book.ttf",
+ LARGE_FONT_SIZE_POINTS)},
+ font_detail{util::load_font("resources/Avenir-Book.ttf", 20)},
+ font_tiny{util::load_font("resources/Avenir-Book.ttf", 15)},
+ font_giant{
+ util::load_font("resources/Avenir-Book.ttf", GIANT_FONT_SIZE_POINTS)},
+ font_medium{util::load_font("resources/Avenir-Book.ttf",
+ MEDIUM_FONT_SIZE_POINTS)} {
+ speed_widget = std::make_unique(renderer, font_large, "");
+ speed_widget->set_position(SPEED_TEXT_X, SPEED_TEXT_Y);
+ speed_widget->set_alignment(Alignment::CENTER, Alignment::TOP);
+
+ speed_hint_widget = std::make_unique(renderer, font_tiny, "SPD");
+ speed_hint_widget->set_position(20, 210);
+ speed_hint_widget->set_alignment(Alignment::CENTER, Alignment::TOP);
+
+ brake_balance_widget = std::make_unique(renderer, font_large, "");
+ brake_balance_widget->set_position(SPEED_TEXT_X + SCREEN_WIDTH / 2,
+ SPEED_TEXT_Y);
+ brake_balance_widget->set_alignment(Alignment::CENTER, Alignment::TOP);
+ brake_balance_hint_widget =
+ std::make_unique(renderer, font_tiny, "BB");
+ brake_balance_hint_widget->set_position(20 + SCREEN_WIDTH / 2, 210);
+ brake_balance_hint_widget->set_alignment(Alignment::CENTER, Alignment::TOP);
+
+ general_info_widget = std::make_unique(renderer, font_detail, "");
+ general_info_widget->set_position(SPEED_TEXT_X, SPEED_TEXT_Y + 200);
+ general_info_widget->set_alignment(Alignment::LEFT, Alignment::TOP);
+ general_info_widget->set_wrap_width(SCREEN_WIDTH - 50);
+
+ focus_widget = std::make_unique(renderer, font_giant, "");
+ focus_widget->set_position(120, 70);
+ focus_widget->set_alignment(Alignment::LEFT, Alignment::TOP);
+ focus_hint_widget = std::make_unique(renderer, font_medium, "");
+ focus_hint_widget->set_position(120, 40);
+ // focus_hint_widget->set_alignment(Alignment::CENTER, Alignment::CENTER);
+}
+
+DriverView::~DriverView() {}
+
+void DriverView::draw(const AppState& state) {
+ auto ds = state.get_data_source();
+ auto hv_ratio = ds.get_hv_voltage_ratio();
+ SDL_Rect rect;
+ rect.x = 0;
+ rect.y = 0;
+ rect.w = SCREEN_WIDTH;
+ rect.h = SCREEN_HEIGHT;
+
+ static auto hv_low = false;
+ // hysteresis for the hv voltage low
+ if (hv_ratio < 0.15) {
+ hv_low = true;
+ } else if (hv_ratio > 0.2) {
+ hv_low = false;
+ }
+
+ int red = hv_low ? 0xa0 : 0x30;
+ SDL_SetRenderDrawColor(renderer, red, 0x30, 0x30, 255);
+ SDL_RenderFillRect(renderer, &rect);
+
+ auto brake_balance_text = fmt::format("{:.0f}", ds.get_brake_balance() * 100);
+
+ if (ds.brake_balance_change_is_recent()) {
+ SDL_SetRenderDrawColor(renderer, 255, 69, 100, 255);
+ SDL_RenderFillRect(renderer, &rect);
+ focus_widget->update_text(brake_balance_text);
+ focus_widget->draw();
+ focus_hint_widget->update_text("BBAL");
+ focus_hint_widget->draw();
+ return;
+ }
+
+ rect.w = SCREEN_WIDTH * hv_ratio;
+ rect.h = SOC_BAR_HEIGHT;
+ SDL_SetRenderDrawColor(renderer, 76, 255, 0, 255);
+ SDL_RenderFillRect(renderer, &rect);
+
+ speed_widget->update_text(fmt::format("{:02.0f}", ds.get_speed()));
+ speed_widget->draw();
+ speed_hint_widget->draw();
+
+ brake_balance_widget->update_text(brake_balance_text);
+
+ brake_balance_widget->draw();
+ brake_balance_hint_widget->draw();
+
+ // draw rectangles around the speed and brake balance widgets
+ SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 255);
+ rect.x = 0;
+ rect.y = SOC_BAR_HEIGHT;
+ rect.w = SCREEN_WIDTH / 2;
+ rect.h = 150;
+ SDL_RenderDrawRect(renderer, &rect);
+ rect.x = SCREEN_WIDTH / 2;
+ SDL_RenderDrawRect(renderer, &rect);
+
+ auto general_string = fmt::format(
+ "HV: {:.0f}V | LV: {:.1f}V | B_TEMP: {:.1f}C\nAPPS: {:.0f}% | BPF: "
+ "{:.0f} bar | BPR: {:.0f} bar",
+ ds.get_hv_voltage(), ds.get_lv_voltage(), ds.get_battery_temperature(),
+ ds.get_throttle_ratio() * 100, ds.get_brake_pressure_front_bar(),
+ ds.get_brake_pressure_rear_bar());
+
+ general_info_widget->update_text(general_string);
+ general_info_widget->draw();
+}
\ No newline at end of file
diff --git a/src/MissionSelect.cpp b/src/MissionSelect.cpp
index 7e1601e..b5dc204 100644
--- a/src/MissionSelect.cpp
+++ b/src/MissionSelect.cpp
@@ -1,6 +1,7 @@
#include "MissionSelect.h"
#include "AppState.h"
+#include "DriverView.h"
#include "defines.h"
#include "events.h"
#include "util.h"
diff --git a/src/widgets.cpp b/src/widgets.cpp
index 21418d0..2523cdc 100644
--- a/src/widgets.cpp
+++ b/src/widgets.cpp
@@ -167,8 +167,13 @@ void TextWidget::update_text(const std::string& new_text) {
}
SDL_Texture* TextWidget::generate_text(const std::string& text) {
- SDL_Surface* surf =
- TTF_RenderText_Solid(font, text.c_str(), {0xFF, 0xFF, 0XFF, 0xFF});
+ SDL_Surface* surf;
+ if (wrap_width == -1) {
+ surf = TTF_RenderText_Blended(font, text.c_str(), {0xFF, 0xFF, 0XFF, 0xFF});
+ } else {
+ surf = TTF_RenderText_Blended_Wrapped(font, text.c_str(),
+ {0xFF, 0xFF, 0XFF, 0xFF}, wrap_width);
+ }
if (surf == nullptr) {
throw std::runtime_error(
fmt::format("Unable to render text surface: {}", TTF_GetError()));
@@ -181,6 +186,8 @@ SDL_Texture* TextWidget::generate_text(const std::string& text) {
return texture;
}
+void TextWidget::set_wrap_width(int width) { wrap_width = width; }
+
ListWidget::ListWidget(SDL_Renderer* renderer, int element_height,
Alignment element_alignment)
: Widget{renderer}, element_height{element_height},