diff options
-rw-r--r-- | conf.d/project/etc/sig-demoboard.json | 16 | ||||
-rw-r--r-- | conf.d/project/etc/sig-doors.json | 65 | ||||
-rw-r--r-- | conf.d/project/etc/sig-geoloc.json | 105 | ||||
-rw-r--r-- | conf.d/project/etc/sources.json | 25 | ||||
-rw-r--r-- | plugins/low-can.cpp | 51 | ||||
-rw-r--r-- | signal-composer-binding/signal-composer-binding.cpp | 20 | ||||
-rw-r--r-- | signal-composer-binding/signal-composer.cpp | 184 | ||||
-rw-r--r-- | signal-composer-binding/signal-composer.hpp | 11 | ||||
-rw-r--r-- | signal-composer-binding/signal.cpp | 64 | ||||
-rw-r--r-- | signal-composer-binding/signal.hpp | 8 | ||||
-rw-r--r-- | signal-composer-binding/source.cpp | 42 | ||||
-rw-r--r-- | signal-composer-binding/source.hpp | 8 |
12 files changed, 368 insertions, 231 deletions
diff --git a/conf.d/project/etc/sig-demoboard.json b/conf.d/project/etc/sig-demoboard.json index c078d27..3d03f3e 100644 --- a/conf.d/project/etc/sig-demoboard.json +++ b/conf.d/project/etc/sig-demoboard.json @@ -1,11 +1,12 @@ { "signals": [ { - "label": "low-can/messages.vehicle.speed", + "id": "vehicle_speed", + "event": "low-can/messages.vehicle.speed", "class": "temporal", "unit": "km/h", "frequency": 1, - "actions": { + "onReceived": { "label": "Unit converter", "lua": "_Unit_Converter", "args": { @@ -15,22 +16,25 @@ } }, { - "name": "low-can/diagnostic_messages.engine.speed", + "id": "engine_speed", + "event": "low-can/diagnostic_messages.engine.speed", "class": "temporal", "unit": "rpm", "frequency": 1 }, { - "name": "low-can/diagnostic_messages.fuel.level", + "id": "fuel_level", + "event": "low-can/diagnostic_messages.fuel.level", "class":"temporal", "unit": "litre", "frequency": 1 }, { - "name": "low-can/diagnostic_messages.engine.load", + "id": "engine_load", + "event": "low-can/diagnostic_messages.engine.load", "class":"temporal", "unit": "Nm", - "interval": 1 + "frequency": 1 } ] } diff --git a/conf.d/project/etc/sig-doors.json b/conf.d/project/etc/sig-doors.json index ea191e4..1d23f9a 100644 --- a/conf.d/project/etc/sig-doors.json +++ b/conf.d/project/etc/sig-doors.json @@ -2,10 +2,9 @@ "signals": [ { "id": "rear_left_window", - "source": "low-can/messages.windows.rear_left.open", + "event": "low-can/messages.windows.rear_left.open", "onReceived": { - "plugin": "low-can-callbacks", - "function": "isOpen", + "function": "plugin://low-can-callbacks/isOpen", "args": { "ojoi": "pok" } @@ -13,10 +12,9 @@ }, { "id": "rear_left_door", - "source": "low-can/messages.doors.rear_left.open", + "event": "low-can/messages.doors.rear_left.open", "onReceived": { - "plugin": "low-can-callbacks", - "function": "isOpen", + "function": "plugin://low-can-callbacks/isOpen", "args": { "ojoi": "pok" } @@ -24,10 +22,9 @@ }, { "id": "rear_right_window", - "source": "low-can/messages.windows.rear_right.open", + "event": "low-can/messages.windows.rear_right.open", "onReceived": { - "plugin": "low-can-callbacks", - "function": "isOpen", + "function": "plugin://low-can-callbacks/isOpen", "args": { "ojoi": "pok" } @@ -35,10 +32,9 @@ }, { "id": "rear_right_door", - "source": "low-can/messages.doors.rear_right.open", + "event": "low-can/messages.doors.rear_right.open", "onReceived": { - "plugin": "low-can-callbacks", - "function": "isOpen", + "function": "plugin://low-can-callbacks/isOpen", "args": { "ojoi": "pok" } @@ -46,10 +42,9 @@ }, { "id": "front_left_window", - "source": "low-can/messages.windows.front_left.open", + "event": "low-can/messages.windows.front_left.open", "onReceived": { - "plugin": "low-can-callbacks", - "function": "isOpen", + "function": "plugin://low-can-callbacks/isOpen", "args": { "ojoi": "pok" } @@ -57,10 +52,9 @@ }, { "id": "front_left_door", - "source": "low-can/messages.doors.front_left.open", + "event": "low-can/messages.doors.front_left.open", "onReceived": { - "plugin": "low-can-callbacks", - "function": "isOpen", + "function": "plugin://low-can-callbacks/isOpen", "args": { "ojoi": "pok" } @@ -68,10 +62,9 @@ }, { "id": "front_right_window", - "source": "low-can/messages.windows.front_right.open", + "event": "low-can/messages.windows.front_right.open", "onReceived": { - "plugin": "low-can-callbacks", - "function": "isOpen", + "function": "plugin://low-can-callbacks/isOpen", "args": { "ojoi": "pok" } @@ -79,10 +72,9 @@ }, { "id": "front_right_door", - "source": "low-can/messages.doors.front_right.open", + "event": "low-can/messages.doors.front_right.open", "onReceived": { - "plugin": "low-can-callbacks", - "function": "isOpen", + "function": "plugin://low-can-callbacks/isOpen", "args": { "ojoi": "pok" } @@ -90,13 +82,12 @@ }, { "id": "rear_left", - "source": [ + "depends": [ "rear_left_window", "rear_left_door" ], "onReceived": { - "plugin": "low-can-callbacks", - "function": "isOpen", + "function": "plugin://low-can-callbacks/isOpen", "args": { "ojoi": "pok" } @@ -104,43 +95,40 @@ }, { "id": "rear_right", - "source": [ + "depends": [ "rear_right_door", "rear_right_window" ], "onReceived": { - "plugin": "low-can-callbacks", - "function": "isOpen", + "function": "plugin://low-can-callbacks/isOpen", "args": {} } }, { "id": "front_left", - "source": [ + "depends": [ "front_left_door", "front_left_window" ], "onReceived": { - "plugin": "low-can-callbacks", - "function": "isOpen", + "function": "plugin://low-can-callbacks/isOpen", "args": {} } }, { "id": "front_right", - "source": [ + "depends": [ "front_right_door", "front_right_window" ], "onReceived": { - "plugin": "low-can-callbacks", - "function": "isOpen", + "function": "plugin://low-can-callbacks/isOpen", "args": {} } }, { "id": "all_doors", - "source": [ + "depends": [ "front_left_door", "front_left_window", "front_right_door", @@ -151,8 +139,7 @@ "rear_right_window" ], "onReceived": { - "plugin": "low-can-callbacks", - "function": "isOpen", + "function": "plugin://low-can-callbacks/isOpen", "args": { "evtname": "doors.open" } diff --git a/conf.d/project/etc/sig-geoloc.json b/conf.d/project/etc/sig-geoloc.json index 7fc4d29..3b0e4eb 100644 --- a/conf.d/project/etc/sig-geoloc.json +++ b/conf.d/project/etc/sig-geoloc.json @@ -1,136 +1,155 @@ { "signals": [ { - "label": "gps/latitude", - "class": "state", + "id": "latitude", + "event": "gps/location", "unit": "degree", - "frequency": 1 + "frequency": 1, + "onReceived": { + "function": "plugin://gps-callbacks/getLatitude" + } }, { - "label": "gps/longitude", - "class": "state", + "id": "longitude", + "event": "gps/location", "unit": "degree", - "frequency": 1 + "frequency": 1, + "onReceived": { + "function": "plugin://gps-callbacks/getSignal", + "args": { + "what": "longitude" + } + } }, { - "label": "gps/altitude", - "class": "state", + "id": "altitude", + "event": "gps/location", "unit": "meter", "frequency": 1 }, { - "label": "heading", + "id": "heading", + "depends": [ + "latitude", + "longitude" + ], "class": "state", "unit": "degree", "frequency": 1, - "dependsOn": [ - "gps/latitude", - "gps/longitude" - ], "actions": { - "label": "Heading calculation", + "id": "Heading calculation", "lua": "_Heading", "args": {} } }, { - "label": "gps/speed", + "id": "speed", + "event": "gps/location", "class": "temporal", "unit": "m/s", "frequency": 1 }, { - "label": "mraa/climb", - "class": "state", + "id": "climb", + "event": "mraa/getclimb", "unit": "degree", "frequency": 1 }, { - "label": "mraa/roll_rate", - "class": "state", + "id": "roll_rate", + "event": "mraa/gyroscope", "unit": "degree/s", "frequency": 1 }, { - "label": "mraa/roll_rate", - "class": "state", + "id": "pitch_rate", + "event": "mraa/gyroscope", "unit": "degree/s", "frequency": 1 }, { - "label": "mraa/roll_rate", - "class": "state", + "id": "yaw_rate", + "event": "gps/gyroscope", "unit": "degree/s", "frequency": 1 }, { - "label": "gps/pdop", + "id": "pdop", + "event": "gps/location", "frequency": 1 }, { - "label": "gps/hdop", + "id": "hdop", + "event": "gps/location", "frequency": 1 }, { - "label": "gps/vdop", + "id": "vdop", + "event": "gps/location", "frequency": 1 }, { - "label": "gps/used_satellites", + "id": "used_satellites", + "event": "gps/location", "frequency": 1 }, { - "label": "gps/tracked_satellites", + "id": "tracked_satellites", + "event": "gps/location", "frequency": 1 }, { - "label": "gps/visible_satellites", + "id": "visible_satellites", + "event": "gps/location", "frequency": 1 }, { - "label": "gps/sigma_hposition", - "class": "state", + "id": "sigma_hposition", + "event": "gps/location", "unit": "meter", "frequency": 1 }, { - "label": "gps/sigma_altitude", - "class": "state", + "id": "sigma_altitude", + "event": "gps/location", "unit": "meter", "frequency": 1 }, { - "label": "gps/sigma_heading", - "class": "state", + "id": "sigma_heading", + "event": "gps/location", "unit": "degree", "frequency": 1 }, { - "label": "gps/sigma_speed", + "id": "sigma_speed", + "event": "gps/location", "class": "temporal", "unit": "m/s", "frequency": 1 }, { - "label": "gps/sigma_climb", - "class": "state", + "id": "sigma_climb", + "event": "gps/location", "unit": "degree", "frequency": 1 }, { - "label": "gps/gnss_fix_status", - "class": "state", + "id": "gnss_fix_status", + "event": "gps/location", "frequency": 1 }, { - "label": "gps/dr_status", + "id": "dr_status", + "event": "gps/location", "class": "state", "frequency": 1 }, { - "label": "gps/reliabilty_index", + "id": "reliabilty_index", + "event": "gps/location", "class": "temporal", "frequency": 1 } ] -}
\ No newline at end of file +} diff --git a/conf.d/project/etc/sources.json b/conf.d/project/etc/sources.json index 6a9657a..be99a41 100644 --- a/conf.d/project/etc/sources.json +++ b/conf.d/project/etc/sources.json @@ -4,14 +4,13 @@ "api": "low-can", "info": "Low level binding to handle CAN bus communications", "init": { - "function": "low-can/subscribe", + "function": "api://low-can/subscribe", "args": { "event": "message*" } - }, - "getSignal": { - "plugin": "low-can-callbacks", - "function": "subscribeToLow", + }, + "getSignals": { + "function": "plugin://low-can-callbacks/subscribeToLow", "args": { "arg": "first argument" } @@ -20,13 +19,13 @@ "api": "gps", "info": "Low level binding which retrieve Satellite positionning values", "init": { - "function": "_LUA_Simple_Echo_Args", + "function": "api://gps/location", "args": { "arg1": "first argument" } - }, - "getSignal": { - "function": "_LUA_Simple_Echo_Args", + }, + "getSignals": { + "function": "lua://_Simple_Echo_Args", "args": { "arg": "first argument" } @@ -35,13 +34,13 @@ "api": "mraa", "info": "Low level binding which retrieve different values from several sensors like gyroscope, accelerometer, etc", "init": { - "function": "_LUA_Simple_Echo_Args", + "function": "lua://_Simple_Echo_Args", "args": { "arg1": "first argument" } - }, - "getSignal": { - "function": "_LUA_Simple_Echo_Args", + }, + "getSignals": { + "function": "lua://_Simple_Echo_Args", "args": { "arg": "first argument" } diff --git a/plugins/low-can.cpp b/plugins/low-can.cpp index 1f528a5..8b573ba 100644 --- a/plugins/low-can.cpp +++ b/plugins/low-can.cpp @@ -77,15 +77,18 @@ CTLP_ONLOAD(plugin, bAppHandle) } CTLP_CAPI (subscribeToLow, source, argsJ, eventJ, context) { - json_object* signalArrayJ = NULL, *subscribeArgsJ = NULL, *subscribeFilterJ = NULL, *responseJ = NULL; - const char* unit = NULL; + json_object* dependsArrayJ = nullptr, *subscribeArgsJ = nullptr, *subscribeFilterJ = nullptr, *responseJ = nullptr, *filterJ = nullptr; + const char *id = nullptr, *event = nullptr, *unit = nullptr; double frequency = 0; int err = 0; - err = wrap_json_unpack(eventJ, "{so,s?s,s?F !}", - "signal", &signalArrayJ, + err = wrap_json_unpack(eventJ, "{ss,s?s,s?o,s?s,s?F,s?o !}", + "id", &id, + "event", &event, + "depends", &dependsArrayJ, "unit", &unit, - "frequency", &frequency); + "frequency", &frequency, + "getSignalsArgs", &filterJ); if(err) { AFB_ERROR("Problem to unpack JSON object eventJ: %s", @@ -93,29 +96,27 @@ CTLP_CAPI (subscribeToLow, source, argsJ, eventJ, context) { return err; } - if(frequency > 0) + if(frequency > 0 && !filterJ) + {wrap_json_pack(&subscribeFilterJ, "{sf}", "frequency", frequency);} + else + {subscribeFilterJ = filterJ;} + + std::string eventStr = std::string(event); + std::string lowEvent = eventStr.substr(eventStr.find("/")+1); + err = wrap_json_pack(&subscribeArgsJ, "{ss, so*}", + "event", lowEvent.c_str(), + "filter", subscribeFilterJ); + if(err) { - wrap_json_pack(&subscribeFilterJ, "{sf}", "frequency", frequency); + AFB_ERROR("Error building subscription query object"); + return err; } - - for (int idx = 0; idx < json_object_array_length(signalArrayJ); idx++) + AFB_DEBUG("Calling subscribe with %s", json_object_to_json_string_ext(subscribeArgsJ, JSON_C_TO_STRING_PRETTY)); + err = afb_service_call_sync("low-can", "subscribe", subscribeArgsJ, &responseJ); + if(err) { - json_object* aSignalJ = json_object_array_get_idx(signalArrayJ, idx); - err = wrap_json_pack(&subscribeArgsJ, "{ss, so*}", - "event", json_object_get_string(aSignalJ), - "filter", subscribeFilterJ); - if(err) - { - AFB_ERROR("Error building subscription query object"); - return err; - } - AFB_DEBUG("Calling subscribe with %s", json_object_to_json_string_ext(subscribeArgsJ, JSON_C_TO_STRING_PRETTY)); - err = afb_service_call_sync("low-can", "subscribe", subscribeArgsJ, &responseJ); - if(err) - { - AFB_ERROR("Can't find api 'low-can'"); - return err; - } + AFB_ERROR("Can't find api 'low-can'"); + return err; } return err; diff --git a/signal-composer-binding/signal-composer-binding.cpp b/signal-composer-binding/signal-composer-binding.cpp index c70941a..4f12150 100644 --- a/signal-composer-binding/signal-composer-binding.cpp +++ b/signal-composer-binding/signal-composer-binding.cpp @@ -39,6 +39,7 @@ void onEvent(const char *event, json_object *object) sig->onReceivedCB(object); } } + /// @brief entry point for client subscription request. void subscribe(afb_req request) { @@ -60,14 +61,19 @@ void unsubscribe(afb_req request) /// @brief verb that loads JSON configuration (old SigComp.json file now) void loadConf(afb_req request) { - json_object* args = afb_req_json(request); - const char* confd; + json_object* args = afb_req_json(request), *fileJ; + const char* filepath; - wrap_json_unpack(args, "{s:s}", "path", &confd); - if(true) - afb_req_success(request, NULL, NULL); + wrap_json_unpack(args, "{s:s}", "filepath", &filepath); + fileJ = json_object_from_file(filepath); + + if(bindingApp::instance().loadSignals(fileJ)) + {afb_req_fail_f(request, "Loading configuration or subscription error", "Error code: -1");} else - afb_req_fail_f(request, "Loading configuration or subscription error", "Error code: -1"); + { + + afb_req_success(request, NULL, NULL); + } } /// @brief entry point to list available signals @@ -79,7 +85,7 @@ void list(afb_req request) for(auto& sig: allSignals) {json_object_array_add(allSignalsJ, sig->toJSON());} - if(json_object_array_length(allSignalsJ)) + if(json_object_array_length(allSignalsJ) && !execConf()) { afb_req_success(request, allSignalsJ, NULL); } diff --git a/signal-composer-binding/signal-composer.cpp b/signal-composer-binding/signal-composer.cpp index b29df22..151de6c 100644 --- a/signal-composer-binding/signal-composer.cpp +++ b/signal-composer-binding/signal-composer.cpp @@ -16,6 +16,7 @@ */ #include <string.h> +#include <fnmatch.h> #include "signal-composer.hpp" @@ -26,15 +27,23 @@ extern "C" void setSignalValueHandle(const char* aName, long long int timestamp, {sig->set(timestamp, value);} } +bool startsWith(const std::string& str, const std::string& pattern) +{ + size_t sep; + if( (sep = str.find(pattern)) != std::string::npos && !sep) + {return true;} + return false; +} + static struct pluginCBT pluginHandle = { .setSignalValue = setSignalValueHandle, }; CtlSectionT bindingApp::ctlSections_[] = { - [0]={.key="plugins" ,.label = "plugins", .info=nullptr, + [0]={.key="plugins" , .label = "plugins", .info=nullptr, .loadCB=PluginConfig, .handle=&pluginHandle}, - [1]={.key="sources" ,.label = "sources", .info=nullptr, + [1]={.key="sources" , .label = "sources", .info=nullptr, .loadCB=loadSourcesAPI, .handle=nullptr}, [2]={.key="signals" , .label= "signals", .info=nullptr, @@ -68,7 +77,7 @@ bindingApp& bindingApp::instance() SourceAPI* bindingApp::getSourceAPI(const std::string& api) { - for(auto& source: sourcesList_) + for(auto& source: sourcesListV_) { if (source.api() == api) {return &source;} @@ -76,6 +85,27 @@ SourceAPI* bindingApp::getSourceAPI(const std::string& api) return nullptr; } +std::vector<std::string> bindingApp::parseURI(const std::string& uri) +{ + std::vector<std::string> uriV; + std::string delimiters = "/"; + + std::string::size_type start = 0; + auto pos = uri.find_first_of(delimiters, start); + while(pos != std::string::npos) + { + if(pos != start) // ignore empty tokens + uriV.emplace_back(uri, start, pos - start); + start = pos + 1; + pos = uri.find_first_of(delimiters, start); + } + + if(start < uri.length()) // ignore trailing delimiter + uriV.emplace_back(uri, start, uri.length() - start); // add what's left of the string + + return uriV; +} + CtlActionT* bindingApp::convert2Action(const std::string& name, json_object* actionJ) { json_object *functionArgsJ = nullptr, *action = nullptr; @@ -88,35 +118,52 @@ CtlActionT* bindingApp::convert2Action(const std::string& name, json_object* act "args", &functionArgsJ)) { action = nullptr; - if(::strcasestr(function, "lua")) + if(startsWith(function, "lua://")) { + std::string fName = std::string(function).substr(6); wrap_json_pack(&action, "{ss,ss,so*}", "label", name.c_str(), - "lua", function, + "lua", fName, "args", functionArgsJ); } - else if(strstr(function, "/")) + else if(startsWith(function, "api://")) { - const char* api = strsep(&function, "/"); - //std::string api = functionS.substr(0, sep); - //std::string verb = functionS.substr(sep); + std::string uri = std::string(function).substr(6); + std::vector<std::string> uriV = bindingApp::parseURI(uri); + if(uriV.size() != 2) + { + AFB_ERROR("Miss something in uri either plugin name or function name. Uri has to be like: api://<plugin-name>/<function-name>"); + return nullptr; + } wrap_json_pack(&action, "{ss,ss,ss,so*}", "label", name.c_str(), - "api", api, - "verb", function, + "api", uriV[0].c_str(), + "verb", uriV[1].c_str(), "args", functionArgsJ); } - else + else if(startsWith(function, "plugin://")) { + std::string uri = std::string(function).substr(9); + std::vector<std::string> uriV = bindingApp::parseURI(uri); + if(uriV.size() != 2) + { + AFB_ERROR("Miss something in uri either plugin name or function name. Uri has to be like: plugin://<plugin-name>/<function-name>"); + return nullptr; + } json_object *callbackJ = nullptr; wrap_json_pack(&callbackJ, "{ss,ss,so*}", - "plugin", plugin, - "function", function, + "plugin", uriV[0].c_str(), + "function", uriV[1].c_str(), "args", functionArgsJ); wrap_json_pack(&action, "{ss,so}", "label", name.c_str(), "callback", callbackJ); } + else + { + AFB_ERROR("Wrong function uri specified. You have to specified 'lua://', 'plugin://' or 'api://'. (%s)", function); + return nullptr; + } } if(action) {return ActionLoad(action);} return nullptr; @@ -124,18 +171,18 @@ CtlActionT* bindingApp::convert2Action(const std::string& name, json_object* act int bindingApp::loadOneSourceAPI(json_object* sourceJ) { - json_object *initJ = nullptr, *getSignalJ = nullptr; - CtlActionT *initCtl = nullptr, *getSignalCtl = nullptr; + json_object *initJ = nullptr, *getSignalsJ = nullptr; + CtlActionT *initCtl = nullptr, *getSignalsCtl = nullptr; const char *api, *info; int err = wrap_json_unpack(sourceJ, "{ss,s?s,s?o,s?o !}", "api", &api, "info", &info, "init", &initJ, - "getSignal", &getSignalJ); + "getSignals", &getSignalsJ); if (err) { - AFB_ERROR("Missing something api|[info]|[init]|[getSignal] in %s", json_object_get_string(sourceJ)); + AFB_ERROR("Missing something api|[info]|[init]|[getSignals] in %s", json_object_get_string(sourceJ)); return err; } @@ -147,9 +194,9 @@ int bindingApp::loadOneSourceAPI(json_object* sourceJ) } if(initJ) {initCtl = convert2Action("init", initJ);} - if(getSignalJ) {getSignalCtl = convert2Action("getSignal", getSignalJ);} + if(getSignalsJ) {getSignalsCtl = convert2Action("getSignals", getSignalsJ);} - sourcesList_.push_back(SourceAPI(api, info, initCtl, getSignalCtl)); + sourcesListV_.push_back(SourceAPI(api, info, initCtl, getSignalsCtl)); return err; } @@ -192,64 +239,96 @@ int bindingApp::loadSourcesAPI(CtlSectionT* section, json_object *sourcesJ) int bindingApp::loadOneSignal(json_object* signalJ) { - json_object *onReceivedJ = nullptr, *sourcesJ = nullptr; + json_object *onReceivedJ = nullptr, *dependsJ = nullptr, *getSignalsArgs = nullptr; CtlActionT* onReceivedCtl; const char *id = nullptr, + *event = nullptr, *sClass = nullptr, *unit = nullptr; - double frequency; + double frequency=0.0; std::string api; - std::vector<std::string> sourcesV; + std::vector<std::string> dependsV; ssize_t sep; - int err = wrap_json_unpack(signalJ, "{ss,so,s?s,s?s,s?F,s?o !}", + int err = wrap_json_unpack(signalJ, "{ss,s?s,s?o,s?o,s?s,s?s,s?F,s?o !}", "id", &id, - "source", &sourcesJ, + "event", &event, + "depends", &dependsJ, + "getSignalsArgs", &getSignalsArgs, "class", &sClass, "unit", &unit, "frequency", &frequency, "onReceived", &onReceivedJ); if (err) { - AFB_ERROR("Missing something id|source|[class]|[unit]|[frequency]|[onReceived] in %s", json_object_get_string(signalJ)); + AFB_ERROR("Missing something id|[event|depends]|[getSignalsArgs]|[class]|[unit]|[frequency]|[onReceived] in %s", json_object_get_string(signalJ)); return err; } - // Process sources JSON object - if (json_object_get_type(sourcesJ) == json_type_array) + // Set default sClass is not specified + sClass = !sClass ? "state" : sClass; + unit = !unit ? "" : unit; + + // Get an action handler + onReceivedCtl = onReceivedJ ? convert2Action("onReceived", onReceivedJ) : nullptr; + + // event or depends field manadatory + if( (!event && !dependsJ) || (event && dependsJ) ) + { + AFB_ERROR("Missing something id|[event|depends]|[getSignalsArgs]|[class]|[unit]|[frequency]|[onReceived] in %s. Or you declare event AND depends, only one of them is needed.", json_object_get_string(signalJ)); + return -1; + } + + // Process depends JSON object to declare virtual signal dependencies + if (dependsJ) { - int count = json_object_array_length(sourcesJ); - for(int i = 0; i < count; i++) + if(json_object_get_type(dependsJ) == json_type_array) + { + int count = json_object_array_length(dependsJ); + for(int i = 0; i < count; i++) + { + std::string sourceStr = json_object_get_string(json_object_array_get_idx(dependsJ, i)); + if( (sep = sourceStr.find("/")) != std::string::npos) + { + AFB_ERROR("Signal composition needs to use signal 'id', don't use full low level signal name"); + return -1; + } + dependsV.push_back(sourceStr); + } + api = sourcesListV_.rbegin()->api(); + } + else { - std::string sourceStr = json_object_get_string(json_object_array_get_idx(sourcesJ, i)); + std::string sourceStr = json_object_get_string(dependsJ); if( (sep = sourceStr.find("/")) != std::string::npos) { AFB_ERROR("Signal composition needs to use signal 'id', don't use full low level signal name"); return -1; } - sourcesV.push_back(sourceStr); + dependsV.push_back(sourceStr); + api = sourcesListV_.rbegin()->api(); } } - else + + // Declare a raw signal + if(event) { - std::string sourceStr = json_object_get_string(sourcesJ); - if( (sep = sourceStr.find("/")) != std::string::npos) + std::string eventStr = std::string(event); + if( (sep = eventStr.find("/")) == std::string::npos) { - api = sourceStr.substr(0, sep); + AFB_ERROR("Missing something in event declaration. Has to be like: <api>/<event>"); + return -1; } - sourcesV.push_back(sourceStr); + api = eventStr.substr(0, sep); + } + else + { + event = ""; } - - // Set default sClass is not specified - sClass = !sClass ? "state" : sClass; - unit = !unit ? "" : unit; - - // Get an action handler - onReceivedCtl = convert2Action("onReceived", onReceivedJ); SourceAPI* src = getSourceAPI(api) ? getSourceAPI(api):getSourceAPI("signal-composer"); - if( src != nullptr) - {src->addSignal(id, sourcesV, sClass, unit, frequency, onReceivedCtl);} + if(src != nullptr) + {src->addSignal(id, event, dependsV, sClass, unit, frequency, onReceivedCtl, getSignalsArgs);} else {err = -1;} @@ -281,6 +360,11 @@ int bindingApp::loadSignals(CtlSectionT* section, json_object *signalsJ) return err; } +int bindingApp::loadSignals(json_object* signalsJ) +{ + return loadSignals(nullptr, signalsJ); +} + std::shared_ptr<Signal> bindingApp::searchSignal(const std::string& aName) { std::string api; @@ -306,7 +390,7 @@ std::shared_ptr<Signal> bindingApp::searchSignal(const std::string& aName) std::vector<std::shared_ptr<Signal>> bindingApp::getAllSignals() { std::vector<std::shared_ptr<Signal>> allSignals; - for( auto& source : sourcesList_) + for( auto& source : sourcesListV_) { std::vector<std::shared_ptr<Signal>> srcSignals = source.getSignals(); allSignals.insert(allSignals.end(), srcSignals.begin(), srcSignals.end()); @@ -320,14 +404,14 @@ CtlConfigT* bindingApp::ctlConfig() return ctlConfig_; } -int bindingApp::execSubscription() const +int bindingApp::execSubscription() { int err = 0; - for(const SourceAPI& srcAPI: sourcesList_) + for(SourceAPI& srcAPI: sourcesListV_) { if (srcAPI.api() != std::string(ctlConfig_->api)) { - err += srcAPI.makeSubscription(); + err = srcAPI.makeSubscription(); } } return err; diff --git a/signal-composer-binding/signal-composer.hpp b/signal-composer-binding/signal-composer.hpp index 341eaab..4050700 100644 --- a/signal-composer-binding/signal-composer.hpp +++ b/signal-composer-binding/signal-composer.hpp @@ -28,7 +28,7 @@ private: CtlConfigT* ctlConfig_; static CtlSectionT ctlSections_[]; ///< Config Section definition (note: controls section index should match handle retrieval in) - std::vector<SourceAPI> sourcesList_; + std::vector<SourceAPI> sourcesListV_; explicit bindingApp(const std::string& filepath); bindingApp(); @@ -37,15 +37,16 @@ private: CtlActionT* convert2Action(const std::string& name, json_object* action); int loadOneSourceAPI(json_object* sourcesJ); - static int loadSourcesAPI(CtlSectionT* section, json_object *sectionJ); + static int loadSourcesAPI(CtlSectionT* section, json_object *signalsJ); int loadOneSignal(json_object* signalsJ); - static int loadSignals(CtlSectionT* section, json_object *sectionJ); + static int loadSignals(CtlSectionT* section, json_object *signalsJ); public: static bindingApp& instance(); + static std::vector<std::string> parseURI(const std::string& uri); int loadConfig(const std::string& filepath); - //void loadSignalsFile(std::string signalsFile); + int loadSignals(json_object* signalsJ); CtlConfigT* ctlConfig(); std::vector<std::shared_ptr<Signal>> getAllSignals(); @@ -53,7 +54,7 @@ public: std::shared_ptr<Signal> searchSignal(const std::string& aName); json_object* getSignalValue(const std::string& sig, json_object* options); - int execSubscription() const; + int execSubscription(); }; struct pluginCBT diff --git a/signal-composer-binding/signal.cpp b/signal-composer-binding/signal.cpp index ea5a324..1679a6f 100644 --- a/signal-composer-binding/signal.cpp +++ b/signal-composer-binding/signal.cpp @@ -24,18 +24,32 @@ #define MICRO 1000000 +Signal::Signal(const std::string& id, const std::string& event, std::vector<std::string>& depends, const std::string& unit, double frequency, CtlActionT* onReceived, json_object* getSignalsArgs) +:id_(id), + event_(event), + dependsSigV_(depends), + timestamp_(0.0), + value_({0,0,0,0,0,""}), + frequency_(frequency), + unit_(unit), + onReceived_(onReceived), + getSignalsArgs_(getSignalsArgs) +{} + Signal::Signal(const std::string& id, - std::vector<std::string>& sources, - const std::string& unit, - double frequency, - CtlActionT* onReceived) + std::vector<std::string>& depends, + const std::string& unit, + double frequency, + CtlActionT* onReceived) :id_(id), - signalSigList_(sources), + event_(), + dependsSigV_(depends), timestamp_(0.0), value_({0,0,0,0,0,""}), frequency_(frequency), unit_(unit), - onReceived_(onReceived) + onReceived_(onReceived), + getSignalsArgs_() {} Signal::operator bool() const @@ -54,11 +68,8 @@ bool Signal::operator ==(const Signal& other) const bool Signal::operator ==(const std::string& aName) const { if(! fnmatch(aName.c_str(), id_.c_str(), FNM_CASEFOLD)) {return true;} - for( const std::string& src : signalSigList_) - { - if(! fnmatch(aName.c_str(), src.c_str(), FNM_CASEFOLD)) {return true;} - if( src.find(aName) != std::string::npos) {return true;} - } + if(event_.find(aName) != std::string::npos) {return true;} + return false; } @@ -70,24 +81,27 @@ const std::string Signal::id() const json_object* Signal::toJSON() const { json_object* queryJ = nullptr; - std::vector<std::string> lowSignalName; - for (const std::string& src: signalSigList_ ) + std::vector<std::string> dependsSignalName; + for (const std::string& src: dependsSigV_ ) { ssize_t sep = src.find_first_of("/"); if(sep != std::string::npos) { - lowSignalName.push_back(src.substr(sep+1)); + dependsSignalName.push_back(src.substr(sep+1)); } } json_object* nameArray = json_object_new_array(); - for (const std::string& lowSig: lowSignalName) + for (const std::string& lowSig: dependsSignalName) { json_object_array_add(nameArray, json_object_new_string(lowSig.c_str())); } - wrap_json_pack(&queryJ, "{so,ss*,sf*}", - "signal", nameArray, + wrap_json_pack(&queryJ, "{ss,ss*,so*,ss*,sf*,so*}", + "id", id_.c_str(), + "event", event_.c_str(), + "depends", nameArray, "unit", unit_.c_str(), - "frequency", frequency_); + "frequency", frequency_, + "getSignalsArgs", getSignalsArgs_); return queryJ; } @@ -104,6 +118,7 @@ void Signal::set(long long int timestamp, struct SignalValue& value) { timestamp_ = timestamp; value_ = value; + history_[timestamp_] = value_; } /// @brief Observer method called when a Observable Signal has changes. @@ -112,7 +127,7 @@ void Signal::set(long long int timestamp, struct SignalValue& value) /// @param[in] value - value of change void Signal::update(long long int timestamp, struct SignalValue value) { - AFB_NOTICE("Got an update from observed signal. Timestamp: %lld, vb: %d, vn: %lf, vs: %s", timestamp, value.boolVal, value.numVal, value.strVal.c_str()); + AFB_DEBUG("Got an update from observed signal. Timestamp: %lld, vb: %d, vn: %lf, vs: %s", timestamp, value.boolVal, value.numVal, value.strVal.c_str()); } /// @brief Notify observers that there is a change and execute callback defined @@ -128,11 +143,18 @@ int Signal::onReceivedCB(json_object *queryJ) return err; } -/// @brief Make a Signal observer observes a Signal observable +/// @brief Make a Signal observer observes a Signal observable if not already +/// present in the Observers vector. /// /// @param[in] obs - pointer to a Signal observable void Signal::attach(Signal* obs) { + for ( auto& sig : Observers_) + { + if (obs == sig) + {return;} + } + Observers_.push_back(obs); } @@ -142,7 +164,7 @@ void Signal::attach(Signal* obs) /// @param[in] bApp - bindinApp instance void Signal::attachToSourceSignals(bindingApp& bApp) { - for (const std::string& srcSig: signalSigList_) + for (const std::string& srcSig: dependsSigV_) { if(srcSig.find("/") == std::string::npos) { diff --git a/signal-composer-binding/signal.hpp b/signal-composer-binding/signal.hpp index 2088ffd..4e078fc 100644 --- a/signal-composer-binding/signal.hpp +++ b/signal-composer-binding/signal.hpp @@ -37,13 +37,16 @@ class Signal { private: std::string id_; - std::vector<std::string> signalSigList_; + std::string event_; + std::vector<std::string> dependsSigV_; long long int timestamp_; struct SignalValue value_; std::map<long long int, struct SignalValue> history_; ///< history_ - Hold signal value history in map with <timestamp, value> double frequency_; std::string unit_; CtlActionT* onReceived_; + json_object* getSignalsArgs_; + std::vector<Signal*> Observers_; void notify(); @@ -51,7 +54,8 @@ private: int recursionCheck(const std::string& origId); public: - Signal(const std::string& id, std::vector<std::string>& sources, const std::string& unit, double frequency, CtlActionT* onReceived); + Signal(const std::string& id, const std::string& event, std::vector<std::string>& depends, const std::string& unit, double frequency, CtlActionT* onReceived, json_object* getSignalsArgs); + Signal(const std::string& id, std::vector<std::string>& depends, const std::string& unit, double frequency, CtlActionT* onReceived); explicit operator bool() const; bool operator==(const Signal& other) const; bool operator==(const std::string& aName) const; diff --git a/signal-composer-binding/source.cpp b/signal-composer-binding/source.cpp index 80c59d6..1f5d588 100644 --- a/signal-composer-binding/source.cpp +++ b/signal-composer-binding/source.cpp @@ -20,8 +20,8 @@ SourceAPI::SourceAPI() {} -SourceAPI::SourceAPI(const std::string& api, const std::string& info, CtlActionT* init, CtlActionT* getSignal): - api_(api), info_(info), init_(init), getSignal_(getSignal) +SourceAPI::SourceAPI(const std::string& api, const std::string& info, CtlActionT* init, CtlActionT* getSignals): + api_(api), info_(info), init_(init), getSignals_(getSignals) {} std::string SourceAPI::api() const @@ -29,41 +29,51 @@ std::string SourceAPI::api() const return api_; } -void SourceAPI::addSignal(const std::string& id, std::vector<std::string>& sources, const std::string& sClass, const std::string& unit, double frequency, CtlActionT* onReceived) +void SourceAPI::addSignal(const std::string& id, const std::string& event, std::vector<std::string>& depends, const std::string& sClass, const std::string& unit, double frequency, CtlActionT* onReceived, json_object* getSignalsArgs) { - std::shared_ptr<Signal> sig = std::make_shared<Signal>(id, sources, unit, frequency, onReceived); + std::shared_ptr<Signal> sig = std::make_shared<Signal>(id, event, depends, unit, frequency, onReceived, getSignalsArgs); - signalsList_.push_back(sig); + signalsMap_[sig] = false; } std::vector<std::shared_ptr<Signal>> SourceAPI::getSignals() const { - return signalsList_; + std::vector<std::shared_ptr<Signal>> signals; + for (auto& sig: signalsMap_) + { + signals.push_back(sig.first); + } + return signals; } std::shared_ptr<Signal> SourceAPI::searchSignal(const std::string& name) const { - for (auto& sig: signalsList_) + for (auto& sig: signalsMap_) { - if(*sig == name) {return sig;} + if(*sig.first == name) {return sig.first;} } return nullptr; } -int SourceAPI::makeSubscription() const +int SourceAPI::makeSubscription() { int err = 0; - if(getSignal_) + if(getSignals_) { - for(std::shared_ptr<Signal> sig: signalsList_) + for(auto& sig: signalsMap_) { - json_object* lowSignalNameJ = sig->toJSON(); - if(!lowSignalNameJ) + json_object* signalJ = sig.first->toJSON(); + if(!signalJ) { - AFB_ERROR("Error building JSON query object to subscribe to for signal %s", sig->id().c_str()); - return -1; + AFB_ERROR("Error building JSON query object to subscribe to for signal %s", sig.first->id().c_str()); + err = -1; + break; } - err += ActionExecOne(getSignal_, lowSignalNameJ); + err += sig.second ? 0:ActionExecOne(getSignals_, signalJ); + if(err) + {AFB_WARNING("Fails to subscribe to signal %s", sig.first->id().c_str());} + else + {sig.second = true;} } } diff --git a/signal-composer-binding/source.hpp b/signal-composer-binding/source.hpp index c295b6e..560a137 100644 --- a/signal-composer-binding/source.hpp +++ b/signal-composer-binding/source.hpp @@ -26,19 +26,19 @@ private: std::string api_; std::string info_; CtlActionT* init_; - CtlActionT* getSignal_; + CtlActionT* getSignals_; - std::vector<std::shared_ptr<Signal>> signalsList_; + std::map<std::shared_ptr<Signal>, bool> signalsMap_; public: SourceAPI(); SourceAPI(const std::string& api, const std::string& info, CtlActionT* init, CtlActionT* getSignal); std::string api() const; - void addSignal(const std::string& id, std::vector<std::string>& sources, const std::string& sClass, const std::string& unit, double frequency, CtlActionT* onReceived); + void addSignal(const std::string& id, const std::string& event, std::vector<std::string>& sources, const std::string& sClass, const std::string& unit, double frequency, CtlActionT* onReceived, json_object* getSignalsArgs); std::vector<std::shared_ptr<Signal>> getSignals() const; std::shared_ptr<Signal> searchSignal(const std::string& name) const; - int makeSubscription() const; + int makeSubscription(); }; |