Raspberry Pi Weighting Control System
This project serves as a simple weighting control system, that was realized as a Bachelor Thesis
config_loader.cpp
Go to the documentation of this file.
1 #include "config_loader.h"
2 
3 #include <string.h>
4 #include <limits.h>
5 #include <errno.h>
6 #include <fstream>
7 #include <string>
8 #include <vector>
9 #include <exception>
10 #include <spdlog/spdlog.h>
11 #include "app_workspace.h"
12 #include "utility.h"
13 
14 // for debug
15 #include <iostream>
16 
17 #include "confini.h"
18 
19 #define GEN_DEFAULT_FONTS_PATH "fonts/"
20 #define GEN_DEFAULT_FONT "09809_COURIER.ttf"
21 #define GEN_DEFAULT_LANG_PATH "languages/"
22 #define GEN_DEFAULT_LANG "en"
23 //
24 #define KB_DEFAULT_DEV "/dev/spidev0.1"
25 #define KB_DEFAULT_MODE 0
26 #define KB_DEFAULT_BITS 8
27 #define KB_DEFAULT_SPEED 500000
28 #define KB_DEFAULT_DELAY 0
29 //
30 #define RFID_DEFAULT_DEV "/dev/spidev0.0"
31 #define RFID_DEFAULT_MODE 0
32 #define RFID_DEFAULT_BITS 8
33 #define RFID_DEFAULT_SPEED 1000000
34 #define RFID_DEFAULT_DELAY 0
35 //
36 #define DB_DEFAULT_HOST "localhost"
37 #define DB_DEFAULT_USER "pi"
38 #define DB_DEFAULT_PASSWORD "raspberry"
39 #define DB_DEFAULT_DATABASE "control_system"
40 #define DB_DEFAULT_PORT 3306
41 //
42 #define HX_DEFAULT_DATA_PIN 25
43 #define HX_DEFAULT_CLOCK_PIN 18
44 #define HX_DEFAULT_RATE 80
45 #define HX_DEFAULT_REF_UNIT -45
46 #define HX_DEFAULT_OFFSET -254817
47 
48 
49 namespace config_loader {
51  int parse_int(const char* str, int *res) {
52  char *tmpend;
53  long val;
54 
55  errno = 0;
56  val = strtol(str, &tmpend, 10);
57 
58  if (errno != 0) {// over/under flow and other error
59  spdlog::error("config_loader.cpp - Int parsing error. Over/Under-flow?");
60  return 1;
61  }
62  if (str == tmpend) {// no digits
63  spdlog::error("config_loader.cpp - Int parsing error. No digits in given string!");
64  return 1;
65  }
66  if (*tmpend != '\0') {
67  spdlog::error("config_loader.cpp - Int parsing error. String contains chars other than digits!");
68  return 1;
69  }
70  if (val < INT_MIN || val > INT_MAX) {
71  spdlog::error("config_loader.cpp - Int parsing error. Parsed number is LONG but not INT.");
72  return 1;
73  }
74 
75  *res = val;
76  return 0;
77  }
78 
83  int parse_and_init_int(const char* str, int *dest, int default_val, int max_val = INT_MAX) {
84  if (parse_int(str, dest)) {
85  spdlog::info("config_loader.cpp - Initializing int with default value.");
86  *dest = default_val;
87  return 1;
88  } // no else needed - parse_int() already assigns result to dest
89 
90  if (*dest > max_val) {
91  spdlog::info("config_loader.cpp - Initializing int with default value due to overflow.");
92  *dest = default_val;
93  return 1;
94  }
95 
96  return 0;
97  }
98 
103  int parse_and_init_str(std::string &dest, char *str, IniFormat format, const char* default_val) {
104  int len = ini_string_parse(str, format);
105  if (len <= 0) {
106  spdlog::info("config_loader.cpp - Initializing string with default value.");
107  dest = default_val;
108  } else {
109  dest = str;
110  }
111 
112  return 0;
113  }
114 
123  static int parse_and_init(IniDispatch * const disp, void * const v_other) {
124  // disp->append_to = section (+subsection) name (disp->at_len = length)
125  // disp->data = key (disp->d_len = length)
126  // disp->value = value (disp->v_len = length)
127 
128  // see libconfini's IniNodeType for type enum
129  if (disp->type == INI_UNKNOWN) {
130  // FIXME - log level ?
131  spdlog::error("config_loader.cpp - Couldn't determine type of config line. Key: {0}, Value: {1}",
132  disp->data,
133  disp->value
134  );
135  }
136 
137  if (disp->type == INI_KEY) {
138  /* // LEAVING THIS FOR DEBUGGING
139  spdlog::debug("config_loader.cpp - Encountered key. {0}, {1}",
140  disp->data,
141  disp->value
142  );
143  printf("Dispatch:\nid: %d\nat: %s\natl: %d\ndat: %s\ndatl: %d\nform: %d\ntype: %d\nval: %s\nvall: %d\n",
144  disp->dispatch_id, disp->append_to, disp->at_len,
145  disp->data, disp->d_len, disp->format, disp->type, disp->value, disp->v_len);
146  */
147 
148  #define conf ((app_config*) v_other)
149  #define IS_SECTION(SECTION) ini_array_match(SECTION, disp->append_to, '.', disp->format)
150  #define IS_KEY(KEY) ini_string_match_si(KEY, disp->data, disp->format)
151 
152  if (disp->at_len == 0) {
153  // read values that have no section here
154  } else if (IS_SECTION("general")) { // section "general"
155 
156  if (IS_KEY("debug_screens")) {
157  //if (ini_string_match_si("debug_screens", disp->data, disp->format)) {
158  // 0 = false (default value on fail)
159  // FIXME returns int, but debug_screens is boolean - size mismatch?
160  conf->debug_screens = ini_get_bool_i(disp->value, 0, disp->format);
161  } else if (IS_KEY("optional_config_path")) {
162  int len = ini_string_parse(disp->value, disp->format);
163  if (len > 0) {
164  conf->opt_conf_path = disp->value;
165  }
166  } else if (IS_KEY("init_failure_quit")) {
167  conf->init_fail_quit = ini_get_bool_i(disp->value, 0, disp->format);
168  } else if (IS_KEY("fonts_path")) {
169  parse_and_init_str(conf->fonts_path, disp->value, disp->format, GEN_DEFAULT_FONTS_PATH);
170  if (conf->fonts_path.back() != '/')
171  conf->fonts_path.append("/");
172  /*
173  int len = ini_string_parse(disp->value, disp->format);
174  len <= 0 ? conf->fonts_path = "fonts/" : conf->fonts_path = disp->value;
175  */
176  } else if (IS_KEY("log_debug")) {
177  conf->log_debug = ini_get_bool_i(disp->value, 0, disp->format);
178  } else if (IS_KEY("font")) {
179  parse_and_init_str(conf->font, disp->value, disp->format, GEN_DEFAULT_FONT);
180  } else if (IS_KEY("lang_path")) {
181  parse_and_init_str(conf->lang_path, disp->value, disp->format, GEN_DEFAULT_LANG_PATH);
182  if (conf->lang_path.back() != '/')
183  conf->lang_path.append("/");
184  } else if (IS_KEY("available_langs")) {
185  int size = ini_array_get_length(disp->value, ',', disp->format);
186  char *lang;
187  for (int i = 0; i < size; i++) {
188  lang = ini_array_release(&(disp->value), ',', disp->format);
189  ini_string_parse(lang, disp->format);
190  conf->lang_options.push_back(lang);
191  }
192  } else if (IS_KEY("default_lang")) {
193  parse_and_init_str(conf->lang_default, disp->value, disp->format, GEN_DEFAULT_LANG);
194  conf->lang_current = conf->lang_default;
195  }
196 
197  } else if (IS_SECTION("spi.keyboard")) { // section "spi.*"
198  app_workspace_ns::spi_config *spi = &(conf->keyb_conf);
199 
200  if (IS_KEY("dev")) {
201  parse_and_init_str(spi->dev, disp->value, disp->format, KB_DEFAULT_DEV);
202  } else if (IS_KEY("mode")) {
203  parse_and_init_int(disp->value, (int*) &(spi->config->mode), KB_DEFAULT_MODE, UINT8_MAX);
204  } else if (IS_KEY("bits")) {
205  parse_and_init_int(disp->value, (int*) &(spi->config->bits_per_word), KB_DEFAULT_BITS, UINT8_MAX);
206  } else if (IS_KEY("speed")) {
207  parse_and_init_int(disp->value, (int*) &(spi->config->speed), KB_DEFAULT_SPEED);
208  } else if (IS_KEY("delay")) {
209  parse_and_init_int(disp->value, (int*) &(spi->config->delay), KB_DEFAULT_DELAY, UINT16_MAX);
210  }
211 
212  } else if (IS_SECTION("spi.rfid")) {
213  app_workspace_ns::spi_config *spi = &(conf->rfid_conf);
214 
215  if (IS_KEY("dev")) {
216  parse_and_init_str(spi->dev, disp->value, disp->format, RFID_DEFAULT_DEV);
217  } else if (IS_KEY("mode")) {
218  parse_and_init_int(disp->value, (int*) &(spi->config->mode), RFID_DEFAULT_MODE, UINT8_MAX);
219  } else if (IS_KEY("bits")) {
220  parse_and_init_int(disp->value, (int*) &(spi->config->bits_per_word), RFID_DEFAULT_BITS, UINT8_MAX);
221  } else if (IS_KEY("speed")) {
222  parse_and_init_int(disp->value, (int*) &(spi->config->speed), RFID_DEFAULT_SPEED);
223  } else if (IS_KEY("delay")) {
224  parse_and_init_int(disp->value, (int*) &(spi->config->delay), RFID_DEFAULT_DELAY, UINT16_MAX);
225  }
226 
227  } else if (IS_SECTION("database")) { // section "database"
228  if (IS_KEY("host")) {
229  parse_and_init_str(conf->db_conf.host, disp->value, disp->format, DB_DEFAULT_HOST);
230  } else if (IS_KEY("user")) {
231  parse_and_init_str(conf->db_conf.user, disp->value, disp->format, DB_DEFAULT_USER);
232  } else if (IS_KEY("password")) {
233  parse_and_init_str(conf->db_conf.passwd, disp->value, disp->format, DB_DEFAULT_PASSWORD);
234  } else if (IS_KEY("database")) {
235  parse_and_init_str(conf->db_conf.db, disp->value, disp->format, DB_DEFAULT_DATABASE);
236  } else if (IS_KEY("port")) {
237  parse_and_init_int(disp->value, (int*) &(conf->db_conf.port), DB_DEFAULT_PORT, UINT16_MAX);
238  }
239 
240  } else if (IS_SECTION("hx711")) {
241  if (IS_KEY("data_pin")) {
242  parse_and_init_int(disp->value, &(conf->hx_conf.data_pin), HX_DEFAULT_DATA_PIN);
243  } else if (IS_KEY("clock_pin")) {
244  parse_and_init_int(disp->value, &(conf->hx_conf.clock_pin), HX_DEFAULT_CLOCK_PIN);
245  } else if (IS_KEY("rate")) {
246  parse_and_init_int(disp->value, &(conf->hx_conf.rate), HX_DEFAULT_RATE);
247  }
248  // MOVED to opt config
249  // else if (IS_KEY("ref_unit")) {
250  // parse_and_init_int(disp->value, &(conf->hx_conf.ref_unit), HX_DEFAULT_REF_UNIT);
251  // } else if (IS_KEY("offset")) {
252  // parse_and_init_int(disp->value, &(conf->hx_conf.offset), HX_DEFAULT_OFFSET);
253  // }
254  }
255  }
256 
257  // those settings were moved to opt config, but in case opt config is missing, they still need to be init to def
258  conf->hx_conf.ref_unit = HX_DEFAULT_REF_UNIT;
259  conf->hx_conf.ref_unit = HX_DEFAULT_OFFSET;
260 
261  #undef conf
262  #undef IS_SECTION
263  #undef IS_KEY
264 
265  return 0;
266  }
267 
268  int load_main_config_ini(const char* cfg_path) {
269  app_config *cfg = app_workspace::get_instance()->main_config.get();
270 
271  if (load_ini_path(
272  cfg_path,
273  INI_DEFAULT_FORMAT,
274  NULL,
275  parse_and_init,
276  cfg))
277  {
278  spdlog::critical("config_loader.cpp - Failed to read main configuration file (app_config.conf)");
279  return 1;
280  }
281 
282  return 0;
283  }
284 
285 
286  int exist_opt_config(const char* cfg_path) {
287  std::fstream config(cfg_path, std::ios::in);
288  if (config.is_open()) {
289  config.close();
290  return 0;
291  } else
292  return 1;
293  }
294 
295  int load_opt_config(const char* cfg_path) {
296  app_config *cfg = app_workspace::get_instance()->main_config.get();
297  // std::fstream config(cfg->opt_conf_path, std::ios::in);
298  std::fstream config(cfg_path, std::ios::in);
299 
300  if (!config.is_open()) {
301  spdlog::error("config_loader.cpp - Failed to open optional config file. Cannot read config.");
302  return 1;
303  }
304 
305  std::string line;
306  size_t sep_index;
307 
308  while (std::getline(config, line)) {
309  sep_index = line.find_first_of('#');
310  if (sep_index != line.npos) { // if the line contains comment trim line only up to #
311  line = line.substr(0, sep_index);
312  }
313 
314  sep_index = line.find_first_of('=');
315  if (sep_index == line.npos) {
316  spdlog::debug("config_loader.cpp - Config line doesn't have a key! Skipping line. (Line: {0})",
317  line.c_str());
318  continue;
319  }
320 
321  std::string key = line.substr(0, sep_index);
322  trim(key);
323  std::string val = line.substr(sep_index + 1, line.length());
324  trim(val);
325 
326  try {
327  // warning! KEY MUST BE in lower case, not doing tolower()
328  if (!key.compare(OPT_CFG_REF_UNIT_KEY)) { // is ref_unit
329  spdlog::debug("config_loader.cpp - Optional config found {0}", key.c_str());
330  cfg->hx_conf.ref_unit = std::stoi(val);
331  } else if (!key.compare(OPT_CFG_OFFSET_KEY)) { // is offset
332  spdlog::debug("config_loader.cpp - Optional config found {0}", key.c_str());
333  cfg->hx_conf.offset = std::stoi(val);
334  } else { // is nothing else
335  spdlog::error("config_loader.cpp - Optional config: Unknown key: {0}", key.c_str());
336  }
337  } catch (std::invalid_argument &ex) {
338  spdlog::error("config_loader.cpp - Optional config: {0} - {1} IS NOT a number!",
339  key.c_str(), val.c_str());
340  } catch (std::out_of_range &ex) {
341  spdlog::error("config_loader.cpp - Optional config: {0} - {1} value is OUT OF RANGE",
342  key.c_str(), val.c_str());
343  }
344 
345  spdlog::debug("config_loader.cpp - Optional config: {0} - {1}", key.c_str(), val.c_str());
346  }
347 
348  config.close();
349  return 0;
350  }
351 
352  int replace_opt_in_opt_config(const char* cfg_path, std::string key, std::string value) {
353  std::fstream config(cfg_path, std::ios::in);
354  std::vector<std::string> lines;
355 
356  if (!config.is_open()) {
357  spdlog::error("config_loader.cpp - Failed to open optional config file. Cannot read config.");
358  return 1;
359  }
360 
361  std::string line;
362 
363  while (std::getline(config, line)) {
364  lines.push_back(line);
365  }
366  config.close();
367 
368  std::ofstream new_config(cfg_path, std::ios::trunc);
369  size_t sep_index;
370 
371  for (size_t i = 0; i < lines.size(); i++) {
372  sep_index = lines.at(i).find_first_of('=');
373 
374  if (lines.at(i)[0] == '#' || sep_index == lines.at(i).npos) { // lines a comment or doesnt have key
375  new_config << lines.at(i) << "\n";
376  continue;
377  }
378 
379  std::string ex_key = lines.at(i).substr(0, sep_index);
380  trim(ex_key);
381 
382  if (!ex_key.compare(key)) {
383  std::string new_line = key + "=" + value;
384  new_config << new_line << "\n";
385  } else {
386  new_config << lines.at(i) << "\n";
387  }
388  }
389 
390  new_config.flush();
391  new_config.close();
392 
393  return 0;
394  }
395 };
static std::unique_ptr< app_workspace > & get_instance()
Get the instance app_workspace which is a singleton.
#define KB_DEFAULT_MODE
#define KB_DEFAULT_DEV
#define RFID_DEFAULT_DEV
#define DB_DEFAULT_PORT
#define GEN_DEFAULT_FONTS_PATH
#define KB_DEFAULT_SPEED
#define KB_DEFAULT_DELAY
#define HX_DEFAULT_CLOCK_PIN
#define RFID_DEFAULT_MODE
#define DB_DEFAULT_USER
#define GEN_DEFAULT_FONT
#define KB_DEFAULT_BITS
#define GEN_DEFAULT_LANG_PATH
#define DB_DEFAULT_DATABASE
#define IS_SECTION(SECTION)
#define RFID_DEFAULT_DELAY
#define HX_DEFAULT_OFFSET
#define conf
#define RFID_DEFAULT_BITS
#define DB_DEFAULT_PASSWORD
#define RFID_DEFAULT_SPEED
#define DB_DEFAULT_HOST
#define HX_DEFAULT_DATA_PIN
#define HX_DEFAULT_REF_UNIT
#define IS_KEY(KEY)
#define GEN_DEFAULT_LANG
#define HX_DEFAULT_RATE
#define OPT_CFG_REF_UNIT_KEY
Definition: config_loader.h:6
#define OPT_CFG_OFFSET_KEY
Definition: config_loader.h:7
int replace_opt_in_opt_config(const char *cfg_path, std::string key, std::string value)
This is used to replace a values in optional config.
int load_main_config_ini(const char *cfg_path)
This funciton loads main configuration. _ini symobolizes that the expected format is ....
int load_opt_config(const char *cfg_path)
Loads the optional config.
int parse_int(const char *str, int *res)
int parse_and_init_int(const char *str, int *dest, int default_val, int max_val=INT_MAX)
int exist_opt_config(const char *cfg_path)
Checks if optional config exists.
int parse_and_init_str(std::string &dest, char *str, IniFormat format, const char *default_val)
SPI * spi
Definition: rc522.cpp:36
Structure that holds the values of the application configuration file.
app_workspace_ns::hx711_config hx_conf
Structure holding SPI device and SPI config which is a library structure "spi_config_t".