diff options
-rw-r--r-- | high-viwi-binding/high.cpp | 364 | ||||
-rw-r--r-- | high-viwi-binding/high.hpp | 33 |
2 files changed, 233 insertions, 164 deletions
diff --git a/high-viwi-binding/high.cpp b/high-viwi-binding/high.cpp index a7c00cc..e0bdc3f 100644 --- a/high-viwi-binding/high.cpp +++ b/high-viwi-binding/high.cpp @@ -52,166 +52,232 @@ High::High() { } +/// @brief Simply fill registeredObjects from loaded configuration. +/// +/// @param[in] uri - ViWi xObject uri +/// @param[in] properties - ViWi xObject properties retrieved +void High::registerObjects(const std::string& uri, const std::map<std::string, Property>& properties) +{ + registeredObjects[uri] = properties; +} + +/// @brief Handle definition JSON object parsing from provided +/// configuration. +/// +/// @param[in] definitions - pointer to the json_object that hold definitions +/// JSON object to be parsed. +std::map<std::string, std::map<std::string, Property>> High::loadDefinitions(json_object* definitions) const +{ + std::map<std::string, std::map<std::string, Property>> properties; + + int arraylen1 = json_object_array_length(definitions); + for(int n = 0; n < arraylen1; ++n) + { + json_object* obj, *jvalue, *jproperties; + obj = json_object_array_get_idx(definitions, n); + json_object_object_get_ex(obj, "name", &jvalue); + const std::string name = json_object_get_string(jvalue); + if(! wrap_json_unpack(obj, "{s:o}", "properties", &jproperties)) + { + std::map<std::string, Property> props; + json_object_object_foreach(jproperties, key, val) + { + Property p; + json_object_object_get_ex(val, "type", &jvalue); + p.type = json_object_get_string(jvalue); + json_object_object_get_ex(val, "description", &jvalue); + p.description = json_object_get_string(jvalue); + props[key] = p; + } + properties[name] = props; + } + } + + return properties; +} + +/// @brief Handle resources JSON object parsing from provided +/// configuration. +/// +/// @param[in] resources - pointer to the json_object that hold +/// resources, previously defined, JSON object to be parsed. +void High::loadResources(json_object* resources, const std::map<std::string, std::map<std::string, Property>>& properties) +{ + json_object* jarray1; + json_object_object_get_ex(resources, "resources", &jarray1); + int arraylen1 = json_object_array_length(jarray1); + std::map<std::string, int> toSubscribe; + for(int n = 0; n < arraylen1; ++n) + { + json_object* obj, *jvalue, *jarray2; + obj = json_object_array_get_idx(jarray1, n); + json_object_object_get_ex(obj, "name", &jvalue); + const std::string name = json_object_get_string(jvalue); + json_object_object_get_ex(obj, "values", &jarray2); + const int arraylen2 = json_object_array_length(jarray2); + for(int i = 0; i < arraylen2; ++i) + { + const std::string id = generateId(); + const std::string uri = name + id; + jvalue = json_object_array_get_idx(jarray2, i); + if(properties.find(name) == properties.end()) + { + AFB_WARNING("Unable to find name %s in properties", name.c_str()); + continue; + } + const std::map<std::string, Property> props = properties[name]; + std::map<std::string, Property> localProps; //note that local props can have less members than defined. + localProps["id"] = props.at("id"); + localProps["name"] = props.at("name"); + localProps["uri"] = props.at("uri"); + localProps["id"].value_string = std::string(id); + localProps["uri"].value_string = std::string(uri); + json_object_object_foreach(jvalue, key, val) + { + const std::string value = json_object_get_string(val); + if(props.find(key) == props.end()) + { + AFB_WARNING("Unable to find key %s in properties", value.c_str()); + continue; + } + Property prop = props.at(key); + if(startsWith(value, "${")) + { + const std::string canMessage = value.substr(2, value.size() - 1); + const std::vector<std::string> params = split(canMessage, ','); + if(params.size() != 2) + { + AFB_WARNING("Invalid CAN message definition %s", value.c_str()); + continue; + } + prop.lowMessageName = params.at(0); + prop.interval = stoi(params.at(1)); + if(toSubscribe.find(prop.lowMessageName) != toSubscribe.end()) + { + if(toSubscribe.at(prop.lowMessageName) > prop.interval) + toSubscribe[prop.lowMessageName] = prop.interval; + } else { + toSubscribe[prop.lowMessageName] = prop.interval; + } + switch (prop.type) + { + case "string": + prop.value_string = std::string("nul"); + break; + case "boolean": + prop.value_bool = false; + break; + case "double": + prop.value_double = 0.0; + break; + case "int": + prop.value_int = 0; + break; + default: + AFB_ERROR("ERROR 2! unexpected type in parseConfig %s %s", prop.description.c_str(), prop.type.c_str()); + break; + } + } else { + prop.value_string= std::string(value); + } + localProps[key] = prop; + } + + registerObjects(uri, localProps); +} + /// @brief Reads the json configuration and generates accordingly the resources container. An UID is generated for each resource. -/// Makes necessary subscriptions to low-level, eventually with a frequency. +/// Makes necessary subscriptions to low-level, eventually with a frequency. /// /// @param[in] confd - path to configuration directory which holds the binding configuration to load /// void High::parseConfigAndSubscribe(const std::string& confd) { - char* filename; - char* fullpath; - std::vector<std::string> conf_files_path; + char* filename; + char* fullpath; + std::vector<std::string> conf_files_path; - // Grab all config files with 'viwi' in their names in the path provided - struct json_object* conf_filesJ = ScanForConfig(confd.c_str(), CTL_SCAN_FLAT, "viwi", "json"); - if (!conf_filesJ || json_object_array_length(conf_filesJ) == 0) - { - AFB_ERROR("No JSON config files found in %s", confd.c_str()); - return; - } + // Grab all config files with 'viwi' in their names in the path provided + struct json_object* conf_filesJ = ScanForConfig(confd.c_str(), CTL_SCAN_FLAT, "viwi", "json"); + if (!conf_filesJ || json_object_array_length(conf_filesJ) == 0) + { + AFB_ERROR("No JSON config files found in %s", confd.c_str()); + return; + } - for(int i=0; i < json_object_array_length(conf_filesJ); i++) - { - json_object *entryJ=json_object_array_get_idx(conf_filesJ, i); + for(int i=0; i < json_object_array_length(conf_filesJ); i++) + { + json_object *entryJ=json_object_array_get_idx(conf_filesJ, i); - int err = wrap_json_unpack (entryJ, "{s:s, s:s !}", "fullpath", &fullpath,"filename", &filename); - if (err) { - AFB_ERROR ("OOOPs invalid config file path = %s", json_object_get_string(entryJ)); - return; - } - std::string filepath = fullpath; - filepath += filename; - conf_files_path.push_back(filepath); - } + int err = wrap_json_unpack (entryJ, "{s:s, s:s !}", "fullpath", &fullpath,"filename", &filename); + if (err) { + AFB_ERROR ("OOOPs invalid config file path = %s", json_object_get_string(entryJ)); + return; + } + std::string filepath = fullpath; + filepath += filename; + conf_files_path.push_back(filepath); + } - json_object *config = json_object_from_file("high.json"); - json_object *jvalue, *jarray1, *jarray2, *obj; - std::map<std::string, std::map<std::string, Property>> properties; + json_object *jarray1, *jarray2; + int i = 0; + std::map<std::string, std::map<std::string, Property>> properties; + while(properties.empty() || i < conf_files_path.size()) + { + json_object *config = json_object_from_file(conf_files_path[i]); + if(! wrap_json_unpack(config, "{s:o}", "definition", &jarray1)) + { + properties = loadDefinitions(jarray1); + conf_files_path.erase(i); + } + i++; + } - json_object_object_get_ex(config, "definitions", &jarray1); - int arraylen1 = json_object_array_length(jarray1); - for(int n = 0; n < arraylen1; ++n) - { - obj = json_object_array_get_idx(jarray1, n); - json_object_object_get_ex(obj, "name", &jvalue); - const std::string name = json_object_get_string(jvalue); - json_object_object_get_ex(obj, "properties", &jarray2); - std::map<std::string, Property> props; - json_object_object_foreach(jarray2, key, val) - { - Property p; - json_object_object_get_ex(val, "type", &jvalue); - p.type = json_object_get_string(jvalue); - json_object_object_get_ex(val, "description", &jvalue); - p.description = json_object_get_string(jvalue); - props[key] = p; - } - properties[name] = props; - } - json_object_object_get_ex(config, "resources", &jarray1); - arraylen1 = json_object_array_length(jarray1); - std::map<std::string, int> toSubscribe; - for(int n = 0; n < arraylen1; ++n) - { - obj = json_object_array_get_idx(jarray1, n); - json_object_object_get_ex(obj, "name", &jvalue); - const std::string name = json_object_get_string(jvalue); - json_object_object_get_ex(obj, "values", &jarray2); - const int arraylen2 = json_object_array_length(jarray2); - for(int i = 0; i < arraylen2; ++i) - { - const std::string id = generateId(); - const std::string uri = name + id; - jvalue = json_object_array_get_idx(jarray2, i); - if(properties.find(name) == properties.end()) - { - AFB_WARNING("Unable to find name %s in properties", name.c_str()); - continue; - } - const std::map<std::string, Property> props = properties[name]; - std::map<std::string, Property> localProps; //note that local props can have less members than defined. - localProps["id"] = props.at("id"); - localProps["name"] = props.at("name"); - localProps["uri"] = props.at("uri"); - localProps["id"].value_string = std::string(id); - localProps["uri"].value_string = std::string(uri); - json_object_object_foreach(jvalue, key, val) - { - const std::string value = json_object_get_string(val); - if(props.find(key) == props.end()) - { - AFB_WARNING("Unable to find key %s in properties", value.c_str()); - continue; - } - Property prop = props.at(key); - if(startsWith(value, "${")) - { - const std::string canMessage = value.substr(2, value.size() - 1); - const std::vector<std::string> params = split(canMessage, ','); - if(params.size() != 2) - { - AFB_WARNING("Invalid CAN message definition %s", value.c_str()); - continue; - } - prop.lowMessageName = params.at(0); - prop.interval = stoi(params.at(1)); - if(toSubscribe.find(prop.lowMessageName) != toSubscribe.end()) - { - if(toSubscribe.at(prop.lowMessageName) > prop.interval) - toSubscribe[prop.lowMessageName] = prop.interval; - } else { - toSubscribe[prop.lowMessageName] = prop.interval; - } - if(prop.type == "string") - prop.value_string = std::string("nul"); - else if(prop.type == "boolean") - prop.value_bool = false; - else if(prop.type == "double") - prop.value_double = 0.0; - else if(prop.type == "int") - prop.value_int = 0; - else - AFB_ERROR("ERROR 2! unexpected type in parseConfig %s %s", prop.description.c_str(), prop.type.c_str()); - } else { - prop.value_string= std::string(value); - } - localProps[key] = prop; - } - registeredObjects[uri] = localProps; - for(const auto &p : localProps) - { - if(p.second.lowMessageName.size() > 0) - { - std::set<std::string> objectList; - if(lowMessagesToObjects.find(p.second.lowMessageName) != lowMessagesToObjects.end()) - objectList = lowMessagesToObjects.at(p.second.lowMessageName); - objectList.insert(uri); - lowMessagesToObjects[p.second.lowMessageName] = objectList; - } - } - } - } - for(const auto &p : toSubscribe) - { - json_object *jobj = json_object_new_object(); - json_object_object_add(jobj,"event", json_object_new_string(p.first.c_str())); - if(p.second > 0) - { - json_object *filter = json_object_new_object(); - json_object_object_add(filter, "frequency", json_object_new_double(1000.0 / (double)p.second)); - json_object_object_add(jobj, "filter", filter); - } - json_object *dummy; - const std::string js = json_object_get_string(jobj); - if(afb_service_call_sync("low-can", "subscribe", jobj, &dummy) < 0) - AFB_ERROR("high-can subscription to low-can FAILED %s", js.c_str()); - else - AFB_NOTICE("high-can subscribed to low-can %s", js.c_str()); - json_object_put(dummy); - } - json_object_put(config); - AFB_NOTICE("configuration loaded"); + // Search for resources JSON node to load them if + // definition has been found. + if(! properties.empty()) + { + for(const std::string& filepath: conf_files_path) + { + if(! wrap_json_unpack(config, "{s:o}", "resources", &jarray2)) + { + loadResources(jarray2, properties); + } + } + } + for(const auto &p : localProps) + { + if(p.second.lowMessageName.size() > 0) + { + std::set<std::string> objectList; + if(lowMessagesToObjects.find(p.second.lowMessageName) != lowMessagesToObjects.end()) + objectList = lowMessagesToObjects.at(p.second.lowMessageName); + objectList.insert(uri); + lowMessagesToObjects[p.second.lowMessageName] = objectList; + } + } + } + } + for(const auto &p : toSubscribe) + { + json_object *jobj = json_object_new_object(); + json_object_object_add(jobj,"event", json_object_new_string(p.first.c_str())); + if(p.second > 0) + { + json_object *filter = json_object_new_object(); + json_object_object_add(filter, "frequency", json_object_new_double(1000.0 / (double)p.second)); + json_object_object_add(jobj, "filter", filter); + } + json_object *dummy; + const std::string js = json_object_get_string(jobj); + if(afb_service_call_sync("low-can", "subscribe", jobj, &dummy) < 0) + AFB_ERROR("high-can subscription to low-can FAILED %s", js.c_str()); + else + AFB_NOTICE("high-can subscribed to low-can %s", js.c_str()); + json_object_put(dummy); + } + json_object_put(config); + AFB_NOTICE("configuration loaded"); } /// @brief Create and start a systemD timer. Only one timer is created per frequency. diff --git a/high-viwi-binding/high.hpp b/high-viwi-binding/high.hpp index 525f9f9..d021ce7 100644 --- a/high-viwi-binding/high.hpp +++ b/high-viwi-binding/high.hpp @@ -19,19 +19,21 @@ struct TimedEvent { std::string name; std::string eventName; }; + struct Property { - /** - * alternatively, instead of a value per type, we could use boost::any, or in c++17 variant. - */ - std::string type; - std::string description; - std::string lowMessageName; - int interval; - bool value_bool; - std::string value_string; - double value_double; - int value_int; - Property() { interval = 0; value_bool = false; value_double = 0.0; value_int = 0;} + /** + * alternatively, instead of a value per type, we could use boost::any, or in c++17 variant. + */ + std::string type; + std::string description; + std::string lowMessageName; + int interval; + int value_int; + bool value_bool; + double value_double; + std::string value_string; + + Property() { interval = 0; value_int = 0; value_bool = false; value_double = 0.0;} }; class High @@ -45,11 +47,9 @@ public: void tick(sd_event_source *source, const long &now, void *interv); void startTimer(const int &t); ~High(); - void parseConfigAndSubscribe(const std::string& confd); - void loadDefinition(const json_object* definitionsJ); - void loadResources(const json_object* resourcesJ); static bool startsWith(const std::string &s, const std::string &val); static void callBackFromSubscribe(void *handle, int iserror, json_object *result); + void parseConfigAndSubscribe(const std::string& confd); private: std::map<std::string, afb_event> events; std::map<int, std::vector<TimedEvent>> timedEvents; @@ -58,4 +58,7 @@ private: std::set<int> timers; std::string generateId() const; json_object *generateJson(const std::string &messageObject, std::vector<std::string> *fields = nullptr); + void registerObjects(const std::string& uri, const std::map<std::string, Property>& properties); + std::map<std::string, std::map<std::string, Property>> loadDefinitions(json_object* definitionsJ) const; + void loadResources(json_object* resourcesJ); }; |