diff options
author | Romain Forlot <romain.forlot@iot.bzh> | 2017-09-15 16:29:17 +0200 |
---|---|---|
committer | Romain Forlot <romain.forlot@iot.bzh> | 2017-12-14 11:00:25 +0100 |
commit | 677cde6288a4574b8251a4a532b1d9e1594b09b2 (patch) | |
tree | c510a0cdd76e2b8de1cdaf254fd270c8bfebae42 | |
parent | eabae24ea592420de46e36f0b1af5d39eee5b8a4 (diff) |
Subscription by plugin to signals
Change-Id: Icb923f87df2be8eb664106bc9077b3a8221dd3ce
Signed-off-by: Romain Forlot <romain.forlot@iot.bzh>
-rw-r--r-- | conf.d/project/etc/init-daemon.json | 6 | ||||
-rw-r--r-- | conf.d/project/etc/sig-doors.json | 10 | ||||
-rw-r--r-- | conf.d/project/etc/sources.json | 3 | ||||
-rw-r--r-- | controller/ctl-action.c | 3 | ||||
-rw-r--r-- | controller/ctl-config.c | 109 | ||||
-rw-r--r-- | controller/ctl-plugin.c | 37 | ||||
-rw-r--r-- | plugins/CMakeLists.txt (renamed from low-can/CMakeLists.txt) | 2 | ||||
-rw-r--r-- | plugins/low-can.c (renamed from low-can/doors.c) | 45 | ||||
-rw-r--r-- | signal-composer-binding/signal-composer-binding.cpp | 21 | ||||
-rw-r--r-- | signal-composer-binding/signal-composer.cpp | 37 | ||||
-rw-r--r-- | signal-composer-binding/signal-composer.hpp | 6 | ||||
-rw-r--r-- | signal-composer-binding/signal.cpp | 39 | ||||
-rw-r--r-- | signal-composer-binding/signal.hpp | 7 | ||||
-rw-r--r-- | signal-composer-binding/source.cpp | 26 | ||||
-rw-r--r-- | signal-composer-binding/source.hpp | 4 |
15 files changed, 245 insertions, 110 deletions
diff --git a/conf.d/project/etc/init-daemon.json b/conf.d/project/etc/init-daemon.json index dc18f7e..1b27320 100644 --- a/conf.d/project/etc/init-daemon.json +++ b/conf.d/project/etc/init-daemon.json @@ -10,10 +10,10 @@ }, "plugins": [ { - "label": "Door handle", + "label": "low-can-callbacks", "version": "1.0", - "info": "Manage all doors and windows status", - "basename": "doors" + "info": "Manage interaction with low-can service", + "basename": "low-can" } ] } diff --git a/conf.d/project/etc/sig-doors.json b/conf.d/project/etc/sig-doors.json index bc26296..1859dbe 100644 --- a/conf.d/project/etc/sig-doors.json +++ b/conf.d/project/etc/sig-doors.json @@ -39,7 +39,7 @@ "rear_left_door" ], "onReceived": { - "plugin": "Door handle", + "plugin": "low-can-callbacks", "function": "isOpen", "args": { "ojoi": "pok" @@ -53,7 +53,7 @@ "rear_right_window" ], "onReceived": { - "plugin": "Door handle", + "plugin": "low-can-callbacks", "function": "isOpen", "args": {} } @@ -65,7 +65,7 @@ "front_left_window" ], "onReceived": { - "plugin": "Door handle", + "plugin": "low-can-callbacks", "function": "isOpen", "args": {} } @@ -77,7 +77,7 @@ "front_right_window" ], "onReceived": { - "plugin": "Door handle", + "plugin": "low-can-callbacks", "function": "isOpen", "args": {} } @@ -95,7 +95,7 @@ "rear_right_window" ], "onReceived": { - "plugin": "Door handle", + "plugin": "low-can-callbacks", "function": "isOpen", "args": { "evtname": "doors.open" diff --git a/conf.d/project/etc/sources.json b/conf.d/project/etc/sources.json index a70da50..6a9657a 100644 --- a/conf.d/project/etc/sources.json +++ b/conf.d/project/etc/sources.json @@ -10,7 +10,8 @@ } }, "getSignal": { - "function": "_LUA_Simple_Echo_Args", + "plugin": "low-can-callbacks", + "function": "subscribeToLow", "args": { "arg": "first argument" } diff --git a/controller/ctl-action.c b/controller/ctl-action.c index 5da24e8..8e2db1a 100644 --- a/controller/ctl-action.c +++ b/controller/ctl-action.c @@ -28,7 +28,6 @@ int ActionExecOne(CtlActionT* action, json_object *queryJ) { int err; - switch (action->type) { case CTL_TYPE_API: { @@ -146,7 +145,7 @@ OnErrorExit: }; CtlActionT *ActionLoad(json_object *actionsJ) { - int err; + int err = 0; CtlActionT *actions; // action array is close with a nullvalue; diff --git a/controller/ctl-config.c b/controller/ctl-config.c index 948f0d1..888b420 100644 --- a/controller/ctl-config.c +++ b/controller/ctl-config.c @@ -25,7 +25,6 @@ #include "filescan-utils.h" #include "ctl-config.h" - // Load control config file char* CtlConfigSearch(const char *dirList, const char* fileName) { @@ -69,6 +68,61 @@ char* CtlConfigSearch(const char *dirList, const char* fileName) { return NULL; } +json_object* loadJSON(const char* filepath, const char* fileName) +{ + // Search for config in filepath + const char* filepathFound = CtlConfigSearch(filepath, fileName); + if(!filepathFound) + { + AFB_ERROR("CTL-LOAD-CONFIG No JSON Config found in %s", filepath); + return NULL; + } + return json_object_from_file(filepathFound); +} + +int browseAdditionnalFiles(CtlConfigT* ctlConfig, const char* filepath, int sectionIdx){ + int err = 0; + int done = 0; + const char* fileName; + json_object* configJ = NULL, *sectionJ = NULL; + CtlSectionT* sections = ctlConfig->sections; + + if (json_object_get_type(ctlConfig->filesJ) == json_type_array) { + int filesEnd = json_object_array_length(ctlConfig->filesJ); + for (int jdx = 0; jdx < filesEnd; jdx++) { + fileName = json_object_get_string(json_object_array_get_idx(ctlConfig->filesJ, jdx)); + configJ = loadJSON(filepath, fileName); + if(json_object_object_get_ex(configJ, sections[sectionIdx].key, §ionJ)) + { + err += sections[sectionIdx].loadCB(§ions[sectionIdx], sectionJ); + done++; + } + } + if(!done) { + AFB_ERROR("CtlConfigLoad: fail to find '%s' section in config '%s' with '%s' in its name", sections[sectionIdx].key, filepath, fileName); + err++; + } + if(err) {goto OnErrorExit;} + } else { + fileName = json_object_get_string(ctlConfig->filesJ); + configJ = loadJSON(filepath, fileName); + if(json_object_object_get_ex(configJ, sections[sectionIdx].key, §ionJ)) { + err += sections[sectionIdx].loadCB(§ions[sectionIdx], sectionJ); + done++; + } + else { + AFB_ERROR("CtlConfigLoad: fail to find '%s' section in config '%s' with '%s' in its name", sections[sectionIdx].key, filepath, fileName); + err++; + } + if(err) {goto OnErrorExit;} + } + + return err; + + OnErrorExit: + return err; +} + int CtlConfigExec(CtlConfigT *ctlConfig) { // best effort to initialise everything before starting if (ctlConfig->requireJ) { @@ -113,20 +167,8 @@ CtlConfigT *CtlConfigLoad(const char* filepath, CtlSectionT *sections) { if (err) goto OnErrorExit; #endif - json_object* loadJSON(const char* fileName) - { - // Search for config in filepath - const char* filepathFound = CtlConfigSearch(filepath, fileName); - if(!filepathFound) - { - AFB_ERROR("CTL-LOAD-CONFIG No JSON Config found in %s", filepath); - return NULL; - } - return json_object_from_file(filepathFound); - } - // Load JSON file - ctlConfigJ = loadJSON(NULL); + ctlConfigJ = loadJSON(filepath, NULL); if (!ctlConfigJ) { AFB_ERROR("CTL-LOAD-CONFIG Invalid JSON %s ", filepath); goto OnErrorExit; @@ -161,42 +203,11 @@ CtlConfigT *CtlConfigLoad(const char* filepath, CtlSectionT *sections) { ctlConfig->sections = sections; for (int idx = 0; sections[idx].key != NULL; idx++) { - json_object * sectionJ; + json_object * sectionJ = NULL; int done = json_object_object_get_ex(ctlConfigJ, sections[idx].key, §ionJ); if (!done) { - json_object* configJ = NULL; - const char* fileName; AFB_DEBUG("CtlConfigLoad: fail to find '%s' section in config '%s'. Searching deeper", sections[idx].key, filepath); - if (json_object_get_type(ctlConfig->filesJ) == json_type_array) { - int filesEnd = json_object_array_length(ctlConfig->filesJ); - for (int jdx = 0; jdx < filesEnd; jdx++) { - fileName = json_object_get_string(json_object_array_get_idx(ctlConfig->filesJ, jdx)); - configJ = loadJSON(fileName); - if(json_object_object_get_ex(configJ, sections[idx].key, §ionJ)) - { - const char* k = sections[idx].key; - err += sections[idx].loadCB(§ions[idx], sectionJ); - done++; - } - } - if(!done) { - AFB_ERROR("CtlConfigLoad: fail to find '%s' section in config '%s' with '%s' in its name", sections[idx].key, filepath, fileName); - err++; - } - if(err) {goto OnErrorExit;} - } else { - fileName = json_object_get_string(ctlConfig->filesJ); - configJ = loadJSON(fileName); - if(json_object_object_get_ex(configJ, sections[idx].key, §ionJ)) { - err += sections[idx].loadCB(§ions[idx], sectionJ); - done++; - } - else { - AFB_ERROR("CtlConfigLoad: fail to find '%s' section in config '%s' with '%s' in its name", sections[idx].key, filepath, fileName); - err++; - } - if(err) {goto OnErrorExit;} - } + err += browseAdditionnalFiles(ctlConfig, filepath, idx); } else { err += sections[idx].loadCB(§ions[idx], sectionJ); } @@ -209,7 +220,3 @@ OnErrorExit: free(ctlConfig); return NULL; } - - - - diff --git a/controller/ctl-plugin.c b/controller/ctl-plugin.c index ae203af..b14d591 100644 --- a/controller/ctl-plugin.c +++ b/controller/ctl-plugin.c @@ -153,10 +153,6 @@ STATIC int PluginLoadOne (CtlPluginT *ctlPlugin, json_object *pluginJ, void* han // store dlopen handle to enable onload action at exec time ctlPlugin->dlHandle = dlHandle; - // Jose hack to make verbosity visible from sharedlib - struct afb_binding_data_v2 *afbHidenData = dlsym(dlHandle, "afbBindingV2data"); - if (afbHidenData) *afbHidenData = afbBindingV2data; - // Push lua2cWrapper @ into plugin Lua2cWrapperT *lua2cInPlug = dlsym(dlHandle, "Lua2cWrap"); #ifndef CONTROL_SUPPORT_LUA @@ -219,16 +215,31 @@ OnErrorExit: int PluginConfig(CtlSectionT *section, json_object *pluginsJ) { int err=0; - if (json_object_get_type(pluginsJ) == json_type_array) { - int length = json_object_array_length(pluginsJ); - ctlPlugins = calloc (length+1, sizeof(CtlPluginT)); - for (int idx=0; idx < length; idx++) { - json_object *pluginJ = json_object_array_get_idx(pluginsJ, idx); - err += PluginLoadOne(&ctlPlugins[idx], pluginJ, section->handle); + if (ctlPlugins) + { + int pluginsCount = (sizeof(ctlPlugins) / sizeof(CtlPluginT)) + 1; + for(int idx = 0; idx < pluginsCount; idx++) + { + // Jose hack to make verbosity visible from sharedlib and + // be able to call verb from others api inside the binder + struct afb_binding_data_v2 *afbHidenData = dlsym(ctlPlugins[idx].dlHandle, "afbBindingV2data"); + if (afbHidenData) *afbHidenData = afbBindingV2data; + } + return 0; + } + else + { + if (json_object_get_type(pluginsJ) == json_type_array) { + int length = json_object_array_length(pluginsJ); + ctlPlugins = calloc (length+1, sizeof(CtlPluginT)); + for (int idx=0; idx < length; idx++) { + json_object *pluginJ = json_object_array_get_idx(pluginsJ, idx); + err += PluginLoadOne(&ctlPlugins[idx], pluginJ, section->handle); + } + } else { + ctlPlugins = calloc (2, sizeof(CtlPluginT)); + err += PluginLoadOne(&ctlPlugins[0], pluginsJ, section->handle); } - } else { - ctlPlugins = calloc (2, sizeof(CtlPluginT)); - err += PluginLoadOne(&ctlPlugins[0], pluginsJ, section->handle); } return err; diff --git a/low-can/CMakeLists.txt b/plugins/CMakeLists.txt index e1e1907..106a237 100644 --- a/low-can/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -18,7 +18,7 @@ -PROJECT_TARGET_ADD(doors) +PROJECT_TARGET_ADD(low-can) # Define targets ADD_LIBRARY(${TARGET_NAME} MODULE ${TARGET_NAME}.c) diff --git a/low-can/doors.c b/plugins/low-can.c index ce0b0c9..3988026 100644 --- a/low-can/doors.c +++ b/plugins/low-can.c @@ -51,6 +51,51 @@ CTLP_ONLOAD(plugin, api) { return (void*)allDoorCtx; } +CTLP_CAPI (subscribeToLow, source, argsJ, eventJ, context) { + json_object* signalArrayJ = NULL, *subscribeArgsJ = NULL, *subscribeFilterJ = NULL, *responseJ = NULL; + const char* unit = NULL; + double frequency = 0; + int err = 0; + + err = wrap_json_unpack(eventJ, "{so,s?s,s?F !}", + "signal", &signalArrayJ, + "unit", &unit, + "frequency", &frequency); + if(err) + { + AFB_ERROR("Problem to unpack JSON object eventJ: %s", + json_object_to_json_string(eventJ)); + return err; + } + + if(frequency >= 0) + { + wrap_json_pack(&subscribeFilterJ, "{sf}", "frequency", frequency); + } + + for (int idx = 0; idx < json_object_array_length(signalArrayJ); idx++) + { + 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; + } + } + + return err; +} + CTLP_CAPI (isOpen, source, argsJ, eventJ, context) { const char* eventName; diff --git a/signal-composer-binding/signal-composer-binding.cpp b/signal-composer-binding/signal-composer-binding.cpp index 860de07..5ff24b0 100644 --- a/signal-composer-binding/signal-composer-binding.cpp +++ b/signal-composer-binding/signal-composer-binding.cpp @@ -85,41 +85,44 @@ void get(afb_req request) int loadConf() { - int ret = 0; + int err = 0; const char* rootdir = strncat(GetBindingDirPath(), "/etc", sizeof(GetBindingDirPath()) - strlen(GetBindingDirPath()) -1); bindingApp& bApp = bindingApp::instance(); - ret = bApp.loadConfig(rootdir); + err = bApp.loadConfig(rootdir); #ifdef CONTROL_SUPPORT_LUA - ret += LuaConfigLoad(); + err += LuaConfigLoad(); #endif - return ret; + return err; } int execConf() { bindingApp& bApp = bindingApp::instance(); - int ret = CtlConfigExec(bApp.ctlConfig()); + int err = 0; + CtlConfigExec(bApp.ctlConfig()); std::vector<std::shared_ptr<Signal>> allSignals = bApp.getAllSignals(); ssize_t sigCount = allSignals.size(); for( std::shared_ptr<Signal>& sig: allSignals) { - sig->attachToSources(bApp); + sig->attachToSourceSignals(bApp); } for(auto& sig: allSignals) { - if( (ret = sig->recursionCheck()) ) + if( (err += 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; + return err; } } + bApp.execSubscription(); + AFB_DEBUG("Signal Composer Control configuration Done.\n signals=%d", (int)sigCount); - return ret; + return err; } diff --git a/signal-composer-binding/signal-composer.cpp b/signal-composer-binding/signal-composer.cpp index a1300e3..3158797 100644 --- a/signal-composer-binding/signal-composer.cpp +++ b/signal-composer-binding/signal-composer.cpp @@ -114,7 +114,7 @@ CtlActionT* bindingApp::convert2Action(const std::string& name, json_object* act int bindingApp::loadOneSourceAPI(json_object* sourceJ) { json_object *initJ = nullptr, *getSignalJ = nullptr; - CtlActionT *initCtl, *getSignalCtl; + CtlActionT *initCtl = nullptr, *getSignalCtl = nullptr; const char *api, *info; int err = wrap_json_unpack(sourceJ, "{ss,s?s,s?o,s?o !}", @@ -205,7 +205,6 @@ int bindingApp::loadOneSignal(json_object* signalJ) } // 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); @@ -251,19 +250,22 @@ int bindingApp::loadSignals(CtlSectionT* section, json_object *signalsJ) int err = 0; bindingApp& bApp = instance(); - if (json_object_get_type(signalsJ) == json_type_array) + if(signalsJ) { - int count = json_object_array_length(signalsJ); - - for (int idx = 0; idx < count; idx++) + if (json_object_get_type(signalsJ) == json_type_array) { - json_object *signalJ = json_object_array_get_idx(signalsJ, idx); - err = bApp.loadOneSignal(signalJ); - if (err) return err; + 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);} } - else - {err = bApp.loadOneSignal(signalsJ);} return err; } @@ -306,3 +308,16 @@ CtlConfigT* bindingApp::ctlConfig() { return ctlConfig_; } + +int bindingApp::execSubscription() const +{ + int err = 0; + for(const SourceAPI& srcAPI: sourcesList_) + { + if (srcAPI.api() != std::string(ctlConfig_->api)) + { + err += srcAPI.makeSubscription(); + } + } + return err; +} diff --git a/signal-composer-binding/signal-composer.hpp b/signal-composer-binding/signal-composer.hpp index 8affc7a..2a79779 100644 --- a/signal-composer-binding/signal-composer.hpp +++ b/signal-composer-binding/signal-composer.hpp @@ -47,8 +47,10 @@ public: int loadConfig(const std::string& filepath); //void loadSignalsFile(std::string signalsFile); + CtlConfigT* ctlConfig(); + std::vector<std::shared_ptr<Signal>> getAllSignals(); SourceAPI* getSourceAPI(const std::string& api); std::shared_ptr<Signal> searchSignal(const std::string& aName); - std::vector<std::shared_ptr<Signal>> getAllSignals(); - CtlConfigT* ctlConfig(); + + int execSubscription() const; }; diff --git a/signal-composer-binding/signal.cpp b/signal-composer-binding/signal.cpp index 0689a99..2dc517c 100644 --- a/signal-composer-binding/signal.cpp +++ b/signal-composer-binding/signal.cpp @@ -26,7 +26,7 @@ Signal::Signal(const std::string& id, double frequency, CtlActionT* onReceived) :id_(id), - sourcesSig_(sources), + signalSigList_(sources), frequency_(frequency), unit_(unit), onReceived_(onReceived) @@ -48,17 +48,46 @@ bool Signal::operator ==(const Signal& other) const bool Signal::operator==(const std::string& aName) const { if(id_ == aName) {return true;} - for( const std::string& src : sourcesSig_) + for( const std::string& src : signalSigList_) { if(src == aName) {return true;} } return false; } -std::string Signal::id() const +const std::string Signal::id() const { return id_; } + +json_object* Signal::toJSON() const +{ + json_object* queryJ = nullptr; + std::vector<std::string> lowSignalName; + for (const std::string& src: signalSigList_ ) + { + ssize_t sep = src.find_first_of("/"); + if(sep != std::string::npos) + { + lowSignalName.push_back(src.substr(sep+1)); + } + } + json_object* nameArray = json_object_new_array(); + for (const std::string& lowSig: lowSignalName) + { + json_object_array_add(nameArray, json_object_new_string(lowSig.c_str())); + } + /*json_object_object_add(queryJ, "signal", nameArray); + json_object_object_add(queryJ, "unit", json_object_new_string(unit_.c_str())); + json_object_object_add(queryJ, "unit", json_object_new_double(frequency_));*/ + wrap_json_pack(&queryJ, "{so,ss*,sf*}", + "signal", nameArray, + "unit", unit_.c_str(), + "frequency", frequency_); + + return queryJ; +} + void update(double timestamp, double value) { AFB_NOTICE("Got an update from observed signal"); @@ -74,9 +103,9 @@ void Signal::attach(Signal* obs) Observers_.push_back(obs); } -void Signal::attachToSources(bindingApp& bApp) +void Signal::attachToSourceSignals(bindingApp& bApp) { - for (const std::string& srcSig: sourcesSig_) + for (const std::string& srcSig: signalSigList_) { if(srcSig.find("/") == std::string::npos) { diff --git a/signal-composer-binding/signal.hpp b/signal-composer-binding/signal.hpp index cb50c67..4d4c641 100644 --- a/signal-composer-binding/signal.hpp +++ b/signal-composer-binding/signal.hpp @@ -28,7 +28,7 @@ class Signal { private: std::string id_; - std::vector<std::string> sourcesSig_; + std::vector<std::string> signalSigList_; long long int timestamp_; double value_; std::map<long long int, double> history_; ///< history_ - Hold signal value history in map with <timestamp, value> @@ -46,11 +46,12 @@ public: bool operator==(const Signal& other) const; bool operator==(const std::string& aName) const; - std::string id() const; + const std::string id() const; + json_object* toJSON() const; void update(long long int timestamp, double value); int onReceivedCB(json_object *queryJ); - void attachToSources(bindingApp& bApp); + void attachToSourceSignals(bindingApp& bApp); void notify(); //virtual double average() const; diff --git a/signal-composer-binding/source.cpp b/signal-composer-binding/source.cpp index 6e7fec2..80c59d6 100644 --- a/signal-composer-binding/source.cpp +++ b/signal-composer-binding/source.cpp @@ -33,19 +33,39 @@ void SourceAPI::addSignal(const std::string& id, std::vector<std::string>& sourc { std::shared_ptr<Signal> sig = std::make_shared<Signal>(id, sources, unit, frequency, onReceived); - signals_.push_back(sig); + signalsList_.push_back(sig); } std::vector<std::shared_ptr<Signal>> SourceAPI::getSignals() const { - return signals_; + return signalsList_; } std::shared_ptr<Signal> SourceAPI::searchSignal(const std::string& name) const { - for (auto& sig: signals_) + for (auto& sig: signalsList_) { if(*sig == name) {return sig;} } return nullptr; } + +int SourceAPI::makeSubscription() const +{ + int err = 0; + if(getSignal_) + { + for(std::shared_ptr<Signal> sig: signalsList_) + { + json_object* lowSignalNameJ = sig->toJSON(); + if(!lowSignalNameJ) + { + AFB_ERROR("Error building JSON query object to subscribe to for signal %s", sig->id().c_str()); + return -1; + } + err += ActionExecOne(getSignal_, lowSignalNameJ); + } + } + + return err; +} diff --git a/signal-composer-binding/source.hpp b/signal-composer-binding/source.hpp index c1644c5..c295b6e 100644 --- a/signal-composer-binding/source.hpp +++ b/signal-composer-binding/source.hpp @@ -28,7 +28,7 @@ private: CtlActionT* init_; CtlActionT* getSignal_; - std::vector<std::shared_ptr<Signal>> signals_; + std::vector<std::shared_ptr<Signal>> signalsList_; public: SourceAPI(); @@ -39,4 +39,6 @@ public: std::vector<std::shared_ptr<Signal>> getSignals() const; std::shared_ptr<Signal> searchSignal(const std::string& name) const; + + int makeSubscription() const; }; |