Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

278 lines
7.2 KiB

Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Terminal: change command-line parser (#2247) Change the underlying command line handling: - switch to a custom parser, inspired by redis / sds - update terminalRegisterCommand signature, pass only bare minimum - clean-up `help` & `commands`. update settings `set`, `get` and `del` - allow our custom test suite to run command-line tests - clean-up Stream IO to allow us to print large things into debug stream (for example, `eeprom.dump`) - send parsing errors to the debug log As a proof of concept, introduce `TERMINAL_MQTT_SUPPORT` and `TERMINAL_WEB_API_SUPPORT` - MQTT subscribes to the `<root>/cmd/set` and sends response to the `<root>/cmd`. We can't output too much, as we don't have any large-send API. - Web API listens to the `/api/cmd?apikey=...&line=...` (or PUT, params inside the body). This one is intended as a possible replacement of the `API_SUPPORT`. Internals introduce a 'task' around the AsyncWebServerRequest object that will simulate what WiFiClient does and push data into it continuously, switching between CONT and SYS. Both are experimental. We only accept a single command and not every command is updated to use Print `ctx.output` object. We are also somewhat limited by the Print / Stream overall, perhaps I am overestimating the usefulness of Arduino compatibility to such an extent :) Web API handler can also sometimes show only part of the result, whenever the command tries to yield() by itself waiting for something. Perhaps we would need to create a custom request handler for that specific use-case.
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
Rework settings (#2282) * wip based on early draft. todo benchmarking * fixup eraser, assume keys are unique * fix cursor copy, test removal at random * small benchmark via permutations. todo lambdas and novirtual * fix empty condition / reset * overwrite optimizations, fix move offsets overflows * ...erase using 0xff instead of 0 * test the theory with code, different length kv were bugged * try to check for out-of-bounds writes / reads * style * trying to fix mover again * clarify length, defend against reading len on edge * fix uncommited rewind change * prove space assumptions * more concise traces, fix move condition (agrh!!!) * slightly more internal knowledge (estimates API?) * make sure cursor is only valid within the range * ensure 0 does not blow things * go back up * cursor comments * comments * rewrite writes through cursor * in del too * estimate kv storage requirements, return available size * move raw erase / move into a method, allow ::set to avoid scanning storage twice * refactor naming, use in code * amend storage slicing test * fix crash handler offsets, cleanup configuration * start -> begin * eeprom readiness * dependencies * unused * SPI_FLASH constants for older Core * vtables -> templates * less include dependencies * gcov help, move estimate outside of the class * writer position can never match, use begin + offset * tweak save_crash to trigger only once in a serious crash * doh, header function should be inline * foreach api, tweak structs for public api * use test helper class * when not using foreach, move cursor reset closer to the loop using read_kv * coverage comments, fix typo in tests decltype * ensure set() does not break with offset * make codacy happy again
4 years ago
  1. /*
  2. SETTINGS MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #pragma once
  6. #include "espurna.h"
  7. #include <functional>
  8. #include <utility>
  9. #include <vector>
  10. #include <ArduinoJson.h>
  11. #include "broker.h"
  12. #include "storage_eeprom.h"
  13. #include "settings_embedis.h"
  14. BrokerDeclare(ConfigBroker, void(const String& key, const String& value));
  15. // --------------------------------------------------------------------------
  16. namespace settings {
  17. struct EepromStorage {
  18. uint8_t read(size_t pos) {
  19. return EEPROMr.read(pos);
  20. }
  21. void write(size_t pos, uint8_t value) {
  22. EEPROMr.write(pos, value);
  23. }
  24. void commit() {
  25. #if SETTINGS_AUTOSAVE
  26. eepromCommit();
  27. #endif
  28. }
  29. };
  30. using kvs_type = embedis::KeyValueStore<EepromStorage>;
  31. extern kvs_type kv_store;
  32. } // namespace settings
  33. // --------------------------------------------------------------------------
  34. class settings_key_t {
  35. public:
  36. settings_key_t(const char* value, unsigned char index) :
  37. _value(value), _index(index)
  38. {}
  39. settings_key_t(const String& value, unsigned char index) :
  40. _value(value), _index(index)
  41. {}
  42. settings_key_t(String&& value, unsigned char index) :
  43. _value(std::move(value)), _index(index)
  44. {}
  45. settings_key_t(const char* value) :
  46. _value(value), _index(-1)
  47. {}
  48. settings_key_t(const String& value) :
  49. _value(value), _index(-1)
  50. {}
  51. settings_key_t(const __FlashStringHelper* value) :
  52. _value(value), _index(-1)
  53. {}
  54. settings_key_t() :
  55. _value(), _index(-1)
  56. {}
  57. bool match(const char* value) const {
  58. return (_value == value) || (toString() == value);
  59. }
  60. bool match(const String& value) const {
  61. return (_value == value) || (toString() == value);
  62. }
  63. String toString() const;
  64. explicit operator String () const {
  65. return toString();
  66. }
  67. private:
  68. const String _value;
  69. int _index;
  70. };
  71. using settings_move_key_t = std::pair<settings_key_t, settings_key_t>;
  72. using settings_filter_t = std::function<String(String& value)>;
  73. // --------------------------------------------------------------------------
  74. struct settings_cfg_t {
  75. String& setting;
  76. const char* key;
  77. const char* default_value;
  78. };
  79. using settings_cfg_list_t = std::initializer_list<settings_cfg_t>;
  80. // --------------------------------------------------------------------------
  81. namespace settings {
  82. namespace internal {
  83. uint32_t u32fromString(const String& string, int base);
  84. template <typename T>
  85. using convert_t = T(*)(const String& value);
  86. template <typename T>
  87. T convert(const String& value);
  88. // --------------------------------------------------------------------------
  89. template <>
  90. float convert(const String& value);
  91. template <>
  92. double convert(const String& value);
  93. template <>
  94. int convert(const String& value);
  95. template <>
  96. long convert(const String& value);
  97. template <>
  98. bool convert(const String& value);
  99. template <>
  100. unsigned long convert(const String& value);
  101. template <>
  102. unsigned int convert(const String& value);
  103. template <>
  104. unsigned short convert(const String& value);
  105. template <>
  106. unsigned char convert(const String& value);
  107. template<typename T>
  108. String serialize(const T& value);
  109. template<typename T>
  110. String serialize(const T& value) {
  111. return String(value);
  112. }
  113. } // namespace settings::internal
  114. } // namespace settings
  115. // --------------------------------------------------------------------------
  116. struct settings_key_match_t {
  117. using match_f = bool(*)(const char* key);
  118. using key_f = const String(*)(const String& key);
  119. match_f match;
  120. key_f key;
  121. };
  122. void settingsRegisterDefaults(const settings_key_match_t& matcher);
  123. String settingsQueryDefaults(const String& key);
  124. // --------------------------------------------------------------------------
  125. void moveSetting(const String& from, const String& to);
  126. void moveSetting(const String& from, const String& to, unsigned int index);
  127. void moveSettings(const String& from, const String& to);
  128. template<typename R, settings::internal::convert_t<R> Rfunc = settings::internal::convert>
  129. R getSetting(const settings_key_t& key, R defaultValue) __attribute__((noinline));
  130. template<typename R, settings::internal::convert_t<R> Rfunc = settings::internal::convert>
  131. R getSetting(const settings_key_t& key, R defaultValue) {
  132. auto result = settings::kv_store.get(key.toString());
  133. if (!result) {
  134. return defaultValue;
  135. }
  136. return Rfunc(result.value);
  137. }
  138. template<>
  139. String getSetting(const settings_key_t& key, String defaultValue);
  140. String getSetting(const settings_key_t& key);
  141. String getSetting(const settings_key_t& key, const char* defaultValue);
  142. String getSetting(const settings_key_t& key, const __FlashStringHelper* defaultValue);
  143. template<typename T>
  144. bool setSetting(const settings_key_t& key, const T& value) {
  145. return settings::kv_store.set(key.toString(), String(value));
  146. }
  147. template<>
  148. bool setSetting(const settings_key_t& key, const String& value);
  149. bool delSetting(const settings_key_t& key);
  150. bool hasSetting(const settings_key_t& key);
  151. void saveSettings();
  152. void resetSettings();
  153. void settingsGetJson(JsonObject& data);
  154. bool settingsRestoreJson(char* json_string, size_t json_buffer_size = 1024);
  155. bool settingsRestoreJson(JsonObject& data);
  156. size_t settingsKeyCount();
  157. std::vector<String> settingsKeys();
  158. void settingsProcessConfig(const settings_cfg_list_t& config, settings_filter_t filter = nullptr);
  159. size_t settingsSize();
  160. void settingsSetup();
  161. // -----------------------------------------------------------------------------
  162. // Configuration updates
  163. // -----------------------------------------------------------------------------
  164. int migrateVersion();
  165. void migrate();
  166. // -----------------------------------------------------------------------------
  167. // Deprecated implementation
  168. // -----------------------------------------------------------------------------
  169. template <typename T>
  170. String getSetting(const String& key, unsigned char index, T defaultValue)
  171. __attribute__((deprecated("getSetting({key, index}, default) should be used instead")));
  172. template<typename T>
  173. bool setSetting(const String& key, unsigned char index, T value)
  174. __attribute__((deprecated("setSetting({key, index}, value) should be used instead")));
  175. template<typename T>
  176. bool hasSetting(const String& key, unsigned char index)
  177. __attribute__((deprecated("hasSetting({key, index}) should be used instead")));
  178. template<typename T>
  179. bool delSetting(const String& key, unsigned char index)
  180. __attribute__((deprecated("delSetting({key, index}) should be used instead")));
  181. // --------------------------------------------------------------------------
  182. template<typename T>
  183. String getSetting(const String& key, unsigned char index, T defaultValue) {
  184. return getSetting({key, index}, defaultValue);
  185. }
  186. template<typename T>
  187. bool setSetting(const String& key, unsigned char index, T value) {
  188. return setSetting({key, index}, value);
  189. }
  190. template<typename T>
  191. bool hasSetting(const String& key, unsigned char index) {
  192. return hasSetting({key, index});
  193. }
  194. template<typename T>
  195. bool delSetting(const String& key, unsigned char index) {
  196. return delSetting({key, index});
  197. }