Raspberry Pi Weighting Control System
This project serves as a simple weighting control system, that was realized as a Bachelor Thesis
Macros | Functions
main.cpp File Reference
#include "imgui.h"
#include "backends/imgui_impl_sdl.h"
#include "backends/imgui_impl_opengl3.h"
#include <memory>
#include <stdio.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include <thread>
#include <spdlog/spdlog.h>
#include "app_workspace.h"
#include "keyboard.h"
#include "screen_manager.h"
#include "event_handler.h"
#include "rfid_reader.h"
#include "config_loader.h"
#include "localisation.h"
#include "utility.h"
#include <unistd.h>

Go to the source code of this file.

Macros

#define MAX_EVENTS_PER_FRAME   10
 
#define MAX_CONSEC_EVENT_HANDLE_FAILURES   3
 
#define DEFAULT_MAIN_CFG_PATH   "./app_config.conf"
 

Functions

int parse_args_and_prepare (int argc, char **argv)
 
int main (int argc, char **argv)
 

Macro Definition Documentation

◆ DEFAULT_MAIN_CFG_PATH

#define DEFAULT_MAIN_CFG_PATH   "./app_config.conf"

Definition at line 35 of file main.cpp.

◆ MAX_CONSEC_EVENT_HANDLE_FAILURES

#define MAX_CONSEC_EVENT_HANDLE_FAILURES   3

Definition at line 33 of file main.cpp.

◆ MAX_EVENTS_PER_FRAME

#define MAX_EVENTS_PER_FRAME   10

Definition at line 32 of file main.cpp.

Function Documentation

◆ main()

int main ( int  argc,
char **  argv 
)

Definition at line 57 of file main.cpp.

