From d663f702c0e4fa562783504a483dce8f362d628e Mon Sep 17 00:00:00 2001 From: Romain Forlot Date: Tue, 12 Sep 2017 00:29:28 +0200 Subject: Progression on config parsing. Change-Id: Idd8d8987ccabef381f6d79c1d508930a2d4bae3c Signed-off-by: Romain Forlot --- signal-composer-binding/CMakeLists.txt | 3 +- signal-composer-binding/ctl-dispatch.c | 705 ------------- signal-composer-binding/ctl-lua.c | 1057 -------------------- signal-composer-binding/ctl-lua.h | 28 - signal-composer-binding/observer.hpp | 30 + signal-composer-binding/signal-composer-apidef.h | 32 +- .../signal-composer-apidef.json | 2 +- .../signal-composer-binding.cpp | 64 +- .../signal-composer-binding.hpp | 9 +- signal-composer-binding/signal-composer.cpp | 243 ++++- signal-composer-binding/signal-composer.hpp | 92 +- signal-composer-binding/signal.cpp | 86 ++ signal-composer-binding/signal.hpp | 44 +- signal-composer-binding/source.cpp | 42 + signal-composer-binding/source.hpp | 42 + 15 files changed, 567 insertions(+), 1912 deletions(-) delete mode 100644 signal-composer-binding/ctl-dispatch.c delete mode 100644 signal-composer-binding/ctl-lua.c delete mode 100644 signal-composer-binding/ctl-lua.h create mode 100644 signal-composer-binding/observer.hpp create mode 100644 signal-composer-binding/signal.cpp create mode 100644 signal-composer-binding/source.cpp create mode 100644 signal-composer-binding/source.hpp (limited to 'signal-composer-binding') diff --git a/signal-composer-binding/CMakeLists.txt b/signal-composer-binding/CMakeLists.txt index 78db103..8757204 100644 --- a/signal-composer-binding/CMakeLists.txt +++ b/signal-composer-binding/CMakeLists.txt @@ -21,7 +21,7 @@ PROJECT_TARGET_ADD(signal-composer) # Define project Targets - add_library(${TARGET_NAME} MODULE ${TARGET_NAME}-binding.cpp ${TARGET_NAME}.cpp ctl-lua.c ctl-dispatch.c) + add_library(${TARGET_NAME} MODULE ${TARGET_NAME}-binding.cpp ${TARGET_NAME}.cpp signal-composer.cpp source.cpp) # Binder exposes a unique public entry point SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES @@ -37,5 +37,6 @@ PROJECT_TARGET_ADD(signal-composer) # Library dependencies (include updates automatically) TARGET_LINK_LIBRARIES(${TARGET_NAME} afb-utilities + afb-controller ${link_libraries} ) diff --git a/signal-composer-binding/ctl-dispatch.c b/signal-composer-binding/ctl-dispatch.c deleted file mode 100644 index b84c86e..0000000 --- a/signal-composer-binding/ctl-dispatch.c +++ /dev/null @@ -1,705 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, something express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Reference: - * Json load using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include - -#include "ctl-lua.h" -#include "signal-composer-binding.hpp" - -typedef void*(*DispatchPluginInstallCbT)(const char* label, const char*version, const char*info); - -static afb_req NULL_AFBREQ = {}; - -typedef struct { - const char* id; - const char* source; - const char* class; - DispatchActionT* onReceived; -} DispatchSignalT; - -typedef struct { - const char* label; - const char* info; - const char* ssource; - const char* sclass; - DispatchActionT* actions; -} DispatchHandleT; - -typedef struct { - const char *label; - const char *info; - void *context; - char *sharelib; - void *dlHandle; - luaL_Reg *l2cFunc; - int l2cCount; -} DispatchPluginT; - -typedef struct { - const char* label; - const char *info; - const char *version; - DispatchPluginT *plugin; - DispatchHandleT **sources; - DispatchHandleT **signals; -} DispatchConfigT; - -// global config handle -static DispatchConfigT *configHandle = NULL; - -static int DispatchSignalToIndex(DispatchHandleT **signals, const char* controlLabel) { - - for (int idx = 0; signals[idx]; idx++) { - if (!strcasecmp(controlLabel, signals[idx]->label)) return idx; - } - return -1; -} - -static int DispatchOneSignal(DispatchSourceT source, DispatchHandleT **signals, const char* controlLabel, json_object *queryJ, afb_req request) { - int err; - - if (!configHandle) { - AFB_ERROR("DISPATCH-CTL-API: (Hoops/Bug!!!) No Config Loaded"); - return -1; - } - - if (!configHandle->signals) { - AFB_ERROR("DISPATCH-CTL-API: No Signal Action in Json config label=%s version=%s", configHandle->label, configHandle->version); - return -1; - } - - int index = DispatchSignalToIndex(signals, controlLabel); - if (index < 0 || !signals[index]->actions) { - AFB_ERROR("DISPATCH-CTL-API:NotFound/Error label=%s in Json Signal Config File", controlLabel); - return -1; - } - - // Fulup (Bug/Feature) in current version is unique to every onload profile - if (configHandle->plugin && configHandle->plugin->l2cCount) { - LuaL2cNewLib (configHandle->plugin->label, configHandle->plugin->l2cFunc, configHandle->plugin->l2cCount); - } - - // loop on action for this control - DispatchActionT *actions = signals[index]->actions; - for (int idx = 0; actions[idx].label; idx++) { - - switch (actions[idx].mode) { - case CTL_MODE_API: - { - json_object *returnJ; - - // if query is empty increment usage count and pass args - if (!queryJ || json_object_get_type(queryJ) != json_type_object) { - json_object_get(actions[idx].argsJ); - queryJ= actions[idx].argsJ; - } else if (actions[idx].argsJ) { - - // Merge queryJ and argsJ before sending request - if (json_object_get_type(actions[idx].argsJ) == json_type_object) { - json_object_object_foreach(actions[idx].argsJ, key, val) { - json_object_object_add(queryJ, key, val); - } - } else { - json_object_object_add(queryJ, "args", actions[idx].argsJ); - } - } - - int err = afb_service_call_sync(actions[idx].api, actions[idx].call, queryJ, &returnJ); - if (err) { - static const char*format = "DispatchOneSignal(Api) api=%s verb=%s args=%s"; - if (afb_req_is_valid(request))afb_req_fail_f(request, "DISPATCH-CTL-MODE:API", format, actions[idx].label, actions[idx].api, actions[idx].call); - else AFB_ERROR(format, actions[idx].api, actions[idx].call, actions[idx].label); - return -1; - } - break; - } - -#ifdef CONTROL_SUPPORT_LUA - case CTL_MODE_LUA: - err = LuaCallFunc(source, &actions[idx], queryJ); - if (err) { - static const char*format = "DispatchOneSignal(Lua) label=%s func=%s args=%s"; - if (afb_req_is_valid(request)) afb_req_fail_f(request, "DISPATCH-CTL-MODE:Lua", format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ)); - else AFB_ERROR(format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ)); - return -1; - } - break; -#endif - - case CTL_MODE_CB: - err = (*actions[idx].actionCB) (source, actions[idx].label, actions[idx].argsJ, queryJ, configHandle->plugin->context); - if (err) { - static const char*format = "DispatchOneSignal(Callback) label%s func=%s args=%s"; - if (afb_req_is_valid(request)) afb_req_fail_f(request, "DISPATCH-CTL-MODE:Cb", format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ)); - else AFB_ERROR(format, actions[idx].label, actions[idx].call, json_object_get_string(actions[idx].argsJ)); - return -1; - } - break; - - default: - { - static const char*format = "DispatchOneSignal(unknown) mode control=%s action=%s"; - AFB_ERROR(format, signals[index]->label); - if (afb_req_is_valid(request))afb_req_fail_f(request, "DISPATCH-CTL-MODE:Unknown", format, signals[index]->label); - } - } - } - - // everything when fine - if (afb_req_is_valid(request)) afb_req_success(request, NULL, signals[index]->label); - return 0; -} - -// Event name is mapped on control label and executed as a standard control -/* -void DispatchOneEvent(const char *evtLabel, json_object *eventJ) { - DispatchHandleT **events = configHandle->events; - - (void) DispatchOneSignal(CTL_SOURCE_EVENT, events, evtLabel, eventJ, NULL_AFBREQ); -} -*/ -// Event name is mapped on control label and executed as a standard control - -int DispatchSources() { - if (!configHandle) return 1; - - int err = 0; - DispatchHandleT **sources = configHandle->sources; - ssize_t i = 0, nSources = sizeof(*sources) / sizeof(DispatchHandleT); - - while(i < nSources) { - const char* sourceLabel = sources[i++]->label; - err = DispatchOneSignal(CTL_SOURCE_ONLOAD, sources, sourceLabel, NULL, NULL_AFBREQ); - } - - return err; -} - -void ctlapi_dispatch(afb_req request) { - DispatchHandleT **signals = configHandle->signals; - json_object *queryJ, *argsJ=NULL; - const char *target; - DispatchSourceT source= CTL_SOURCE_UNKNOWN; - - queryJ = afb_req_json(request); - int err = wrap_json_unpack(queryJ, "{s:s, s?i s?o !}", "target", &target, "source", &source, "args", &argsJ); - if (err) { - afb_req_fail_f(request, "CTL-DISPTACH-INVALID", "missing target or args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - (void) DispatchOneSignal(source, signals, target, argsJ, request); -} - -// Wrapper to Lua2c plugin command add context dans delegate to LuaWrapper -int DispatchOneL2c(lua_State* luaState, char *funcname, Lua2cFunctionT callback) { -#ifndef CONTROL_SUPPORT_LUA - AFB_ERROR("DISPATCH-ONE-L2C: LUA support not selected (cf:CONTROL_SUPPORT_LUA) in config.cmake"); - return 1; -#else - int err=Lua2cWrapper(luaState, funcname, callback, configHandle->plugin->context); - return err; -#endif -} - - -// List Avaliable Configuration Files - -void ctlapi_config(struct afb_req request) { - json_object*tmpJ; - char *dirList; - // Compile some default directories to browse - char defaultConfPath[CONTROL_MAXPATH_LEN]; - strncpy(defaultConfPath, GetBindingDirPath(), sizeof(GetBindingDirPath())); - strncat(defaultConfPath, "/etc:", sizeof(defaultConfPath) - strlen(defaultConfPath) - 1); - strncat(defaultConfPath, GetBindingDirPath(), sizeof(defaultConfPath) - strlen(defaultConfPath) - 1); - strncat(defaultConfPath, "/data", sizeof(defaultConfPath) - strlen(defaultConfPath) - 1); - - - json_object* queryJ = afb_req_json(request); - if (queryJ && json_object_object_get_ex(queryJ, "cfgpath", &tmpJ)) { - dirList = strdup(json_object_get_string(tmpJ)); - } else { - - dirList = getenv("CONTROL_CONFIG_PATH"); - if (!dirList) dirList = defaultConfPath; - AFB_NOTICE("CONFIG-MISSING: use default CONTROL_CONFIG_PATH=%s", defaultConfPath); - } - - // get list of config file - struct json_object *responseJ = ScanForConfig(dirList, CTL_SCAN_RECURSIVE, "onload", "json"); - - if (json_object_array_length(responseJ) == 0) { - afb_req_fail(request, "CONFIGPATH:EMPTY", "No Config Found in CONTROL_CONFIG_PATH"); - } else { - afb_req_success(request, responseJ, NULL); - } - - return; -} - -// unpack individual action object - -static int DispatchLoadOneAction(DispatchConfigT *controlConfig, json_object *actionJ, DispatchActionT *action) { - char *api = NULL, *verb = NULL, *callback = NULL, *lua = NULL, *function = NULL; - int err, modeCount = 0; - - err = wrap_json_unpack(actionJ, "{s?s,s?s,s?s,s?s,s?s,s?s,s?s,s?o !}" - , "label", &action->label - , "info", &action->info - , "function", &function - , "callback", &callback - , "lua", &lua - , "api", &api, "verb", &verb - , "args", &action->argsJ); - if (err) { - AFB_ERROR("DISPATCH-LOAD-ACTION Missing something label|info|callback|lua|(api+verb)|args in %s", json_object_get_string(actionJ)); - return -1; - } - - // Generic way to specify a C or LUA actions - if(function) { - if(strcasestr(function, "lua")) { - action->mode = CTL_MODE_LUA; - action->call = function; - modeCount++; - } - else if(controlConfig->plugin) { - action->mode = CTL_MODE_CB; - action->call = callback; - modeCount++; - - action->actionCB = dlsym(controlConfig->plugin->dlHandle, callback); - if (!action->actionCB) { - AFB_ERROR("DISPATCH-LOAD-ACTION fail to find calbback=%s in %s", callback, controlConfig->plugin->sharelib); - return -1; - } - } - // If label not already set then use function name - if(! &action->label) action->label = function; - } - - if (lua) { - action->mode = CTL_MODE_LUA; - action->call = lua; - modeCount++; - // If label not already set then use function name - if(! &action->label) action->label = lua; - } - - if (api && verb) { - action->mode = CTL_MODE_API; - action->api = api; - action->call = verb; - modeCount++; - char* apiVerb = strdup(api); - apiVerb = strncat(apiVerb, "/", sizeof(*apiVerb) - strlen(apiVerb) - 1); - apiVerb = strncat(apiVerb, verb, sizeof(*apiVerb) - strlen(apiVerb) - 1); - // If label not already set then use function name - if(! &action->label) action->label = apiVerb; - free(apiVerb); - } - - if (callback && controlConfig->plugin) { - action->mode = CTL_MODE_CB; - action->call = callback; - modeCount++; - - action->actionCB = dlsym(controlConfig->plugin->dlHandle, callback); - if (!action->actionCB) { - AFB_ERROR("DISPATCH-LOAD-ACTION fail to find calbback=%s in %s", callback, controlConfig->plugin->sharelib); - return -1; - } - } - - // make sure at least one mode is selected - if (modeCount == 0) { - AFB_ERROR("DISPATCH-LOAD-ACTION No Action Selected lua|callback|(api+verb) in %s", json_object_get_string(actionJ)); - return -1; - } - - if (modeCount > 1) { - AFB_ERROR("DISPATCH-LOAD-ACTION:Too Many arguments lua|callback|(api+verb) in %s", json_object_get_string(actionJ)); - return -1; - } - return 0; -}; - -static DispatchActionT *DispatchLoadActions(DispatchConfigT *controlConfig, json_object *actionsJ) { - int err; - DispatchActionT *actions; - - // action array is close with a nullvalue; - if (json_object_get_type(actionsJ) == json_type_array) { - int count = json_object_array_length(actionsJ); - actions = calloc(count + 1, sizeof (DispatchActionT)); - - for (int idx = 0; idx < count; idx++) { - json_object *actionJ = json_object_array_get_idx(actionsJ, idx); - err = DispatchLoadOneAction(controlConfig, actionJ, &actions[idx]); - if (err) return NULL; - } - - } else { - actions = calloc(2, sizeof (DispatchActionT)); - err = DispatchLoadOneAction(controlConfig, actionsJ, &actions[0]); - if (err) return NULL; - } - - return actions; -} - -static void DispatchLoadPlugin(DispatchConfigT *controlConfig, json_object* sourcesJ, json_object* pluginJ) -{ - int err = 0; - json_object *lua2csJ = NULL; - DispatchPluginT *dPlugin= calloc(1, sizeof(DispatchPluginT)); - controlConfig->plugin = dPlugin; - const char*ldSearchPath=NULL; - - err = wrap_json_unpack(pluginJ, "{ss,s?s,s?s,ss,s?o!}", - "label", &dPlugin->label, "info", &dPlugin->info, "ldpath", &ldSearchPath, "sharelib", &dPlugin->sharelib, "lua2c", &lua2csJ); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:ONLOAD Plugin missing label|[info]|sharelib|[lua2c] in %s", json_object_get_string(sourcesJ)); - return; - } - - // if search path not in Json config file, then try default - if (!ldSearchPath) ldSearchPath= strncat(GetBindingDirPath(), "/data", sizeof(GetBindingDirPath()) - strlen(GetBindingDirPath()) - 1); - - // search for default policy config file - json_object *pluginPathJ = ScanForConfig(ldSearchPath, CTL_SCAN_RECURSIVE, dPlugin->sharelib, NULL); - if (!pluginPathJ || json_object_array_length(pluginPathJ) == 0) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:PLUGIN Missing plugin=%s in path=%s", dPlugin->sharelib, ldSearchPath); - return; - } - - char *filename; - char*fullpath; - err = wrap_json_unpack(json_object_array_get_idx(pluginPathJ, 0), "{s:s, s:s !}", "fullpath", &fullpath, "filename", &filename); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:PLUGIN HOOPs invalid plugin file path = %s", json_object_get_string(pluginPathJ)); - return; - } - - if (json_object_array_length(pluginPathJ) > 1) { - AFB_WARNING("DISPATCH-LOAD-CONFIG:PLUGIN plugin multiple instances in searchpath will use %s/%s", fullpath, filename); - } - - char pluginpath[CONTROL_MAXPATH_LEN]; - strncpy(pluginpath, fullpath, sizeof (pluginpath)); - strncat(pluginpath, "/", sizeof (pluginpath)-strlen(pluginpath)-1); - strncat(pluginpath, filename, sizeof (pluginpath)-strlen(pluginpath)-1); - dPlugin->dlHandle = dlopen(pluginpath, RTLD_NOW); - if (!dPlugin->dlHandle) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:PLUGIN Fail to load pluginpath=%s err= %s", pluginpath, dlerror()); - return; - } - - CtlPluginMagicT *ctlPluginMagic = (CtlPluginMagicT*) dlsym(dPlugin->dlHandle, "CtlPluginMagic"); - if (!ctlPluginMagic || ctlPluginMagic->magic != CTL_PLUGIN_MAGIC) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:Plugin symbol'CtlPluginMagic' missing or != CTL_PLUGIN_MAGIC plugin=%s", pluginpath); - return; - } else { - AFB_NOTICE("DISPATCH-LOAD-CONFIG:Plugin %s successfully registered", ctlPluginMagic->label); - } - - // Jose hack to make verbosity visible from sharelib - struct afb_binding_data_v2 *afbHidenData = dlsym(dPlugin->dlHandle, "afbBindingV2data"); - if (afbHidenData) *afbHidenData = afbBindingV2data; - - // Push lua2cWrapper @ into plugin - Lua2cWrapperT *lua2cInPlug = dlsym(dPlugin->dlHandle, "Lua2cWrap"); -#ifndef CONTROL_SUPPORT_LUA - if (lua2cInPlug) *lua2cInPlug = NULL; -#else - // Lua2cWrapper is part of binder and not expose to dynamic link - if (lua2cInPlug) *lua2cInPlug = DispatchOneL2c; - - { - int Lua2cAddOne(luaL_Reg *l2cFunc, const char* l2cName, int index) { - char funcName[CONTROL_MAXPATH_LEN]; - strncpy(funcName, "lua2c_", sizeof(funcName)); - strncat(funcName, l2cName, sizeof(funcName)-strlen(funcName)-1); - - Lua2cFunctionT l2cFunction= (Lua2cFunctionT)dlsym(dPlugin->dlHandle, funcName); - if (!l2cFunction) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:Plugin symbol'%s' missing err=%s", funcName, dlerror()); - return 1; - } - l2cFunc[index].func=(void*)l2cFunction; - l2cFunc[index].name=strdup(l2cName); - - return 0; - } - - int errCount = 0; - luaL_Reg *l2cFunc=NULL; - int count=0; - - // look on l2c command and push them to LUA - if (json_object_get_type(lua2csJ) == json_type_array) { - int length = json_object_array_length(lua2csJ); - l2cFunc = calloc(length + 1, sizeof (luaL_Reg)); - for (count=0; count < length; count++) { - int err; - const char *l2cName = json_object_get_string(json_object_array_get_idx(lua2csJ, count)); - err = Lua2cAddOne(l2cFunc, l2cName, count); - if (err) errCount++; - } - } else { - l2cFunc = calloc(2, sizeof (luaL_Reg)); - const char *l2cName = json_object_get_string(lua2csJ); - errCount = Lua2cAddOne(l2cFunc, l2cName, 0); - count=1; - } - if (errCount) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:Plugin %d symbols not found in plugin='%s'", errCount, pluginpath); - return; - } else { - dPlugin->l2cFunc= l2cFunc; - dPlugin->l2cCount= count; - } - } -#endif - DispatchPluginInstallCbT ctlPluginOnload = dlsym(dPlugin->dlHandle, "CtlPluginOnload"); - if (ctlPluginOnload) { - dPlugin->context = (*ctlPluginOnload) (controlConfig->label, controlConfig->version, controlConfig->info); - } -} - -static DispatchHandleT *DispatchLoadSignal(DispatchConfigT *controlConfig, json_object *controlJ) { - json_object *actionsJ, *permissionsJ; - int err; - - DispatchHandleT *dispatchHandle = calloc(1, sizeof (DispatchHandleT)); - err = wrap_json_unpack(controlJ, "{ss,s?s,ss,ss,s?o,so !}" - , "id", &dispatchHandle->label - , "info", &dispatchHandle->info - , "source", &dispatchHandle->ssource - , "class", &dispatchHandle->sclass - , "permissions", &permissionsJ - , "onReceived", &actionsJ); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:CONTROL Missing something label|[info]|actions in %s", json_object_get_string(controlJ)); - return NULL; - } - - dispatchHandle->actions = DispatchLoadActions(controlConfig, actionsJ); - if (!dispatchHandle->actions) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:CONTROL Error when parsing actions %s", dispatchHandle->label); - return NULL; - } - return dispatchHandle; -} - -static DispatchHandleT *DispatchLoadSource(DispatchConfigT *controlConfig, json_object *sourcesJ) { - json_object *actionsJ = NULL, *pluginJ = NULL; - int err; - - DispatchHandleT *dispatchSources = calloc(1, sizeof (DispatchHandleT)); - err = wrap_json_unpack(sourcesJ, "{ss,s?s,s?o,s?o,s?o !}", - "api", &dispatchSources->label, "info", &dispatchSources->info, "plugin", &pluginJ, "actions", &actionsJ); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:ONLOAD Missing something label|[info]|[plugin]|[actions] in %s", json_object_get_string(sourcesJ)); - return NULL; - } - - // best effort to initialise everything before starting - err = afb_daemon_require_api(dispatchSources->label, 1); - if (err) { - AFB_WARNING("DISPATCH-LOAD-CONFIG:REQUIRE Fail to get=%s", dispatchSources->label); - } - - if (pluginJ) { - DispatchLoadPlugin(controlConfig, sourcesJ, pluginJ); - } - - dispatchSources->actions = DispatchLoadActions(controlConfig, actionsJ); - if (!dispatchSources->actions) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:ONLOAD Error when parsing actions %s", dispatchSources->label); - return NULL; - } - return dispatchSources; -} - -static DispatchConfigT *DispatchLoadConfig(const char* filepath) { - json_object *controlConfigJ, *ignoreJ; - int err; - - // Load JSON file - controlConfigJ = json_object_from_file(filepath); - if (!controlConfigJ) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:JsonLoad invalid JSON %s ", filepath); - return NULL; - } - - AFB_INFO("DISPATCH-LOAD-CONFIG: loading config filepath=%s", filepath); - - json_object *metadataJ = NULL, *sourcesJ = NULL, *signalsJ = NULL; - err = wrap_json_unpack(controlConfigJ, "{s?s,s?o,s?o,s?o !}", "$schema", &ignoreJ, "metadata", &metadataJ, "sources", &sourcesJ, "signals", &signalsJ); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG Missing something metadata|[sources]|[signals] in %s", json_object_get_string(controlConfigJ)); - return NULL; - } - - DispatchConfigT *controlConfig = calloc(1, sizeof (DispatchConfigT)); - if (metadataJ) { - const char*ctlname=NULL; - err = wrap_json_unpack(metadataJ, "{ss,s?s,s?s,ss !}", "label", &controlConfig->label, "version", &controlConfig->version, "name", &ctlname, "info", &controlConfig->info); - if (err) { - AFB_ERROR("DISPATCH-LOAD-CONFIG:METADATA Missing something label|version|[label] in %s", json_object_get_string(metadataJ)); - return NULL; - } - - // if ctlname is provided change process name now - if (ctlname) { - err= prctl(PR_SET_NAME, ctlname,NULL,NULL,NULL); - if (err) AFB_WARNING("Fail to set Process Name to:%s",ctlname); - } - } - - if (sourcesJ) { - DispatchHandleT *dispatchHandle; - - if (json_object_get_type(sourcesJ) != json_type_array) { - controlConfig->sources = (DispatchHandleT**) calloc(2, sizeof (void*)); - dispatchHandle = DispatchLoadSource(controlConfig, sourcesJ); - controlConfig->sources[0] = dispatchHandle; - } else { - int length = json_object_array_length(sourcesJ); - controlConfig->sources = (DispatchHandleT**) calloc(length + 1, sizeof (void*)); - - for (int jdx = 0; jdx < length; jdx++) { - json_object *sourcesJ = json_object_array_get_idx(sourcesJ, jdx); - dispatchHandle = DispatchLoadSource(controlConfig, sourcesJ); - controlConfig->sources[jdx] = dispatchHandle; - } - } - } - - if (signalsJ) { - DispatchHandleT* dispatchHandle; - - if (json_object_get_type(signalsJ) != json_type_array) { - controlConfig->signals = (DispatchHandleT**) calloc(2, sizeof (void*)); - dispatchHandle = DispatchLoadSignal(controlConfig, signalsJ); - controlConfig->signals[0] = dispatchHandle; - } else { - int length = json_object_array_length(signalsJ); - controlConfig->signals = (DispatchHandleT**) calloc(length + 1, sizeof (void*)); - - for (int jdx = 0; jdx < length; jdx++) { - json_object *controlJ = json_object_array_get_idx(signalsJ, jdx); - dispatchHandle = DispatchLoadSignal(controlConfig, controlJ); - controlConfig->signals[jdx] = dispatchHandle; - } - } - } -/* - if (eventsJ) { - DispatchHandleT *dispatchHandle; - - if (json_object_get_type(eventsJ) != json_type_array) { - controlConfig->events = (DispatchHandleT**) calloc(2, sizeof (void*)); - dispatchHandle = DispatchLoadSignal(controlConfig, eventsJ); - controlConfig->events[0] = dispatchHandle; - } else { - int length = json_object_array_length(eventsJ); - controlConfig->events = (DispatchHandleT**) calloc(length + 1, sizeof (void*)); - - for (int jdx = 0; jdx < length; jdx++) { - json_object *eventJ = json_object_array_get_idx(eventsJ, jdx); - dispatchHandle = DispatchLoadSignal(controlConfig, eventJ); - controlConfig->events[jdx] = dispatchHandle; - } - } - } -*/ - return controlConfig; -} - - -// Load default config file at init - -int DispatchInit() { - int index, luaLoaded = 0; - char controlFile [CONTROL_MAXPATH_LEN]; - // Compile some default directories to browse - char defaultConfPath[CONTROL_MAXPATH_LEN]; - strncpy(defaultConfPath, GetBindingDirPath(), sizeof(GetBindingDirPath())); - strncat(defaultConfPath, "/etc:", sizeof(defaultConfPath) - strlen(defaultConfPath) - 1); - strncat(defaultConfPath, GetBindingDirPath(), sizeof(defaultConfPath) - strlen(defaultConfPath) - 1); - strncat(defaultConfPath, "/data", sizeof(defaultConfPath) - strlen(defaultConfPath) - 1); - - const char *dirList = getenv("CONTROL_CONFIG_PATH"); - if (!dirList) dirList = defaultConfPath; - - strncpy(controlFile, CONTROL_CONFIG_PRE "-", CONTROL_MAXPATH_LEN); - strncat(controlFile, GetBinderName(), CONTROL_MAXPATH_LEN-strlen(controlFile)-1); - - // search for default dispatch config file - json_object* responseJ = ScanForConfig(dirList, CTL_SCAN_RECURSIVE, controlFile, "json"); - - // We load 1st file others are just warnings - for (index = 0; index < json_object_array_length(responseJ); index++) { - json_object *entryJ = json_object_array_get_idx(responseJ, index); - - char *filename; - char*fullpath; - int err = wrap_json_unpack(entryJ, "{s:s, s:s !}", "fullpath", &fullpath, "filename", &filename); - if (err) { - AFB_ERROR("DISPATCH-INIT HOOPs invalid JSON entry= %s", json_object_get_string(entryJ)); - return -1; - } - - if (index == 0) { - if (strcasestr(filename, controlFile)) { - char filepath[CONTROL_MAXPATH_LEN]; - strncpy(filepath, fullpath, sizeof (filepath)); - strncat(filepath, "/", sizeof (filepath)-strlen(filepath)-1); - strncat(filepath, filename, sizeof (filepath)-strlen(filepath)-1); - configHandle = DispatchLoadConfig(filepath); - if (!configHandle) { - AFB_ERROR("DISPATCH-INIT:ERROR Fail loading [%s]", filepath); - return -1; - } - luaLoaded = 1; - break; - } - } else { - AFB_WARNING("DISPATCH-INIT:WARNING Secondary Signal Config Ignored %s/%s", fullpath, filename); - } - } - - // no dispatch config found remove control API from binder - if (!luaLoaded) { - AFB_WARNING("DISPATCH-INIT:WARNING (setenv CONTROL_CONFIG_PATH) No Config '%s-*.json' in '%s'", controlFile, dirList); - } - - AFB_NOTICE("DISPATCH-INIT:SUCCES: Signal Dispatch Init"); - return 0; -} diff --git a/signal-composer-binding/ctl-lua.c b/signal-composer-binding/ctl-lua.c deleted file mode 100644 index 2773696..0000000 --- a/signal-composer-binding/ctl-lua.c +++ /dev/null @@ -1,1057 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author Fulup Ar Foll - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ref: - * (manual) https://www.lua.org/manual/5.3/manual.html - * (lua->C) http://www.troubleshooters.com/codecorn/lua/lua_c_calls_lua.htm#_Anatomy_of_a_Lua_Call - * (lua/C Var) http://acamara.es/blog/2012/08/passing-variables-from-lua-5-2-to-c-and-vice-versa/ - * (Lua/C Lib)https://john.nachtimwald.com/2014/07/12/wrapping-a-c-library-in-lua/ - * (Lua/C Table) https://gist.github.com/SONIC3D/10388137 - * (Lua/C Nested table) https://stackoverflow.com/questions/45699144/lua-nested-table-from-lua-to-c - * (Lua/C Wrapper) https://stackoverflow.com/questions/45699950/lua-passing-handle-to-function-created-with-newlib - * - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include - -#include "ctl-lua.h" - -#define LUA_FIST_ARG 2 // when using luaL_newlib calllback receive libtable as 1st arg -#define LUA_MSG_MAX_LENGTH 512 -#define JSON_ERROR (json_object*)-1 - -#define CTX_MAGIC 123456789 -#define CTX_TOKEN "AFB_ctx" - -static afb_req NULL_AFBREQ = {}; - -static lua_State* luaState; - -typedef struct { - char *name; - int count; - afb_event event; -} LuaAfbEvent; -static LuaAfbEvent *luaDefaultEvt; - -typedef struct { - int ctxMagic; - afb_req request; - void *handle; - char *info; -} LuaAfbContextT; - -typedef struct { - const char *callback; - json_object *context; - void *handle; -} LuaCallServiceT; - -typedef enum { - AFB_MSG_INFO, - AFB_MSG_WARNING, - AFB_MSG_NOTICE, - AFB_MSG_DEBUG, - AFB_MSG_ERROR, -} LuaAfbMessageT; - -/* - * Note(Fulup): I fail to use luaL_setmetatable and replaced it with a simple opaque - * handle while waiting for someone smarter than me to find a better solution - * https://stackoverflow.com/questions/45596493/lua-using-lua-newuserdata-from-lua-pcall - */ - -static LuaAfbContextT *LuaCtxCheck (lua_State *luaState, int index) { - LuaAfbContextT *afbContext; - //luaL_checktype(luaState, index, LUA_TUSERDATA); - //afbContext = (LuaAfbContextT *)luaL_checkudata(luaState, index, CTX_TOKEN); - luaL_checktype(luaState, index, LUA_TLIGHTUSERDATA); - afbContext = (LuaAfbContextT *) lua_touserdata(luaState, index); - if (afbContext == NULL && afbContext->ctxMagic != CTX_MAGIC) { - luaL_error(luaState, "Fail to retrieve user data context=%s", CTX_TOKEN); - AFB_ERROR ("afbContextCheck error retrieving afbContext"); - return NULL; - } - return afbContext; -} - -static LuaAfbContextT *LuaCtxPush (lua_State *luaState, afb_req request, void *handle, const char* info) { - // LuaAfbContextT *afbContext = (LuaAfbContextT *)lua_newuserdata(luaState, sizeof(LuaAfbContextT)); - // luaL_setmetatable(luaState, CTX_TOKEN); - LuaAfbContextT *afbContext = (LuaAfbContextT *)calloc(1, sizeof(LuaAfbContextT)); - lua_pushlightuserdata(luaState, afbContext); - if (!afbContext) { - AFB_ERROR ("LuaCtxPush fail to allocate user data context"); - return NULL; - } - afbContext->ctxMagic=CTX_MAGIC; - afbContext->info=strdup(info); - afbContext->request= request; - afbContext->handle= handle; - return afbContext; -} - -static void LuaCtxFree (LuaAfbContextT *afbContext) { - free(afbContext->info); - free(afbContext); -} - -// Push a json structure on the stack as a LUA table -static int LuaPushArgument (json_object *argsJ) { - - //AFB_NOTICE("LuaPushArgument argsJ=%s", json_object_get_string(argsJ)); - - json_type jtype= json_object_get_type(argsJ); - switch (jtype) { - case json_type_object: { - lua_newtable (luaState); - json_object_object_foreach (argsJ, key, val) { - int done = LuaPushArgument (val); - if (done) { - lua_setfield(luaState,-2, key); - } - } - break; - } - case json_type_array: { - int length= json_object_array_length(argsJ); - lua_newtable (luaState); - for (int idx=0; idx < length; idx ++) { - json_object *val=json_object_array_get_idx(argsJ, idx); - LuaPushArgument (val); - lua_seti (luaState,-2, idx); - } - break; - } - case json_type_int: - lua_pushinteger(luaState, json_object_get_int(argsJ)); - break; - case json_type_string: - lua_pushstring(luaState, json_object_get_string(argsJ)); - break; - case json_type_boolean: - lua_pushboolean(luaState, json_object_get_boolean(argsJ)); - break; - case json_type_double: - lua_pushnumber(luaState, json_object_get_double(argsJ)); - break; - case json_type_null: - AFB_WARNING("LuaPushArgument: NULL object type %s", json_object_get_string(argsJ)); - return 0; - break; - - default: - AFB_ERROR("LuaPushArgument: unsupported Json object type %s", json_object_get_string(argsJ)); - return 0; - } - return 1; -} - -static json_object *LuaPopOneArg (lua_State* luaState, int idx); - -// Move a table from Internal Lua representation to Json one -// Numeric table are transformed in json array, string one in object -// Mix numeric/string key are not supported -static json_object *LuaTableToJson (lua_State* luaState, int index) { - #define LUA_KEY_INDEX -2 - #define LUA_VALUE_INDEX -1 - - int idx; - int tableType; - json_object *tableJ= NULL; - - lua_pushnil(luaState); // 1st key - if (index < 0) index--; - for (idx=1; lua_next(luaState, index) != 0; idx++) { - - // uses 'key' (at index -2) and 'value' (at index -1) - if (lua_type(luaState,LUA_KEY_INDEX) == LUA_TSTRING) { - - if (!tableJ) { - tableJ= json_object_new_object(); - tableType=LUA_TSTRING; - } else if (tableType != LUA_TSTRING){ - AFB_ERROR("MIX Lua Table with key string/numeric not supported"); - return NULL; - } - - const char *key= lua_tostring(luaState, LUA_KEY_INDEX); - json_object *argJ= LuaPopOneArg(luaState, LUA_VALUE_INDEX); - json_object_object_add(tableJ, key, argJ); - - } else { - if (!tableJ) { - tableJ= json_object_new_array(); - tableType=LUA_TNUMBER; - } else if(tableType != LUA_TNUMBER) { - AFB_ERROR("MIX Lua Table with key numeric/string not supported"); - return NULL; - } - - json_object *argJ= LuaPopOneArg(luaState, LUA_VALUE_INDEX); - json_object_array_add(tableJ, argJ); - } - - - lua_pop(luaState, 1); // removes 'value'; keeps 'key' for next iteration - } - - // Query is empty free empty json object - if (idx == 1) { - json_object_put(tableJ); - return NULL; - } - return tableJ; -} - -static json_object *LuaPopOneArg (lua_State* luaState, int idx) { - json_object *value=NULL; - - int luaType = lua_type(luaState, idx); - switch(luaType) { - case LUA_TNUMBER: { - lua_Number number= lua_tonumber(luaState, idx);; - int nombre = (int)number; // evil trick to determine wether n fits in an integer. (stolen from ltcl.c) - if (number == nombre) { - value= json_object_new_int((int)number); - } else { - value= json_object_new_double(number); - } - break; - } - case LUA_TBOOLEAN: - value= json_object_new_boolean(lua_toboolean(luaState, idx)); - break; - case LUA_TSTRING: - value= json_object_new_string(lua_tostring(luaState, idx)); - break; - case LUA_TTABLE: - value= LuaTableToJson(luaState, idx); - break; - case LUA_TNIL: - value=json_object_new_string("nil") ; - break; - case LUA_TUSERDATA: - value=json_object_new_int64((int64_t)lua_touserdata(luaState, idx)); // store userdata as int !!! - break; - - default: - AFB_NOTICE ("LuaPopOneArg: script returned Unknown/Unsupported idx=%d type:%d/%s", idx, luaType, lua_typename(luaState, luaType)); - value=NULL; - } - - return value; -} - -static json_object *LuaPopArgs (lua_State* luaState, int start) { - json_object *responseJ; - - int stop = lua_gettop(luaState); - if(stop-start <0) goto OnErrorExit; - - // start at 2 because we are using a function array lib - if (start == stop) { - responseJ=LuaPopOneArg (luaState, start); - } else { - // loop on remaining return arguments - responseJ= json_object_new_array(); - for (int idx=start; idx <= stop; idx++) { - json_object *argJ=LuaPopOneArg (luaState, idx); - if (!argJ) goto OnErrorExit; - json_object_array_add(responseJ, argJ); - } - } - - return responseJ; - - OnErrorExit: - return NULL; -} - - -static int LuaFormatMessage(lua_State* luaState, LuaAfbMessageT action) { - char *message; - - json_object *responseJ= LuaPopArgs(luaState, LUA_FIST_ARG); - - if (!responseJ) { - luaL_error(luaState,"LuaFormatMessage empty message"); - goto OnErrorExit; - } - - // if we have only on argument just return the value. - if (json_object_get_type(responseJ)!=json_type_array || json_object_array_length(responseJ) <2) { - message= (char*)json_object_get_string(responseJ); - goto PrintMessage; - } - - // extract format and push all other parameter on the stack - message = alloca (LUA_MSG_MAX_LENGTH); - const char *format = json_object_get_string(json_object_array_get_idx(responseJ, 0)); - - int arrayIdx=1; - int targetIdx=0; - - for (int idx=0; format[idx] !='\0'; idx++) { - - if (format[idx]=='%' && format[idx] !='\0') { - json_object *slotJ= json_object_array_get_idx(responseJ, arrayIdx); - //if (slotJ) AFB_NOTICE("**** idx=%d slotJ=%s", arrayIdx, json_object_get_string(slotJ)); - - - switch (format[++idx]) { - case 'd': - if (slotJ) targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%d", json_object_get_int(slotJ)); - else targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"nil"); - arrayIdx++; - break; - case 'f': - if (slotJ) targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%f", json_object_get_double(slotJ)); - else targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"nil"); - arrayIdx++; - break; - - case'%': - message[targetIdx]='%'; - targetIdx++; - break; - - case 's': - default: - if (slotJ) targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"%s", json_object_get_string(slotJ)); - else targetIdx += snprintf (&message[targetIdx], LUA_MSG_MAX_LENGTH-targetIdx,"nil"); - arrayIdx++; - } - - } else { - if (targetIdx >= LUA_MSG_MAX_LENGTH) { - AFB_WARNING ("LuaFormatMessage: message[%s] owerverflow LUA_MSG_MAX_LENGTH=%d", format, LUA_MSG_MAX_LENGTH); - targetIdx --; // move backward for EOL - break; - } else { - message[targetIdx++] = format[idx]; - } - } - } - message[targetIdx]='\0'; - -PrintMessage: - switch (action) { - case AFB_MSG_WARNING: - AFB_WARNING ("%s", message); - break; - case AFB_MSG_NOTICE: - AFB_NOTICE ("%s", message); - break; - case AFB_MSG_DEBUG: - AFB_DEBUG ("%s", message); - break; - case AFB_MSG_INFO: - AFB_INFO ("%s", message); - break; - case AFB_MSG_ERROR: - default: - AFB_ERROR ("%s", message); - } - return 0; // nothing return to lua - - OnErrorExit: // on argument to return (the error message) - return 1; -} - -static int LuaPrintInfo(lua_State* luaState) { - int err=LuaFormatMessage (luaState, AFB_MSG_INFO); - return err; -} - -static int LuaPrintError(lua_State* luaState) { - int err=LuaFormatMessage (luaState, AFB_MSG_ERROR); - return err; // no value return -} - -static int LuaPrintWarning(lua_State* luaState) { - int err=LuaFormatMessage (luaState, AFB_MSG_WARNING); - return err; -} - -static int LuaPrintNotice(lua_State* luaState) { - int err=LuaFormatMessage (luaState, AFB_MSG_NOTICE); - return err; -} - -static int LuaPrintDebug(lua_State* luaState) { - int err=LuaFormatMessage (luaState, AFB_MSG_DEBUG); - return err; -} - -static int LuaAfbSuccess(lua_State* luaState) { - LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); - if (!afbContext) goto OnErrorExit; - - // ignore context argument - json_object *responseJ= LuaPopArgs(luaState, LUA_FIST_ARG+1); - if (responseJ == JSON_ERROR) return 1; - - afb_req_success(afbContext->request, responseJ, NULL); - - LuaCtxFree(afbContext); - return 0; - - OnErrorExit: - lua_error(luaState); - return 1; -} - -static int LuaAfbFail(lua_State* luaState) { - LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); - if (!afbContext) goto OnErrorExit; - - json_object *responseJ= LuaPopArgs(luaState, LUA_FIST_ARG+1); - if (responseJ == JSON_ERROR) return 1; - - afb_req_fail(afbContext->request, afbContext->info, json_object_get_string(responseJ)); - - LuaCtxFree(afbContext); - return 0; - - OnErrorExit: - lua_error(luaState); - return 1; -} - -static void LuaAfbServiceCB(void *handle, int iserror, struct json_object *responseJ) { - LuaCallServiceT *contextCB= (LuaCallServiceT*)handle; - int count=1; - - lua_getglobal(luaState, contextCB->callback); - - // push error status & response - lua_pushboolean(luaState, iserror); - count+= LuaPushArgument(responseJ); - count+= LuaPushArgument(contextCB->context); - - int err=lua_pcall(luaState, count, LUA_MULTRET, 0); - if (err) { - AFB_ERROR ("LUA-SERICE-CB:FAIL response=%s err=%s", json_object_get_string(responseJ), lua_tostring(luaState,-1) ); - } - - free (contextCB); -} - - -static int LuaAfbService(lua_State* luaState) { - int count = lua_gettop(luaState); - - // note: argument start at 2 because of AFB: table - if (count <5 || !lua_isstring(luaState, 2) || !lua_isstring(luaState, 3) || !lua_istable(luaState, 4) || !lua_isstring(luaState, 5)) { - lua_pushliteral (luaState, "ERROR: syntax AFB:service(api, verb, {[Lua Table]})"); - goto OnErrorExit; - } - - // get api/verb+query - const char *api = lua_tostring(luaState,2); - const char *verb= lua_tostring(luaState,3); - json_object *queryJ= LuaTableToJson(luaState, 4); - if (queryJ == JSON_ERROR) return 1; - - LuaCallServiceT *contextCB = calloc (1, sizeof(LuaCallServiceT)); - contextCB->callback= lua_tostring(luaState, 5); - contextCB->context = LuaPopArgs(luaState, 6); - - afb_service_call(api, verb, queryJ, LuaAfbServiceCB, contextCB); - - return 0; // no value return - - OnErrorExit: - lua_error(luaState); - return 1; -} - -static int LuaAfbServiceSync(lua_State* luaState) { - int count = lua_gettop(luaState); - json_object *responseJ; - - // note: argument start at 2 because of AFB: table - if (count <3 || !lua_isstring(luaState, 2) || !lua_isstring(luaState, 3) || !lua_istable(luaState, 4)) { - lua_pushliteral (luaState, "ERROR: syntax AFB:servsync(api, verb, {[Lua Table]})"); - goto OnErrorExit; - } - - // get api/verb+query - const char *api = lua_tostring(luaState,2); - const char *verb= lua_tostring(luaState,3); - json_object *queryJ= LuaTableToJson(luaState, 4); - - int iserror=afb_service_call_sync (api, verb, queryJ, &responseJ); - - // push error status & response - count=1; lua_pushboolean(luaState, iserror); - count+= LuaPushArgument(responseJ); - - return count; // return count values - - OnErrorExit: - lua_error(luaState); - return 1; -} - -static int LuaAfbEventPush(lua_State* luaState) { - LuaAfbEvent *afbevt; - int index; - - // if no private event handle then use default binding event - if (lua_islightuserdata(luaState, LUA_FIST_ARG)) { - afbevt = (LuaAfbEvent*) lua_touserdata(luaState, LUA_FIST_ARG); - index=LUA_FIST_ARG+1; - } else { - index=LUA_FIST_ARG; - afbevt=luaDefaultEvt; - } - - if (!afb_event_is_valid(afbevt->event)) { - lua_pushliteral (luaState, "LuaAfbMakePush-Fail invalid event"); - goto OnErrorExit; - } - - json_object *ctlEventJ= LuaTableToJson(luaState, index); - if (!ctlEventJ) { - lua_pushliteral (luaState, "LuaAfbEventPush-Syntax is AFB:signal ([evtHandle], {lua table})"); - goto OnErrorExit; - } - - int done = afb_event_push(afbevt->event, ctlEventJ); - if (!done) { - lua_pushliteral (luaState, "LuaAfbEventPush-Fail No Subscriber to event"); - AFB_ERROR ("LuaAfbEventPush-Fail name subscriber event=%s count=%d", afbevt->name, afbevt->count); - goto OnErrorExit; - } - afbevt->count++; - return 0; - - OnErrorExit: - lua_error(luaState); - return 1; -} - -static int LuaAfbEventSubscribe(lua_State* luaState) { - LuaAfbEvent *afbevt; - - LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); - if (!afbContext) { - lua_pushliteral (luaState, "LuaAfbEventSubscribe-Fail Invalid request handle"); - goto OnErrorExit; - } - - // if no private event handle then use default binding event - if (lua_islightuserdata(luaState, LUA_FIST_ARG+1)) { - afbevt = (LuaAfbEvent*) lua_touserdata(luaState, LUA_FIST_ARG+1); - } else { - afbevt=luaDefaultEvt; - } - - if (!afb_event_is_valid(afbevt->event)) { - lua_pushliteral (luaState, "LuaAfbMakePush-Fail invalid event handle"); - goto OnErrorExit; - } - - int err = afb_req_subscribe(afbContext->request, afbevt->event); - if (err) { - lua_pushliteral (luaState, "LuaAfbEventSubscribe-Fail No Subscriber to event"); - AFB_ERROR ("LuaAfbEventPush-Fail name subscriber event=%s count=%d", afbevt->name, afbevt->count); - goto OnErrorExit; - } - afbevt->count++; - return 0; - - OnErrorExit: - lua_error(luaState); - return 1; -} - -static int LuaAfbEventMake(lua_State* luaState) { - int count = lua_gettop(luaState); - LuaAfbEvent *afbevt=calloc(1,sizeof(LuaAfbEvent)); - - if (count != LUA_FIST_ARG || !lua_isstring(luaState, LUA_FIST_ARG)) { - lua_pushliteral (luaState, "LuaAfbEventMake-Syntax is evtHandle= AFB:event ('myEventName')"); - goto OnErrorExit; - } - - // event name should be the only argument - afbevt->name= strdup (lua_tostring(luaState,LUA_FIST_ARG)); - - // create a new binder event - afbevt->event = afb_daemon_make_event(afbevt->name); - if (!afb_event_is_valid(afbevt->event)) { - lua_pushliteral (luaState, "LuaAfbEventMake-Fail to Create Binder event"); - goto OnErrorExit; - } - - // push event handler as a LUA opaque handle - lua_pushlightuserdata(luaState, afbevt); - return 1; - - OnErrorExit: - lua_error(luaState); - return 1; -} - -// Function call from LUA when lua2c plugin L2C is used -int Lua2cWrapper(lua_State* luaState, char *funcname, Lua2cFunctionT callback, void *context) { - - json_object *argsJ= LuaPopArgs(luaState, LUA_FIST_ARG+1); - int response = (*callback) (funcname, argsJ, context); - - // push response to LUA - lua_pushinteger(luaState, response); - return 1; -} - -// Generated some fake event based on watchdog/counter -int LuaCallFunc (DispatchSourceT source, DispatchActionT *action, json_object *queryJ) { - - int err, count; - - json_object* argsJ = action->argsJ; - const char* func = action->call; - - // load function (should exist in CONTROL_PATH_LUA - lua_getglobal(luaState, func); - - // push source on the stack - count=1; - lua_pushinteger(luaState, source); - - // push argsJ on the stack - if (!argsJ) { - lua_pushnil(luaState); - count++; - } else { - count+= LuaPushArgument (argsJ); - } - - // push queryJ on the stack - if (!queryJ) { - lua_pushnil(luaState); - count++; - } else { - count+= LuaPushArgument (queryJ); - } - - // effectively exec LUA script code - err=lua_pcall(luaState, count, 1, 0); - if (err) { - AFB_ERROR("LuaCallFunc Fail calling %s error=%s", func, lua_tostring(luaState,-1)); - return -1; - } - - // return LUA script value - int rc= (int)lua_tointeger(luaState, -1); - return rc; -} - -// Execute LUA code from received API request -static void LuaDoAction (LuaDoActionT action, afb_req request) { - - int err, count=0; - - json_object* queryJ = afb_req_json(request); - - switch (action) { - - case LUA_DOSTRING: { - const char *script = json_object_get_string(queryJ); - err=luaL_loadstring(luaState, script); - if (err) { - AFB_ERROR ("LUA-DO-COMPILE:FAIL String=%s err=%s", script, lua_tostring(luaState,-1) ); - goto OnErrorExit; - } - // Push AFB client context on the stack - LuaAfbContextT *afbContext= LuaCtxPush(luaState, request,NULL,script); - if (!afbContext) goto OnErrorExit; - - break; - } - - case LUA_DOCALL: { - const char *func; - json_object *argsJ=NULL; - - err= wrap_json_unpack (queryJ, "{s:s, s?o !}", "target", &func, "args", &argsJ); - if (err) { - AFB_ERROR ("LUA-DOCALL-SYNTAX missing target|args query=%s", json_object_get_string(queryJ)); - goto OnErrorExit; - } - - // load function (should exist in CONTROL_PATH_LUA - lua_getglobal(luaState, func); - - // Push AFB client context on the stack - LuaAfbContextT *afbContext= LuaCtxPush(luaState, request, NULL, func); - if (!afbContext) goto OnErrorExit; - - // push query on the stack - if (!argsJ) { - lua_pushnil(luaState); - count++; - } else { - count+= LuaPushArgument (argsJ); - } - - break; - } - - case LUA_DOSCRIPT: { // Fulup need to fix argument passing - char *filename; char*fullpath; - char luaScriptPath[CONTROL_MAXPATH_LEN]; - int index; - - // scan luascript search path once - static json_object *luaScriptPathJ =NULL; - - // extract value from query - const char *target=NULL,*func=NULL; - json_object *argsJ=NULL; - err= wrap_json_unpack (queryJ, "{s:s,s?s,s?s,s?o !}","target", &target,"path",&luaScriptPathJ,"function",&func,"args",&argsJ); - if (err) { - AFB_ERROR ("LUA-DOSCRIPT-SYNTAX:missing target|[path]|[function]|[args] query=%s", json_object_get_string(queryJ)); - goto OnErrorExit; - } - - // search for filename=script in CONTROL_LUA_PATH - if (!luaScriptPathJ) { - strncpy(luaScriptPath, CONTROL_DOSCRIPT_PRE, sizeof(luaScriptPath)); - strncat(luaScriptPath,"-", sizeof(luaScriptPath)-strlen(luaScriptPath)-1); - strncat(luaScriptPath,target, sizeof(luaScriptPath)-strlen(luaScriptPath)-1); - luaScriptPathJ= ScanForConfig(strncat(GetBindingDirPath(), "/etc", sizeof(GetBindingDirPath()) - strlen(GetBindingDirPath()) - 1), CTL_SCAN_RECURSIVE,luaScriptPath,".lua"); - } - for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { - json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); - - err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "fullpath", &fullpath,"filename", &filename); - if (err) { - AFB_ERROR ("LUA-DOSCRIPT-SCAN:HOOPs invalid config file path = %s", json_object_get_string(entryJ)); - goto OnErrorExit; - } - - if (index > 0) AFB_WARNING("LUA-DOSCRIPT-SCAN:Ignore second script=%s path=%s", filename, fullpath); - else { - strncpy (luaScriptPath, fullpath, sizeof(luaScriptPath)); - strncat (luaScriptPath, "/", sizeof(luaScriptPath)-strlen(luaScriptPath)-1); - strncat (luaScriptPath, filename, sizeof(luaScriptPath)-strlen(luaScriptPath)-1); - } - } - - err= luaL_loadfile(luaState, luaScriptPath); - if (err) { - AFB_ERROR ("LUA-DOSCRIPT HOOPs Error in LUA loading scripts=%s err=%s", luaScriptPath, lua_tostring(luaState,-1)); - goto OnErrorExit; - } - - // script was loaded we need to parse to make it executable - err=lua_pcall(luaState, 0, 0, 0); - if (err) { - AFB_ERROR ("LUA-DOSCRIPT:FAIL to load %s", luaScriptPath); - goto OnErrorExit; - } - - // if no func name given try to deduct from filename - if (!func && (func=(char*)GetMidleName(filename))!=NULL) { - strncpy(luaScriptPath,"_", sizeof(luaScriptPath)); - strncat(luaScriptPath,func, sizeof(luaScriptPath)-strlen(luaScriptPath)-1); - func=luaScriptPath; - } - if (!func) { - AFB_ERROR ("LUA-DOSCRIPT:FAIL to deduct funcname from %s", filename); - goto OnErrorExit; - } - - // load function (should exist in CONTROL_PATH_LUA - lua_getglobal(luaState, func); - - // Push AFB client context on the stack - LuaAfbContextT *afbContext= LuaCtxPush(luaState, request, NULL, func); - if (!afbContext) goto OnErrorExit; - - // push function arguments - if (!argsJ) { - lua_pushnil(luaState); - count++; - } else { - count+= LuaPushArgument(argsJ); - } - - break; - } - - default: - AFB_ERROR ("LUA-DOSCRIPT-ACTION unknown query=%s", json_object_get_string(queryJ)); - goto OnErrorExit; - } - - // effectively exec LUA code (afb_reply/fail done later from callback) - err=lua_pcall(luaState, count+1, 0, 0); - if (err) { - AFB_ERROR ("LUA-DO-EXEC:FAIL query=%s err=%s", json_object_get_string(queryJ), lua_tostring(luaState,-1)); - goto OnErrorExit; - } - return; - - OnErrorExit: - afb_req_fail(request,"LUA:ERROR", lua_tostring(luaState,-1)); - return; -} - -void ctlapi_execlua (afb_req request) { - LuaDoAction (LUA_DOSTRING, request); -} - -void ctlapi_request (afb_req request) { - LuaDoAction (LUA_DOCALL, request); -} - -void ctlapi_debuglua (afb_req request) { - LuaDoAction (LUA_DOSCRIPT, request); -} - -static int LuaTimerClear (lua_State* luaState) { - - // Get Timer Handle - LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); - if (!afbContext) return -1; - - // retrieve useful information opaque handle - TimerHandleT *timerHandle = (TimerHandleT*)afbContext->handle; - - AFB_NOTICE ("LuaTimerClear timer=%s", timerHandle->label); - TimerEvtStop(timerHandle); - - return 0; //happy end -} -static int LuaTimerGet (lua_State* luaState) { - - // Get Timer Handle - LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG); - if (!afbContext) return 0; - - // retrieve useful information opaque handle - TimerHandleT *timerHandle = (TimerHandleT*)afbContext->handle; - - // create response as a JSON object - json_object *responseJ= json_object_new_object(); - json_object_object_add(responseJ,"label", json_object_new_string(timerHandle->label)); - json_object_object_add(responseJ,"delay", json_object_new_int(timerHandle->delay)); - json_object_object_add(responseJ,"count", json_object_new_int(timerHandle->count)); - - // return JSON object as Lua table - int count=LuaPushArgument(responseJ); - - // free json object - json_object_put(responseJ); - - return count; // return argument -} - -// Timer Callback - -// Set timer -static int LuaTimerSetCB (void *handle) { - LuaCallServiceT *contextCB =(LuaCallServiceT*) handle; - TimerHandleT *timerHandle = (TimerHandleT*) contextCB->handle; - int count; - - // push timer handle and user context on Lua stack - lua_getglobal(luaState, contextCB->callback); - - // Push timer handle - LuaAfbContextT *afbContext= LuaCtxPush(luaState, NULL_AFBREQ, contextCB->handle, timerHandle->label); - if (!afbContext) return 1; - count=1; - - // Push user Context - count+= LuaPushArgument(contextCB->context); - - int err=lua_pcall(luaState, count, LUA_MULTRET, 0); - if (err) { - AFB_ERROR ("LUA-TIMER-CB:FAIL response=%s err=%s", json_object_get_string(contextCB->context), lua_tostring(luaState,-1)); - return 1; - } - - // get return parameter - if (!lua_isboolean(luaState, -1)) { - return (lua_toboolean(luaState, -1)); - } - - // timer last run free context resource - if (timerHandle->count == 1) { - LuaCtxFree(afbContext); - } - return 0; // By default we are happy -} - -static int LuaTimerSet(lua_State* luaState) { - const char *label=NULL, *info=NULL; - int delay=0, count=0; - - json_object *timerJ = LuaPopOneArg(luaState, LUA_FIST_ARG); - const char *callback = lua_tostring(luaState, LUA_FIST_ARG + 1); - json_object *contextJ = LuaPopOneArg(luaState, LUA_FIST_ARG + 2); - - if (lua_gettop(luaState) != LUA_FIST_ARG+2 || !timerJ || !callback || !contextJ) { - lua_pushliteral(luaState, "LuaTimerSet-Syntax timerset (timerT, 'callback', contextT)"); - goto OnErrorExit; - } - - int err = wrap_json_unpack(timerJ, "{ss, s?s si, si !}", "label", &label, "info", &info, "delay", &delay, "count", &count); - if (err) { - lua_pushliteral(luaState, "LuaTimerSet-Syntax timerT={label:xxx delay:ms, count:xx}"); - goto OnErrorExit; - } - - // everything look fine create timer structure - TimerHandleT *timerHandle = malloc (sizeof (TimerHandleT)); - timerHandle->delay=delay; - timerHandle->count=count; - timerHandle->label=label; - - // Allocate handle to store context and callback - LuaCallServiceT *contextCB = calloc (1, sizeof(LuaCallServiceT)); - contextCB->callback= callback; - contextCB->context = contextJ; - contextCB->handle = timerHandle; - - // fire timer - TimerEvtStart (timerHandle, LuaTimerSetCB, contextCB); - - return 0; // Happy No Return Function - -OnErrorExit: - lua_error(luaState); - return 1; // return error code -} - -// Register a new L2c list of LUA user plugin commands -void LuaL2cNewLib(const char *label, luaL_Reg *l2cFunc, int count) { - // luaL_newlib(luaState, l2cFunc); macro does not work with pointer :( - luaL_checkversion(luaState); - lua_createtable(luaState, 0, count+1); - luaL_setfuncs(luaState,l2cFunc,0); - lua_setglobal(luaState, label); -} - -static const luaL_Reg afbFunction[] = { - {"timerclear", LuaTimerClear}, - {"timerget" , LuaTimerGet}, - {"timerset" , LuaTimerSet}, - {"notice" , LuaPrintNotice}, - {"info" , LuaPrintInfo}, - {"warning" , LuaPrintWarning}, - {"debug" , LuaPrintDebug}, - {"error" , LuaPrintError}, - {"servsync" , LuaAfbServiceSync}, - {"service" , LuaAfbService}, - {"success" , LuaAfbSuccess}, - {"fail" , LuaAfbFail}, - {"subscribe" , LuaAfbEventSubscribe}, - {"evtmake" , LuaAfbEventMake}, - {"evtpush" , LuaAfbEventPush}, - - {NULL, NULL} /* sentinel */ -}; - -// Create Binding Event at Init -int LuaLibInit () { - int err, index; - - // search for default policy config file - char fullprefix[CONTROL_MAXPATH_LEN]; - strncpy (fullprefix, CONTROL_CONFIG_PRE, sizeof(fullprefix)); - strncat (fullprefix, GetBinderName(), sizeof(fullprefix)-strlen(fullprefix)-1); - strncat (fullprefix, "-", sizeof(fullprefix)-strlen(fullprefix)-1); - - const char *dirList= getenv("CONTROL_LUA_PATH"); - if (!dirList) dirList= strncat(GetBindingDirPath(), "/etc", sizeof(GetBindingDirPath()) - strlen(GetBindingDirPath()) - 1); - - json_object *luaScriptPathJ = ScanForConfig(dirList , CTL_SCAN_RECURSIVE, fullprefix, "lua"); - - // open a new LUA interpretor - luaState = luaL_newstate(); - if (!luaState) { - AFB_ERROR ("LUA_INIT: Fail to open lua interpretor"); - goto OnErrorExit; - } - - // load auxiliary libraries - luaL_openlibs(luaState); - - // redirect print to AFB_NOTICE - luaL_newlib(luaState, afbFunction); - lua_setglobal(luaState, "AFB"); - - // create default lua event to send test pause/resume - luaDefaultEvt=calloc(1,sizeof(LuaAfbEvent)); - luaDefaultEvt->name=CONTROL_LUA_EVENT; - luaDefaultEvt->event = afb_daemon_make_event(CONTROL_LUA_EVENT); - if (!afb_event_is_valid(luaDefaultEvt->event)) { - AFB_ERROR ("POLCTL_INIT: Cannot register lua-events=%s ", CONTROL_LUA_EVENT); - goto OnErrorExit; - } - - // load+exec any file found in LUA search path - for (index=0; index < json_object_array_length(luaScriptPathJ); index++) { - json_object *entryJ=json_object_array_get_idx(luaScriptPathJ, index); - - char *filename; char*fullpath; - err= wrap_json_unpack (entryJ, "{s:s, s:s !}", "fullpath", &fullpath,"filename", &filename); - if (err) { - AFB_ERROR ("LUA-INIT HOOPs invalid config file path = %s", json_object_get_string(entryJ)); - goto OnErrorExit; - } - - char filepath[CONTROL_MAXPATH_LEN]; - strncpy(filepath, fullpath, sizeof(filepath)); - strncat(filepath, "/", sizeof(filepath)-strlen(filepath)-1); - strncat(filepath, filename, sizeof(filepath)-strlen(filepath)-1); - err= luaL_loadfile(luaState, filepath); - if (err) { - AFB_ERROR ("LUA-LOAD HOOPs Error in LUA loading scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); - goto OnErrorExit; - } - - // exec/compil script - err = lua_pcall(luaState, 0, 0, 0); - if (err) { - AFB_ERROR ("LUA-LOAD HOOPs Error in LUA exec scripts=%s err=%s", filepath, lua_tostring(luaState,-1)); - goto OnErrorExit; - } - } - - // no policy config found remove control API from binder - if (index == 0) { - AFB_WARNING ("POLICY-INIT:WARNING (setenv CONTROL_LUA_PATH) No LUA '%s*.lua' in '%s'", fullprefix, dirList); - } - - AFB_DEBUG ("Audio control-LUA Init Done"); - return 0; - - OnErrorExit: - return 1; -} diff --git a/signal-composer-binding/ctl-lua.h b/signal-composer-binding/ctl-lua.h deleted file mode 100644 index c5f472d..0000000 --- a/signal-composer-binding/ctl-lua.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifdef SUSE_LUA_INCDIR - #include - #include - #include -#else - #include - #include - #include -#endif - -#include - -#include "signal-composer-binding.hpp" - -typedef enum { - LUA_DOCALL, - LUA_DOSTRING, - LUA_DOSCRIPT, -} LuaDoActionT; - -typedef int (*Lua2cFunctionT)(char *funcname, json_object *argsJ, void*context); - -typedef int (*Lua2cWrapperT) (lua_State* luaState, char *funcname, Lua2cFunctionT callback); - -int LuaLibInit (); -void LuaL2cNewLib(const char *label, luaL_Reg *l2cFunc, int count); -int Lua2cWrapper(lua_State* luaState, char *funcname, Lua2cFunctionT callback, void *context); -int LuaCallFunc (DispatchSourceT source, DispatchActionT *action, json_object *queryJ) ; diff --git a/signal-composer-binding/observer.hpp b/signal-composer-binding/observer.hpp new file mode 100644 index 0000000..e4129c6 --- /dev/null +++ b/signal-composer-binding/observer.hpp @@ -0,0 +1,30 @@ +#include + +class Observer +{ +public: + virtual void update(double timestamp, double value) = 0; +}; + +class Subject +{ + double timestamp_; + double value_; + std::vector m_views; +public: + void attach(Observer *obs) + { + m_views.push_back(obs); + } + void set_val(double timestamp, double value) + { + timestamp_ = timestamp; + value_ = value; + notify(); + } + void notify() + { + for (int i = 0; i < m_views.size(); ++i) + m_views[i]->update(timestamp_, value_); + } +}; diff --git a/signal-composer-binding/signal-composer-apidef.h b/signal-composer-binding/signal-composer-apidef.h index a652de5..b480430 100644 --- a/signal-composer-binding/signal-composer-apidef.h +++ b/signal-composer-binding/signal-composer-apidef.h @@ -1,19 +1,19 @@ -static const char _afb_description_v2_signals_composer[] = +static const char _afb_description_v2_signal_composer[] = "{\"openapi\":\"3.0.0\",\"$schema\":\"http:iot.bzh/download/openapi/schem" "a-3.0/default-schema.json\",\"info\":{\"description\":\"\",\"title\":\"s" "ignals-composer-service\",\"version\":\"4.0\",\"x-binding-c-generator\":" - "{\"api\":\"signals-composer\",\"version\":2,\"prefix\":\"\",\"postfix\":" - "\"\",\"start\":null,\"onevent\":\"onEvent\",\"preinit\":\"loadConf\",\"i" - "nit\":\"execConf\",\"scope\":\"\",\"private\":false}},\"servers\":[{\"ur" - "l\":\"ws://{host}:{port}/api/monitor\",\"description\":\"Signals compose" - "r API connected to low level AGL services\",\"variables\":{\"host\":{\"d" - "efault\":\"localhost\"},\"port\":{\"default\":\"1234\"}},\"x-afb-events\"" - ":[{\"$ref\":\"#/components/schemas/afb-event\"}]}],\"components\":{\"sch" - "emas\":{\"afb-reply\":{\"$ref\":\"#/components/schemas/afb-reply-v2\"},\"" - "afb-event\":{\"$ref\":\"#/components/schemas/afb-event-v2\"},\"afb-reply" - "-v2\":{\"title\":\"Generic response.\",\"type\":\"object\",\"required\":" - "[\"jtype\",\"request\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"" + "{\"api\":\"signal-composer\",\"version\":2,\"prefix\":\"\",\"postfix\":\"" + "\",\"start\":null,\"onevent\":\"onEvent\",\"preinit\":\"loadConf\",\"ini" + "t\":\"execConf\",\"scope\":\"\",\"private\":false}},\"servers\":[{\"url\"" + ":\"ws://{host}:{port}/api/monitor\",\"description\":\"Signals composer A" + "PI connected to low level AGL services\",\"variables\":{\"host\":{\"defa" + "ult\":\"localhost\"},\"port\":{\"default\":\"1234\"}},\"x-afb-events\":[" + "{\"$ref\":\"#/components/schemas/afb-event\"}]}],\"components\":{\"schem" + "as\":{\"afb-reply\":{\"$ref\":\"#/components/schemas/afb-reply-v2\"},\"a" + "fb-event\":{\"$ref\":\"#/components/schemas/afb-event-v2\"},\"afb-reply-" + "v2\":{\"title\":\"Generic response.\",\"type\":\"object\",\"required\":[" + "\"jtype\",\"request\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"" "const\":\"afb-reply\"},\"request\":{\"type\":\"object\",\"required\":[\"" "status\"],\"properties\":{\"status\":{\"type\":\"string\"},\"info\":{\"t" "ype\":\"string\"},\"token\":{\"type\":\"string\"},\"uuid\":{\"type\":\"s" @@ -44,7 +44,7 @@ static const char _afb_description_v2_signals_composer[] = void get(struct afb_req req); void loadConf(struct afb_req req); -static const struct afb_verb_v2 _afb_verbs_v2_signals_composer[] = { +static const struct afb_verb_v2 _afb_verbs_v2_signal_composer[] = { { .verb = "subscribe", .callback = subscribe, @@ -83,10 +83,10 @@ static const struct afb_verb_v2 _afb_verbs_v2_signals_composer[] = { }; const struct afb_binding_v2 afbBindingV2 = { - .api = "signals-composer", - .specification = _afb_description_v2_signals_composer, + .api = "signal-composer", + .specification = _afb_description_v2_signal_composer, .info = "", - .verbs = _afb_verbs_v2_signals_composer, + .verbs = _afb_verbs_v2_signal_composer, .preinit = loadConf, .init = execConf, .onevent = onEvent, diff --git a/signal-composer-binding/signal-composer-apidef.json b/signal-composer-binding/signal-composer-apidef.json index 7085dc2..c9e898e 100644 --- a/signal-composer-binding/signal-composer-apidef.json +++ b/signal-composer-binding/signal-composer-apidef.json @@ -6,7 +6,7 @@ "title": "signals-composer-service", "version": "4.0", "x-binding-c-generator": { - "api": "signals-composer", + "api": "signal-composer", "version": 2, "prefix": "", "postfix": "", diff --git a/signal-composer-binding/signal-composer-binding.cpp b/signal-composer-binding/signal-composer-binding.cpp index 2ca1dec..90401f2 100644 --- a/signal-composer-binding/signal-composer-binding.cpp +++ b/signal-composer-binding/signal-composer-binding.cpp @@ -13,27 +13,27 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ +*/ -#include +#include +// afb-utilities +#include +#include +// controller +#include #include "signal-composer-binding.hpp" #include "signal-composer-apidef.h" -#include "wrap-json.h" #include "signal-composer.hpp" -SignalComposer SigComp; -static CtlConfigT *ctlConfig=NULL; - /// @brief callback for receiving message from low binding. Treatment itself is made in SigComp class. void onEvent(const char *event, json_object *object) { - SigComp.treatMessage(object); } /// @brief entry point for client subscription request. Treatment itself is made in SigComp class. void subscribe(afb_req request) { - if(SigComp.subscribe(request)) + if(true) afb_req_success(request, NULL, NULL); else afb_req_fail(request, "error", NULL); @@ -42,7 +42,7 @@ void subscribe(afb_req request) /// @brief entry point for client un-subscription request. Treatment itself is made in SigComp class. void unsubscribe(afb_req request) { - if(SigComp.unsubscribe(request)) + if(true) afb_req_success(request, NULL, NULL); else afb_req_fail(request, "error when unsubscribe", NULL); @@ -55,7 +55,7 @@ void loadConf(afb_req request) const char* confd; wrap_json_unpack(args, "{s:s}", "path", &confd); - int ret = SigComp.parseConfigAndSubscribe(confd); + int ret = 0; if( ret == 0) afb_req_success(request, NULL, NULL); else @@ -66,39 +66,49 @@ void loadConf(afb_req request) void get(afb_req request) { json_object *jobj; - if(SigComp.get(request, &jobj)) + if(true) { afb_req_success(request, jobj, NULL); - } else { + } + else + { afb_req_fail(request, "error", NULL); } } -/// @brief entry point for systemD timers. Treatment itself is made in SigComp class. -/// @param[in] source: systemD timer, t: time of tick, data: interval (ms). -int ticked(sd_event_source *source, uint64_t t, void* data) -{ - SigComp.tick(source, t, data); - return 0; -} - int loadConf() { - int errcount=0; + int ret = 0; + const char* rootdir = strncat(GetBindingDirPath(), "/etc", + sizeof(GetBindingDirPath()) - strlen(GetBindingDirPath()) -1); - ctlConfig = CtlConfigLoad(); + bindingApp& bApp = bindingApp::instance(); + bApp.loadConfig(rootdir); #ifdef CONTROL_SUPPORT_LUA - errcount += LuaLibInit(); + ret += LuaConfigLoad(); #endif - return errcount; + return ret; } int execConf() { - int err = CtlConfigExec(); + bindingApp& bApp = bindingApp::instance(); + int ret = CtlConfigExec(bApp.ctlConfig()); + std::vector> allSignals = bApp.getAllSignals(); + ssize_t sigCount = allSignals.size(); + + for(auto& sig: allSignals) + { + if( (ret = sig->recursionCheck()) ) + { + AFB_ERROR("There is an infinite recursion loop in your signals definition. Root coming from signal: %s", sig->id().c_str()); + return ret; + } + } + + AFB_DEBUG("Signal Composer Control configuration Done.\n signals=%d", (int)sigCount); - AFB_DEBUG ("Signal Composer Control configuration Done errcount=%d", errcount); - return errcount; + return ret; } diff --git a/signal-composer-binding/signal-composer-binding.hpp b/signal-composer-binding/signal-composer-binding.hpp index 7bf1df1..e73438b 100644 --- a/signal-composer-binding/signal-composer-binding.hpp +++ b/signal-composer-binding/signal-composer-binding.hpp @@ -1,19 +1,14 @@ #pragma once #include +#include -#ifdef __cplusplus - #include extern "C" { -#endif #define AFB_BINDING_VERSION 2 #include -#ifdef __cplusplus -}; -#endif +} void onEvent(const char *event, struct json_object *object); int loadConf(); int execConf(); -int ticked(sd_event_source *source, uint64_t t, void *data); diff --git a/signal-composer-binding/signal-composer.cpp b/signal-composer-binding/signal-composer.cpp index 73f2450..6f629e1 100644 --- a/signal-composer-binding/signal-composer.cpp +++ b/signal-composer-binding/signal-composer.cpp @@ -13,4 +13,245 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ +*/ + +#include "signal-composer.hpp" + +CtlSectionT bindingApp::ctlSections_[] = { + [0]={.key="sources" ,.label = "sources", .info=nullptr, + .loadCB=loadSources, + .handle=nullptr}, + [1]={.key="signals" , .label= "signals", .info=nullptr, + .loadCB=loadSignals, + .handle=nullptr}, + [2]={.key=nullptr, .label=nullptr, .info=nullptr, + .loadCB=nullptr, + .handle=nullptr} +}; + +bindingApp::bindingApp() +{} + +void bindingApp::loadConfig(const std::string& filepath) +{ + ctlConfig_ = CtlConfigLoad(filepath.c_str(), ctlSections_); +} + +bindingApp& bindingApp::instance() +{ + static bindingApp bApp; + return bApp; +} + +Source* bindingApp::getSource(const std::string& api) +{ + for(auto& source: sourcesList_) + { + if (source.api() == api) + {return &source;} + } + return nullptr; +} + +CtlActionT* bindingApp::convert2Action(const std::string& name, json_object* action) +{ + json_object *functionArgsJ = nullptr; + const char* function; + + if(action && + wrap_json_unpack(action, "{ss,s?o !}", "function", &function, "args", &functionArgsJ)) + { + std::string functionS = function; + json_object* action = nullptr; + ssize_t sep; + if(functionS.find("lua") != std::string::npos) + { + wrap_json_pack(&action, "{ss,s?s,s?o,s?s,s?s,s?s,s?o !}", + "label", name, + "lua", function, + "args", functionArgsJ); + } + else if( (sep = functionS.find_first_of("/")) != std::string::npos) + { + std::string api = functionS.substr(0, sep); + std::string verb = functionS.substr(sep); + wrap_json_pack(&action, "{ss,s?s,s?o,s?s,s?s,s?s,s?o !}", + "label", name, + "api", api, + "verb", verb, + "args", functionArgsJ); + } + else + { + wrap_json_pack(&action, "{ss,s?s,s?o,s?s,s?s,s?s,s?o !}", + "label", name, + "callback", function, + "args", functionArgsJ); + } + } + return ActionLoad(action); +} + +int bindingApp::loadOneSource(json_object* sourceJ) +{ + json_object *initJ = nullptr, *getSignalJ = nullptr; + CtlActionT *initCtl, *getSignalCtl; + 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); + if (err) + { + AFB_ERROR("Missing something api|[info]|[init]|[getSignal] in %s", json_object_get_string(sourceJ)); + return err; + } + + initCtl = convert2Action("init", initJ); + getSignalCtl =convert2Action("getSignal", getSignalJ); + + sourcesList_.push_back(Source(api, info, initCtl, getSignalCtl)); + + return err; +} + +int bindingApp::loadSources(CtlSectionT* section, json_object *sourcesJ) +{ + int err = 0; + bindingApp& bApp = instance(); + // add the signal composer itself as source + json_object *sigCompJ = nullptr; + wrap_json_pack(&sigCompJ, "{ss,ss,so,so !}", + "api", "signal-composer", + "info", "Api on behalf the virtual signals are sent", + "init", nullptr, + "getSignal", nullptr); + + json_object_array_add(sourcesJ, sigCompJ); + + if (json_object_get_type(sourcesJ) == json_type_array) + { + int count = json_object_array_length(sourcesJ); + + for (int idx = 0; idx < count; idx++) + { + json_object *sourceJ = json_object_array_get_idx(sourcesJ, idx); + err = bApp.loadOneSource(sourceJ); + if (err) return err; + } + } + else + { + if ((err = bApp.loadOneSource(sourcesJ))) return err; + if ((err = bApp.loadOneSource(sigCompJ))) return err; + } + + return err; +} + +int bindingApp::loadOneSignal(json_object* signalJ) +{ + json_object *onReceivedJ = nullptr, *sourcesJ = nullptr; + CtlActionT* onReceivedCtl; + const char *id = nullptr, + *sClass = nullptr, + *unit = nullptr; + double frequency; + std::string api; + std::vector sourcesV; + ssize_t sep; + + int err = wrap_json_unpack(signalJ, "{ss,so,s?s,s?s,s?F,s?o !}", + "id", &id, + "source", &sourcesJ, + "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)); + return err; + } + + // Process sources JSON object + // TODO: claneys, really needs to factorize JSON array processing + if (json_object_get_type(sourcesJ) == json_type_array) + { + int count = json_object_array_length(sourcesJ); + for(int i = 0; i < count; i++) + { + std::string sourceStr = json_object_get_string(sourcesJ); + 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); + } + } + else + { + std::string sourceStr = json_object_get_string(sourcesJ); + if( (sep = sourceStr.find("/")) != std::string::npos) + { + api = sourceStr.substr(0, sep); + } + sourcesV.push_back(sourceStr); + } + + // Set default sClass is not specified + sClass = "state"; + + // Get an action handler + onReceivedCtl = convert2Action("onReceived", onReceivedJ); + + Source* src = getSource(api); + if( src != nullptr) + {src->addSignal(id, sourcesV, sClass, unit, frequency, onReceivedCtl);} + else + {err = -1;} + + return err; +} + +int bindingApp::loadSignals(CtlSectionT* section, json_object *signalsJ) +{ + int err = 0; + bindingApp& bApp = instance(); + + if (json_object_get_type(signalsJ) == json_type_array) + { + int count = json_object_array_length(signalsJ); + + for (int idx = 0; idx < count; idx++) + { + json_object *signalJ = json_object_array_get_idx(signalsJ, idx); + err = bApp.loadOneSignal(signalJ); + if (err) return err; + } + } + else + {err = bApp.loadOneSignal(signalsJ);} + + return err; +} + +std::vector> bindingApp::getAllSignals() +{ + std::vector> allSignals; + for( auto& source : sourcesList_) + { + std::vector> srcSignals = source.getSignals(); + allSignals.insert(allSignals.end(), srcSignals.begin(), srcSignals.end()); + } + + return allSignals; +} + +CtlConfigT* bindingApp::ctlConfig() +{ + return ctlConfig_; +} diff --git a/signal-composer-binding/signal-composer.hpp b/signal-composer-binding/signal-composer.hpp index bcfe60f..44b958f 100644 --- a/signal-composer-binding/signal-composer.hpp +++ b/signal-composer-binding/signal-composer.hpp @@ -13,60 +13,46 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ - #pragma once +*/ +#pragma once - #include - #include - #include - #include - #include - #include - #include - extern "C" - { - #define AFB_BINDING_VERSION 2 - #include - }; +#include +#include +#include +#include +#include +#include - struct TimedEvent { - int interval; - afb_event event; - std::string name; - std::string eventName; - }; +#include "source.hpp" +#include "signal-composer-binding.hpp" - struct Signal { - std::string name; - std::string source; - std::string sig_class; - std::string type; - }; +class bindingApp +{ +private: + CtlConfigT* ctlConfig_; - class SignalComposer - { - public: - SignalComposer(); - void treatMessage(json_object *message); - bool subscribe(afb_req request); - bool unsubscribe(afb_req request); - bool get(afb_req request, json_object **json); - void tick(sd_event_source *source, const long &now, void *interv); - void startTimer(const int &t); - ~SignalComposer(); - int parseConfigAndSubscribe(const std::string& confd); - static bool startsWith(const std::string &s, const std::string &val); - static void callBackFromSubscribe(void *handle, int iserror, json_object *result); - private: - std::map events; - std::map> timedEvents; - std::map> registeredObjects; - std::map> lowMessagesToObjects; - std::set timers; - std::string generateId() const; - json_object *generateJson(const std::string &messageObject, std::vector *fields = nullptr); - void registerObjects(const std::string& uri, std::map& properties); - std::map> loadDefinitions(json_object* definitionsJ) const; - void loadResources(json_object* resourcesJ, std::map>& properties); - int subscribeRegisteredObjects() const; - }; + static CtlSectionT ctlSections_[]; ///< Config Section definition (note: controls section index should match handle retrieval in) + std::vector sourcesList_; + + explicit bindingApp(const std::string& filepath); + bindingApp(); + ~bindingApp(); + + CtlActionT* convert2Action(const std::string& name, json_object* action); + + int loadOneSource(json_object* sourcesJ); + static int loadSources(CtlSectionT* section, json_object *sectionJ); + + int loadOneSignal(json_object* signalsJ); + static int loadSignals(CtlSectionT* section, json_object *sectionJ); + + Source* getSource(const std::string& api); + +public: + static bindingApp& instance(); + void loadConfig(const std::string& filepath); + void loadSignalsFile(std::string signalsFile); + + std::vector> getAllSignals(); + CtlConfigT* ctlConfig(); +}; diff --git a/signal-composer-binding/signal.cpp b/signal-composer-binding/signal.cpp new file mode 100644 index 0000000..6b4947e --- /dev/null +++ b/signal-composer-binding/signal.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include "signal.hpp" + +Signal::Signal() +{} + +Signal(std::string& id, + std::vector& sources, + std::string& unit, + float frequency, + CtlActionT* onReceived) +:id_(id), + sources_(sources), + unit_(unit), + frequency_(frequency), + onReceived_(onReceived) +{ + for (const std::string& src: sources) + { + if(src.find("/") == std::string::npos) + { + AFB_NOTICE("Attaching %s to %s", sig->id(), src); + if(sig.attach(src)) + {AFB_WARNING("Can't attach. Is %s exists ?", src);} + } + } +} + +Signal::operator bool() const +{ + if(!id_ || !api_ || !signalName_) + {return false;} + return true; +} + +int Signal::recursionCheck(const std::string& origId) +{ + for (const auto& obs: Observers_) + { + if( id_ == obs.id()) + {return -1;} + if( origId == obs.id()) + {return -1;} + if(! obs.recursionCheck(origId)) + {return -1;} + } + return 0; +} + +std::string Signal::id() const +{ + return id_; +} + +int Signal::recursionCheck() +{ + for (const auto& obs: Observers_) + { + if( id_ == obs.id()) + {return -1;} + if(! obs.recursionCheck(id_)) + {return -1;} + } + return 0; +} + +void update(double timestamp, double value) +{ + AFB_NOTICE("Got an update from observed signal"); +} diff --git a/signal-composer-binding/signal.hpp b/signal-composer-binding/signal.hpp index 94878c9..ad944aa 100644 --- a/signal-composer-binding/signal.hpp +++ b/signal-composer-binding/signal.hpp @@ -17,30 +17,42 @@ #pragma once +#include #include #include #include +#include + +#include "observer.hpp" class Signal; -class Signal +//TODO: claneys: define observer and observable interface then inherit +class Signal: public Observer, public Subject { private: - std::string api_; - std::string name_; - std::vector<> history_; - float frequency_; - st::string unit_; - float min_; - float max_; - float last_; + std::string id_; + std::vector sources_; + std::map history_; ///< history_ - Hold signal value history in map with + double frequency_; + std::string unit_; + CtlActionT* onReceived_; - std::vector> Observers_; + std::vector> observers_; + int recursionCheck(const std::string& origId); public: - void notify(); - void infiniteRecursionCheck(); - void attach(); - void detach(); - void notify(); -} + Signal(); + Signal(const std::string& id, std::vector& sources, const std::string& unit, double frequency, CtlActionT* onReceived); + explicit operator bool() const; + + std::string id() const; + + int recursionCheck(); + /* virtual */ void update(double timestamp, double value); + + virtual double average() const; + virtual double min() const; + virtual double max() const; + double lastValue() const; +}; diff --git a/signal-composer-binding/source.cpp b/signal-composer-binding/source.cpp new file mode 100644 index 0000000..919ae5b --- /dev/null +++ b/signal-composer-binding/source.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include "source.hpp" + +Source::Source() +{} + +Source::Source(const std::string& api, const std::string& info, CtlActionT* init, CtlActionT* getSignal): + api_(api), info_(info), init_(init), getSignal_(getSignal) +{} + +std::string Source::api() +{ + return api_; +} + +void Source::addSignal(const std::string& id, std::vector& sources, const std::string& sClass, const std::string& unit, double frequency, CtlActionT* onReceived) +{ + std::shared_ptr sig = std::make_shared(id, sources, unit, frequency, onReceived); + + signals_.push_back(sig); +} + +std::vector> Source::getSignals() +{ + return signals_; +} diff --git a/signal-composer-binding/source.hpp b/signal-composer-binding/source.hpp new file mode 100644 index 0000000..694daae --- /dev/null +++ b/signal-composer-binding/source.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2015, 2016 "IoT.bzh" + * Author "Romain Forlot" + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#pragma once + +#include +#include +#include + +#include "signal.hpp" + +class Source { +private: + std::string api_; + std::string info_; + CtlActionT* init_; + CtlActionT* getSignal_; + + std::vector> signals_; + +public: + Source(); + Source(const std::string& api, const std::string& info, CtlActionT* init, CtlActionT* getSignal); + + std::string api(); + void addSignal(const std::string& id, std::vector& sources, const std::string& sClass, const std::string& unit, double frequency, CtlActionT* onReceived); + std::vector> getSignals(); +}; -- cgit 1.2.3-korg