diff --git a/components/common/cpp/CMakeLists.txt b/components/common/cpp/CMakeLists.txt index 2b99f94b23d2eb130b66a6af9134661b1e9bded9..f85d96d2ce3ba9129b38a0510c7e3495a5893bdb 100644 --- a/components/common/cpp/CMakeLists.txt +++ b/components/common/cpp/CMakeLists.txt @@ -1,6 +1,7 @@ set(COMMONSRC src/config.cpp src/configuration.cpp + src/configurable.cpp src/opencv_to_pcl.cpp ) diff --git a/components/common/cpp/include/ftl/configurable.hpp b/components/common/cpp/include/ftl/configurable.hpp index 71c8a556401fd568e0808e762fe5d851b9fb522a..d14585dc8ceb4894fded0f2269c8583fc4df5ccd 100644 --- a/components/common/cpp/include/ftl/configurable.hpp +++ b/components/common/cpp/include/ftl/configurable.hpp @@ -6,18 +6,35 @@ #include <nlohmann/json.hpp> #include <string> #include <tuple> +#include <map> +#include <list> +#include <functional> #define REQUIRED(...) required(__func__, __VA_ARGS__) namespace ftl { +/** + * The Configurable class should be inherited by any entity that is to be + * configured using json objects. Additionally, any such object can then be + * reconfigured through changes to that underlying json object with event + * callbacks being triggered when specific changes occur. + * + * Configurables may also optionally have a URI that enables static methods + * of getting and setting configuration options. These static methods are used + * by network RPCs to enable remote reconfiguration. + */ class Configurable { public: Configurable() {} explicit Configurable(nlohmann::json &config) : config_(config) { - + if (config["uri"].is_string()) __changeURI(config["uri"].get<std::string>(), this); } + /** + * Force the JSON object to have specific properties with a specific type. + * If not, emit errors and terminate the application. + */ void required(const char *f, const std::vector<std::tuple<std::string, std::string, std::string>> &r) { bool diderror = false; for (auto i : r) { @@ -33,10 +50,55 @@ class Configurable { nlohmann::json &getConfig() { return config_; } + /** + * Get a configuration property from the json object. Returns an optional + * result which will be empty if the property does not exist or is not of + * the requested type. + */ + template <typename T> + std::optional<T> get(const std::string &name) { + try { + return config_[name].get<T>(); + } catch (...) { + return {}; + } + } + + /** + * Change a configuration property and trigger any listening event handlers + * for that property. Also triggers the global listeners. + */ + template <typename T> + void set(const std::string &name, T value) { + config_[name] = value; + _trigger(name); + } + + /** + * Add callback for whenever a specified property changes value. + * @param prop Name of property to watch + * @param callback A function object that will be called on change. + */ + void on(const std::string &prop, std::function<void(Configurable*, const std::string&)>); + protected: nlohmann::json config_; + + private: + std::map<std::string, std::list<std::function<void(Configurable*, const std::string&)>>> observers_; + + void _trigger(const std::string &name); + + static void __changeURI(const std::string &uri, Configurable *cfg); }; +/*template <> +void Configurable::set<const std::string&>(const std::string &name, const std::string &value) { + config_[name] = value; + if (name == "uri") __changeURI(value, this); + _trigger(name); +}*/ + } #endif // _FTL_CONFIGURABLE_HPP_ diff --git a/components/common/cpp/src/configurable.cpp b/components/common/cpp/src/configurable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cea4d74e6b3ef45d3485443e96458ce1e87f814a --- /dev/null +++ b/components/common/cpp/src/configurable.cpp @@ -0,0 +1,24 @@ +#include <ftl/configurable.hpp> + +using ftl::Configurable; +using std::string; +using std::map; +using std::list; +using std::function; + +void Configurable::_trigger(const string &name) { + auto ix = observers_.find(name); + if (ix != observers_.end()) { + for (auto &f : (*ix).second) { + try { + f(this, name); + } catch(...) { + LOG(ERROR) << "Exception in event handler for '" << name << "'"; + } + } + } +} + +void Configurable::__changeURI(const string &uri, Configurable *cfg) { + +} \ No newline at end of file