57  {
58  if (parse_args_and_prepare(argc, argv)) {
59  spdlog::critical("main.cpp - Failed to parse arguments and prepare basic program settings. Exiting...");
60  return 1;
61  }
62 
63  if (app_workspace::get_instance()->main_config->log_debug)
64  spdlog::set_level(spdlog::level::debug); // Set global log level to debug (if true in config)
65 
66  // optional config (hx ref_unit and offset)
67  if (app_workspace::get_instance()->main_config->opt_conf_path.size() > 0) {
68  if (config_loader::load_opt_config(app_workspace::get_instance()->main_config->opt_conf_path.c_str())) {
69  spdlog::error("main.cpp - Failed to load optional config file");
70  }
71  }
72 
73  // example how to change optional config
74  /*
75  config_loader::replace_opt_in_opt_config(app_workspace::get_instance()->main_config->opt_conf_path.c_str(),
76  "offset", std::to_string(5000));
77  */
78 
79  // print_config() - prints on debug level
80  app_workspace::get_instance()->print_config();
81 
82  if (load_default_dict()) {
83  spdlog::critical("Please make sure language options are set correctly in main configuration file. Exiting...");
84  return 2;
85  }
86 
87  // Setup SDL
88  if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) {
89  spdlog::critical("main.cpp - Failed to init SDL. Error: %s\n", SDL_GetError());
90  return 3;
91  }
92 
93  // Decide GL+GLSL versions, leaving this whole thing, to support development on multiple platforms
94 #if defined(IMGUI_IMPL_OPENGL_ES2)
95  // GL ES 2.0 + GLSL 100
96  const char* glsl_version = "#version 100";
97  SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
98  SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
99  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
100  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
101 #elif defined(__APPLE__)
102  // GL 3.2 Core + GLSL 150
103  const char* glsl_version = "#version 150";
104  SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
105  SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
106  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
107  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
108 #else
109  // GL 3.0 + GLSL 130
110  const char* glsl_version = "#version 130";
111  SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
112  SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
113  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
114  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
115 #endif
116 
117  // Create window with graphics context
118  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
119  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
120  SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
121  SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI);
122  SDL_Window* window = SDL_CreateWindow(get_localized_text("APP_WINDOW_TITLE"),
123  SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 480, 640, window_flags);
124  SDL_GLContext gl_context = SDL_GL_CreateContext(window);
125  SDL_GL_MakeCurrent(window, gl_context);
126  SDL_GL_SetSwapInterval(1); // Enable vsync (is this wanted?)
127 
128  // Setup Dear ImGui context
129  IMGUI_CHECKVERSION();
130  ImGui::CreateContext();
131  ImGuiIO& io = ImGui::GetIO(); (void)io;
132  io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
133  // this is docking branch specific, might not need this and use the normal "master" version instead
134  io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
135  io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
136  //io.ConfigViewportsNoAutoMerge = true;
137  //io.ConfigViewportsNoTaskBarIcon = true;
138 
139  // Setup Dear ImGui style (light is probably desired)
140  // ImGui::StyleColorsDark();
141  // ImGui::StyleColorsClassic();
142  ImGui::StyleColorsLight();
143 
144  // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
145  ImGuiStyle& style = ImGui::GetStyle();
146  if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
147  style.WindowRounding = 0.0f;
148  style.Colors[ImGuiCol_WindowBg].w = 1.0f;
149  }
150  style.FrameBorderSize = 1.0f;
151 
152  // Setup Platform/Renderer backends
153  ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
154  ImGui_ImplOpenGL3_Init(glsl_version);
155 
156 
157  // State and flag vars
158  ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
159  bool main_window_open = true;
160  ImGuiWindowFlags main_window_flags = 0;
161 // ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse,
162  main_window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
163  ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoMouseInputs;
164  bool done = false;
165  std::unique_ptr<app_workspace> &app_wrk = app_workspace::get_instance();
166  std::unique_ptr<screen_manager> scr_mgr(new screen_manager());
167  int rv = 0;
168  std::thread keyboard_thread, rfid_thread;
169 
170  // this MUST be here, to get access to screen_manager anywhere if needed
171  app_wrk->set_scr_mgr(scr_mgr.get());
172 
173  // load desired fonts
174  if (app_wrk->load_font_size(app_workspace_ns::SMALL_FONT, 12.0f)) {
175  spdlog::error("Failed to lod font (this is already logged from load_font_size()");
176  // return 1;
177  }
178  if (app_wrk->load_font_size(app_workspace_ns::NORMAL_FONT, 24.0f)) {
179  spdlog::error("Failed to lod font (this is already logged from load_font_size()");
180  // return 1;
181  }
182  if (app_wrk->load_font_size(app_workspace_ns::BIG_FONT, 48.0f)) {
183  spdlog::error("Failed to lod font (this is already logged from load_font_size()");
184  // return 1;
185  }
186 
187  // Pre-loop calls (inits)
188  rv = app_wrk->init_hx711_controller();
189  if (rv) {
190  spdlog::critical("main.cpp - Failed to start HX711.");
191  if (app_wrk->main_config->init_fail_quit)
192  return 1;
193  }
194 
196  if (rv) {
197  spdlog::critical("main.cpp - SPI keyboard failed to initialize. Unless another keyboard"
198  " is connected, you won't be able to control the application.");
199  if (app_wrk->main_config->init_fail_quit) return 1;
200  } else
201  keyboard_thread = std::thread(keyboard::start_capturing_events);
202 
204  if (rv) {
205  spdlog::critical("main.cpp - SPI keyboard failed to initialize. Unless another keyboard"
206  " is connected, you won't be able to control the application.");
207  if (app_wrk->main_config->init_fail_quit) return 1;
208  } else
209  rfid_thread = std::thread(rfid_reader::start_reading_cards);
210 
211  rv = app_workspace::get_instance()->open_db_connection();
212  if (rv) {
213  spdlog::critical("main.cpp - Failed to open DB connection or initialize DB driver.");
214  return 1; // always stop the app, the app is pointless without DB
215  }
216 
217  // Main loop
218  while (!done) {
219  // Poll and handle events (inputs, window resize, etc.)
220  // Potentially can stall the app, with event flood on rpi (slow HW)
221  SDL_Event event;
222  kb_event* raw_event;
223  rfid_event* rfid_ev;
224 
225  while ((rfid_ev = rfid_reader::poll_event())) {
227  }
228 
229  while ((raw_event = keyboard::poll_event())) {
231  }
232 
233  while (SDL_PollEvent(&event)) {
234  // this is to see what event series occurs for example when clicking a button, good for deubg except maybe using error log level
235 
236  // handle_sdl_event = custom event handling
238  //if (event_handler::handle_event(&event, scr_mgr.get()))
239  ImGui_ImplSDL2_ProcessEvent(&event);
240 
241  if (event.type == SDL_QUIT)
242  done = true;
243  if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE &&
244  event.window.windowID == SDL_GetWindowID(window))
245  done = true;
246  }
247 
248  // Start the Dear ImGui frame
249  ImGui_ImplOpenGL3_NewFrame();
250  ImGui_ImplSDL2_NewFrame();
251  ImGui::NewFrame();
252 
253  // START window data
254 
255  bool use_work_area = true;
256 
257  const ImGuiViewport* viewport = ImGui::GetMainViewport();
258  ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
259  ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size);
260 
261  ImGui::Begin(get_localized_text("FRAME_TITLE"), &main_window_open, main_window_flags);
262 
263  ImGui::PushStyleColor(ImGuiCol_NavHighlight, (ImVec4) ImColor(50, 50, 255, 255));
264  ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4) ImColor(230, 230, 230, 255));
265  ImGui::PushStyleColor(ImGuiCol_Border, (ImVec4) ImColor(50, 50, 50, 255));
266 
267  // render scren
268  app_wrk->get_scr_mgr()->render_active_screen();
269  //scr_mgr->render_active_screen();
270 
271  while (app_wrk->font_push_cnt > 0) {
272  ImGui::PopFont();
273  app_wrk->font_push_cnt--;
274  }
275 
276  ImGui::PopStyleColor(3); // par must match number of PushStyleColor
277 
278  ImGui::End();
279  // END window data
280 
281  // Rendering
282  ImGui::Render();
283  glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
284  glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
285  glClear(GL_COLOR_BUFFER_BIT);
286  ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
287 
288  // Update and Render additional Platform Windows
289  if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
290  SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow();
291  SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext();
292  ImGui::UpdatePlatformWindows();
293  ImGui::RenderPlatformWindowsDefault();
294  SDL_GL_MakeCurrent(backup_current_window, backup_current_context);
295  }
296 
297  SDL_GL_SwapWindow(window);
298  }
299 
300  // Cleanup
301  ImGui_ImplOpenGL3_Shutdown();
302  ImGui_ImplSDL2_Shutdown();
303  ImGui::DestroyContext();
304 
305  SDL_GL_DeleteContext(gl_context);
306  SDL_DestroyWindow(window);
307  SDL_Quit();
308 
309  keyboard::clean(); // stops thread if joined
310  rfid_reader::clean(); // stops thread if joined
311 
312  if (keyboard_thread.joinable()) // thread exists
313  keyboard_thread.detach();
314  if (rfid_thread.joinable())
315  rfid_thread.detach();
316 
317  // don't want to brick the application in case the thread didn't want to finish, but give them 200ms to finish
318  std::this_thread::sleep_for(std::chrono::milliseconds(200));
319 
320  return 0;
321 }
static std::unique_ptr< app_workspace > & get_instance()
Get the instance app_workspace which is a singleton.
const char * get_localized_text(const char *key)
Get the localized text object.
int load_default_dict()
Loads default language dictionary.
int parse_args_and_prepare(int argc, char **argv)
Definition: main.cpp:37
int load_opt_config(const char *cfg_path)
Loads the optional config.
int handle_rfid_event(rfid_event *event)
Function handles RFID event.
int handle_raw_event(kb_event *raw_event)
Offers option for cutom event handling. If not desired only created SDL event.
int handle_sdl_event(const SDL_Event *event)
Handles SDL2 library event.
void start_capturing_events()
Infinite loop that reads SPI keyboard. Runs in it's own thread!
Definition: keyboard.cpp:140
void clean()
Stops event capturing and closes SPI.
Definition: keyboard.cpp:209
int init_from_conf()
Initializes my_spi with config loaded from config file into app_workspace. Starts SPI.
Definition: keyboard.cpp:116
kb_event * poll_event()
Polls event from queue into param event Returns 1 on success because it is inteded to return "true" o...
Definition: keyboard.cpp:58
int init_from_conf()
RFID reader init from loaded configuration.
Definition: rfid_reader.cpp:46
void clean()
Stops the reading thread.
rfid_event * poll_event()
Polls event from RFID event_queue.
void start_reading_cards()
Loop that is run in its own thread, reading the RFID reader.
Definition: rfid_reader.cpp:76
Structure of SPI keyboard event, Contains pressed scancode, flags and timestamp of event creation.
Definition: keyboard.h:23
Structure used for RFID event. This structure contains read tag and flags of the event.
Definition: rfid_reader.h:100

◆ parse_args_and_prepare()

int parse_args_and_prepare ( int  argc,
char **  argv 
)

Definition at line 37 of file main.cpp.

37  {
38  input_parser args(argc, argv);
39 
40  if (args.cmd_option_exists("-c")) { // main config file path + name
41  const char* val = args.get_cmd_option("-c").c_str();
43  spdlog::critical("main.cpp - Failed to load main config. Perhaps path {0} is wrong?", val);
44  return 1;
45  }
46  } else {
48  spdlog::critical("main.cpp - Failed to load main config through default path. Please either provide path"
49  "to main configuration with -c argument or make sure {0} exits.", DEFAULT_MAIN_CFG_PATH);
50  return 1;
51  }
52  }
53 
54  return 0;
55 }
Simple argument parser.
Definition: utility.h:16
#define DEFAULT_MAIN_CFG_PATH
Definition: main.cpp:35
int load_main_config_ini(const char *cfg_path)
This funciton loads main configuration. _ini symobolizes that the expected format is ....