diff --git a/fairmq/Plugin.h b/fairmq/Plugin.h index 7dd5f680..8d1eeece 100644 --- a/fairmq/Plugin.h +++ b/fairmq/Plugin.h @@ -91,10 +91,10 @@ class Plugin auto GetPropertyAsString(const std::string& key) const -> std::string { return fPluginServices->GetPropertyAsString(key); } auto GetChannelInfo() const -> std::unordered_map { return fPluginServices->GetChannelInfo(); } auto GetPropertyKeys() const -> std::vector { return fPluginServices->GetPropertyKeys(); } - // template - // auto SubscribeToPropertyChange(std::functionkey*/, const T /*newValue<])> callback) const -> void { fPluginServices.SubscribeToPropertyChange(fkName, callback); } - // template - // auto UnsubscribeFromPropertyChange() -> void { fPluginServices.UnsubscribeFromPropertyChange(fkName); } + template + auto SubscribeToPropertyChange(std::function callback) -> void { fPluginServices->SubscribeToPropertyChange(fkName, callback); } + template + auto UnsubscribeFromPropertyChange() -> void { fPluginServices->UnsubscribeFromPropertyChange(fkName); } private: const std::string fkName; diff --git a/fairmq/PluginServices.h b/fairmq/PluginServices.h index 6333769c..65f76260 100644 --- a/fairmq/PluginServices.h +++ b/fairmq/PluginServices.h @@ -206,30 +206,25 @@ class PluginServices auto GetChannelInfo() const -> std::unordered_map { return fConfig->GetChannelInfo(); } + /// @brief Discover the list of property keys + /// @return list of property keys auto GetPropertyKeys() const -> std::vector { return fConfig->GetPropertyKeys(); } /// @brief Subscribe to property updates of type T /// @param subscriber /// @param callback function /// - /// While PluginServices provides the SetProperty method which can update properties only during certain device states, there are - /// other APIs in a FairMQ device that can update properties at any time. Therefore, the callback implementation should expect to be called in any - /// device state. - // template - // auto SubscribeToPropertyChange( - // const std::string& subscriber, - // std::functionkey*/, const T /*newValue<])> callback - // ) const -> void - // { - // fConfig->Subscribe(subscriber, callback); - // } - // - // /// @brief Unsubscribe from property updates of type T - // /// @param subscriber - // template - // auto UnsubscribeFromPropertyChange(const std::string& subscriber) -> void { fConfig->Unsubscribe(subscriber); } - // - // TODO Fix property subscription + /// Subscribe to property changes with a callback to monitor property changes in an event based fashion. + template + auto SubscribeToPropertyChange(const std::string& subscriber, std::function callback) const -> void + { + fConfig->Subscribe(subscriber, callback); + } + + /// @brief Unsubscribe from property updates of type T + /// @param subscriber + template + auto UnsubscribeFromPropertyChange(const std::string& subscriber) -> void { fConfig->Unsubscribe(subscriber); } static const std::unordered_map fkDeviceStateStrMap; static const std::unordered_map> fkStrDeviceStateMap; diff --git a/fairmq/options/FairMQEventManager.h b/fairmq/options/FairMQEventManager.h deleted file mode 100644 index 71244b25..00000000 --- a/fairmq/options/FairMQEventManager.h +++ /dev/null @@ -1,148 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * - * * - * This software is distributed under the terms of the * - * GNU Lesser General Public Licence version 3 (LGPL) version 3, * - * copied verbatim in the file "LICENSE" * - ********************************************************************************/ - -/* - * File: FairMQEventManager.h - * Author: winckler - * - * Created on August 12, 2016, 13:50 PM - */ - -#ifndef FAIRMQEVENTMANAGER_H -#define FAIRMQEVENTMANAGER_H - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -enum class EventId : uint32_t -{ - // Place your new EventManager events here - UpdateParam = 0, - Custom = 1, -}; - -namespace Events -{ - -template -struct Traits; - -template -struct Traits -{ - using signal_type = boost::signals2::signal; -}; - -template -struct Traits> -{ - using signal_type = boost::signals2::signal& )>; -}; - -template <> -struct Traits -{ - using signal_type = boost::signals2::signal; -}; - -template -struct Traits -{ - using signal_type = boost::signals2::signal; -}; - -template -struct Traits -{ - using signal_type = boost::signals2::signal; -}; - -/* -template struct Traits2; -template <> struct Traits2 { using signal_type = boost::signals2::signal; } ; -template struct Traits2 { using signal_type = boost::signals2::signal; } ; -template <> struct Traits2 { using signal_type = boost::signals2::signal; } ; -// */ - -} // Events namespace - -class FairMQEventManager -{ - public: - using EventKey = std::pair; - - FairMQEventManager() : - fEventMap() - {} - - virtual ~FairMQEventManager() - {} - - template - void Connect(const std::string& key, F&& func) const - { - GetSlot(key).connect(std::forward(func)); - } - - template - void Disconnect(const std::string& key) - { - GetSlot(key).disconnect(); - } - - template - void Emit(const std::string& key, Args&&... args) - { - GetSlot(key)(std::forward(args)...); - } - - template - bool EventKeyFound(const std::string& key) - { - return fEventMap.find(std::pair(event, key)) != fEventMap.end(); - } - - private: - mutable std::map fEventMap; - - template ::signal_type, - typename SlotPtr = boost::shared_ptr> - Slot& GetSlot(const std::string& key) const - { - try - { - EventKey eventKey = std::make_pair(event, key); - //static_assert(std::is_same()),SlotPtr>::value, ""); - if (fEventMap.find(eventKey) == fEventMap.end()) - { - fEventMap.emplace(eventKey, boost::make_shared()); - } - - return *boost::any_cast(fEventMap.at(eventKey)); -// auto &&tmp = boost::any_cast(fEventMap.at(eventKey)); -// return *tmp; - } - catch (boost::bad_any_cast const &e) - { - LOG(ERROR) << "Caught instance of boost::bad_any_cast: " - << e.what() << " on event #" << static_cast(event) << " and key" << key; - abort(); - } - } -}; - -#endif /* FAIRMQEVENTMANAGER_H */ diff --git a/fairmq/options/FairMQProgOptions.cxx b/fairmq/options/FairMQProgOptions.cxx index 616e416e..8823bb3d 100644 --- a/fairmq/options/FairMQProgOptions.cxx +++ b/fairmq/options/FairMQProgOptions.cxx @@ -22,7 +22,7 @@ using namespace std; FairMQProgOptions::FairMQProgOptions() - : FairProgOptions(), FairMQEventManager() + : FairProgOptions() , fMQParserOptions("MQ-Device parser options") , fMQOptionsInCfg("MQ-Device options") , fMQOptionsInCmd("MQ-Device options") diff --git a/fairmq/options/FairMQProgOptions.h b/fairmq/options/FairMQProgOptions.h index 61bf7e77..cb90bab8 100644 --- a/fairmq/options/FairMQProgOptions.h +++ b/fairmq/options/FairMQProgOptions.h @@ -16,6 +16,8 @@ #ifndef FAIRMQPROGOPTIONS_H #define FAIRMQPROGOPTIONS_H +#include + #include #include #include @@ -24,10 +26,19 @@ #include #include "FairProgOptions.h" -#include "FairMQEventManager.h" #include "FairMQChannel.h" -class FairMQProgOptions : public FairProgOptions , public FairMQEventManager +namespace fair +{ +namespace mq +{ + +struct PropertyChange : Event {}; + +} /* namespace mq */ +} /* namespace fair */ + +class FairMQProgOptions : public FairProgOptions { protected: using FairMQMap = std::unordered_map>; @@ -192,12 +203,8 @@ class FairMQProgOptions : public FairProgOptions , public FairMQEventManager } } - // execute stored function of a given key if exist //if (std::is_same::value || std::is_same::value)//if one wants to restrict type - if (EventKeyFound(key)) - { - EmitUpdate::type>(key, val); - } + fEvents.Emit::type>(key, val); return 0; } @@ -232,36 +239,29 @@ class FairMQProgOptions : public FairProgOptions , public FairMQEventManager } } - // execute stored function of a given key if exist //if (std::is_same::value || std::is_same::value)//if one wants to restrict type - if (EventKeyFound(key)) - { - EmitUpdate::type>(key, val); - } + fEvents.Emit::type>(key, val); return 0; } - template - void Subscribe(const std::string& key, F&& func) const + template + void Subscribe(const std::string& subscriber, std::function func) { std::unique_lock lock(fConfigMutex); static_assert(!std::is_same::value || !std::is_same::value, "In template member FairMQProgOptions::Subscribe(key,Lambda) the types const char* or char* for the calback signatures are not supported."); - if (fVarMap.count(key)) - { - Connect(key, std::forward(func)); - } + fEvents.Subscribe(subscriber, func); } template - void Unsubscribe(const std::string& key) const + void Unsubscribe(const std::string& subscriber) { std::unique_lock lock(fConfigMutex); - Disconnect(key); + fEvents.Unsubscribe(subscriber); } /* @@ -292,18 +292,6 @@ class FairMQProgOptions : public FairProgOptions , public FairMQEventManager // map of read channel info - channel name - number of subchannels std::unordered_map fChannelInfo; - bool EventKeyFound(const std::string& key) - { - if (FairMQEventManager::EventKeyFound(key)) - { - return true; - } - else - { - return false; - } - } - using MQKey = std::tuple;//store key info std::map fMQKeyMap;// key=full path - val=key info @@ -335,7 +323,7 @@ class FairMQProgOptions : public FairProgOptions , public FairMQEventManager //compile time check whether T is const char* or char*, and in that case a compile time error is thrown. static_assert(!std::is_same::value || !std::is_same::value, "In template member FairMQProgOptions::EmitUpdate(key,val) the types const char* or char* for the calback signatures are not supported."); - Emit(key, key, val); + fEvents.Emit(key, val); } int UpdateChannelMap(const std::string& channelName, int index, const std::string& member, const std::string& val); @@ -348,6 +336,8 @@ class FairMQProgOptions : public FairProgOptions , public FairMQEventManager } void UpdateChannelInfo(); + + fair::mq::EventManager fEvents; }; #endif /* FAIRMQPROGOPTIONS_H */ diff --git a/fairmq/options/runConfigEx.cxx b/fairmq/options/runConfigEx.cxx index 6ac7c8ea..3cea9b5e 100644 --- a/fairmq/options/runConfigEx.cxx +++ b/fairmq/options/runConfigEx.cxx @@ -99,24 +99,33 @@ int main(int argc, char** argv) // LOG(INFO) << "dataRate: " << dataRate; LOG(INFO) << "Subscribing: (chans.data.0.address)"; - config.Subscribe("chans.data.0.address", [&device](const string& key, const string& value) + config.Subscribe("test", [&device](const string& key, string value) { - LOG(INFO) << "[callback] Updating device parameter " << key << " = " << value; - device.fChannels.at("data").at(0).UpdateAddress(value); + if (key == "chans.data.0.address") + { + LOG(INFO) << "[callback] Updating device parameter " << key << " = " << value; + device.fChannels.at("data").at(0).UpdateAddress(value); + } }); LOG(INFO) << "Subscribing: (chans.data.0.rcvBufSize)"; - config.Subscribe("chans.data.0.rcvBufSize", [&device](const string& key, int value) + config.Subscribe("test", [&device](const string& key, int value) { - LOG(INFO) << "[callback] Updating device parameter " << key << " = " << value; - device.fChannels.at("data").at(0).UpdateRcvBufSize(value); + if(key == "chans.data.0.rcvBufSize") + { + LOG(INFO) << "[callback] Updating device parameter " << key << " = " << value; + device.fChannels.at("data").at(0).UpdateRcvBufSize(value); + } }); LOG(INFO) << "Subscribing: (data-rate)"; - config.Subscribe("data-rate", [&device](const string& key, double value) + config.Subscribe("test", [&device](const string& key, double value) { - LOG(INFO) << "[callback] Updating device parameter " << key << " = " << value; - device.SetRate(value); + if (key == "data-rate") + { + LOG(INFO) << "[callback] Updating device parameter " << key << " = " << value; + device.SetRate(value); + } }); LOG(INFO) << "Starting value updates...\n"; @@ -134,6 +143,11 @@ int main(int argc, char** argv) LOG(INFO) << "device: " << device.GetRate() << endl; // device.Print(); + LOG(INFO) << "nase: " << config.GetValue("nase"); + + config.Unsubscribe("test"); + config.Unsubscribe("test"); + config.Unsubscribe("test"); // advanced commands // LOG(INFO) << "-------------------- start custom 1"; diff --git a/fairmq/test/plugin_services/_config.cxx b/fairmq/test/plugin_services/_config.cxx index b5920b9f..ad1b5f7d 100644 --- a/fairmq/test/plugin_services/_config.cxx +++ b/fairmq/test/plugin_services/_config.cxx @@ -57,4 +57,35 @@ TEST_F(PluginServices, KeyDiscovery) EXPECT_TRUE(find(keys.begin(), keys.end(), "foo") != keys.end()); } +TEST_F(PluginServices, ConfigCallbacks) +{ + mServices.SubscribeToPropertyChange("test", [](const string& key, string value) { + if (key == "chans.data.0.address") { ASSERT_EQ(value, "tcp://localhost:4321"); } + }); + + mServices.SubscribeToPropertyChange("test", [](const string& key, int value) { + if(key == "chans.data.0.rcvBufSize") { + FAIL(); // should not be called because we unsubscribed + } + }); + + mServices.SubscribeToPropertyChange("test", [](const string& key, double value) { + if (key == "data-rate") { ASSERT_EQ(value, 0.9); } + }); + + mServices.SubscribeToDeviceStateChange("test",[&](DeviceState newState){ + switch (newState) { + case DeviceState::InitializingDevice: + mServices.SetProperty("chans.data.0.address", "tcp://localhost:4321"); + mServices.SetProperty("chans.data.0.rcvBufSize", 100); + mServices.SetProperty("data-rate", 0.9); + break; + default: + break; + } + }); + + mServices.UnsubscribeFromPropertyChange("test"); +} + } /* namespace */