From a758f4a632adc7fff4769d97379264de6c68685d Mon Sep 17 00:00:00 2001 From: Romain Forlot Date: Fri, 22 Sep 2017 18:15:33 +0200 Subject: Migrating to real Observer design pattern usage Change-Id: I2fa4e1dc81f5dff852e619a425b8caf26b94b55a Signed-off-by: Romain Forlot --- signal-composer-binding/observer-pattern.hpp | 139 +++++++++++++++++++++++++++ signal-composer-binding/signal.cpp | 85 ++++++++++++---- signal-composer-binding/signal.hpp | 20 ++-- 3 files changed, 214 insertions(+), 30 deletions(-) create mode 100644 signal-composer-binding/observer-pattern.hpp (limited to 'signal-composer-binding') diff --git a/signal-composer-binding/observer-pattern.hpp b/signal-composer-binding/observer-pattern.hpp new file mode 100644 index 0000000..aba2847 --- /dev/null +++ b/signal-composer-binding/observer-pattern.hpp @@ -0,0 +1,139 @@ +/* + * 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. +*/ + +/* This is a classic observer design pattern a bit enhanced to be + able to pull and push info in 2 ways */ + + + #include + #include + +template +class Observable; + +template +class Observer +{ +protected: + virtual ~Observer() + { + const_iterator_ ite = observableList_.end(); + + for(iterator_ itb=observableList_.begin();itb!=ite;++itb) + { + (*itb)->delObserver(this); + } + } + + std::list*> observableList_; + typedef typename std::list*>::iterator iterator_; + typedef typename std::list*>::const_iterator const_iterator_; + +public: + virtual void update(T* observable) = 0; + + void addObservable(Observable* observable) + { + for (auto& obs : observableList_) + { + if (obs == observable) + {return;} + } + + observableList_.push_back(observable); + } + + void delObservable(Observable* observable) + { + const_iterator_ it = std::find(observableList_.begin(), observableList_.end(), observable); + if(it != observableList_.end()) + {observableList_.erase(it);} + } +/* + void recursionCheck(T* obs) + { + for (const auto& obs: observersList_) + { + if( id_ == obs->id()) + {return -1;} + if( origId == obs->id()) + {return -1;} + if(! obs->recursionCheck(origId)) + {return -1;} + } + return 0; + } +*/ +}; + +template +class Observable +{ +private: + std::list*> observerList_; + typedef typename std::list*>::iterator iterator_; + typedef typename std::list*>::const_iterator const_iterator_; + +public: + void addObserver(Observer* observer) + { + observerList_.push_back(observer); + observer->addObservable(this); + } + + void delObserver(Observer* observer) + { + const_iterator_ it = find(observerList_.begin(), observerList_.end(), observer); + if(it != observerList_.end()) + {observerList_.erase(it);} + } +/* + int recursionCheck() + { + for (auto& obs: observerList_) + { + if( static_cast(obs) == static_cast(this)) + {return -1;} + if( obs->recursionCheck(id_)) + {return -1;} + } + return 0; + } +*/ + virtual ~Observable() + { + iterator_ itb = observerList_.begin(); + const_iterator_ ite = observerList_.end(); + + for(;itb!=ite;++itb) + { + (*itb)->delObservable(this); + } + } + +protected: + void notify() + { + iterator_ itb=observerList_.begin(); + const_iterator_ ite=observerList_.end(); + + for(;itb!=ite;++itb) + { + (*itb)->update(static_cast(this)); + } + } +}; diff --git a/signal-composer-binding/signal.cpp b/signal-composer-binding/signal.cpp index 2d49c3c..2dcc552 100644 --- a/signal-composer-binding/signal.cpp +++ b/signal-composer-binding/signal.cpp @@ -88,23 +88,30 @@ json_object* Signal::toJSON() const dependsSignalName.push_back(src.substr(sep+1)); } } - json_object* nameArray = json_object_new_array(); + json_object *nameArrayJ = json_object_new_array(); for (const std::string& lowSig: dependsSignalName) { - json_object_array_add(nameArray, json_object_new_string(lowSig.c_str())); + json_object_array_add(nameArrayJ, json_object_new_string(lowSig.c_str())); } - wrap_json_pack(&queryJ, "{ss,ss*,so*,ss*,sf*,so*}", + + wrap_json_pack(&queryJ, "{ss,so*}", "id", id_.c_str(), - "event", event_.c_str(), - "depends", nameArray, - "unit", unit_.c_str(), - "frequency", frequency_, "getSignalsArgs", getSignalsArgs_); + if (!event_.empty()) {json_object_object_add(queryJ, "event", json_object_new_string(event_.c_str()));} + if (json_object_array_length(nameArrayJ)) {json_object_object_add(queryJ, "depends", nameArrayJ);} + if (!unit_.empty()) {json_object_object_add(queryJ, "unit", json_object_new_string(unit_.c_str()));} + if (frequency_) {json_object_object_add(queryJ, "frequency", json_object_new_double(frequency_));} + + if(timestamp_) {json_object_object_add(queryJ, "timestamp", json_object_new_int64(timestamp_));} + + if (value_.hasBool) {json_object_object_add(queryJ, "value", json_object_new_boolean(value_.boolVal));} + else if (value_.hasNum) {json_object_object_add(queryJ, "value", json_object_new_double(value_.numVal));} + else if (value_.hasStr) {json_object_object_add(queryJ, "value", json_object_new_string(value_.strVal.c_str()));} + return queryJ; } - /// @brief Set Signal timestamp and value property when an incoming /// signal arrived. Called by a plugin because treatment can't be /// standard as signals sources format could changes. See low-can plugin @@ -121,11 +128,10 @@ void Signal::set(long long int timestamp, struct SignalValue& value) /// @brief Observer method called when a Observable Signal has changes. /// -/// @param[in] timestamp - timestamp of occured signal -/// @param[in] value - value of change -void Signal::update(long long int timestamp, struct SignalValue value) +/// @param[in] Observable - object from which update come from +void Signal::update(Signal* sig) { - AFB_DEBUG("Got an update from observed signal. Timestamp: %lld, vb: %d, vn: %lf, vs: %s", timestamp, value.boolVal, value.numVal, value.strVal.c_str()); + AFB_NOTICE("Got an update from observed signal %s", sig->id().c_str()); } /// @brief Notify observers that there is a change and execute callback defined @@ -145,7 +151,7 @@ int Signal::onReceivedCB(json_object *queryJ) /// present in the Observers vector. /// /// @param[in] obs - pointer to a Signal observable -void Signal::attach(Signal* obs) +/*void Signal::attach(Signal* obs) { for ( auto& sig : Observers_) { @@ -154,7 +160,7 @@ void Signal::attach(Signal* obs) } Observers_.push_back(obs); -} +}*/ /// @brief Make a Signal observer observes Signals observables /// set in its observable vector. @@ -166,11 +172,11 @@ void Signal::attachToSourceSignals(Composer& composer) { if(srcSig.find("/") == std::string::npos) { - std::vector> sig = composer.searchSignals(srcSig); - if(sig[0]) + std::vector observables = composer.searchSignals(srcSig); + if(observables[0]) { AFB_NOTICE("Attaching %s to %s", id_.c_str(), srcSig.c_str()); - sig[0]->attach(this); + observables[0]->addObserver(this); continue; } AFB_WARNING("Can't attach. Is %s exists ?", srcSig.c_str()); @@ -180,7 +186,7 @@ void Signal::attachToSourceSignals(Composer& composer) /// @brief Call update() method on observer Signal with /// current Signal timestamp and value -void Signal::notify() const +/*void Signal::notify() const { for (int i = 0; i < Observers_.size(); ++i) Observers_[i]->update(timestamp_, value_); @@ -194,7 +200,7 @@ void Signal::notify() const /// @return 0 if no infinite loop detected, -1 if not. int Signal::recursionCheck(const std::string& origId) const { - for (const auto& obs: Observers_) + for (const auto& obs: observersList_) { if( id_ == obs->id()) {return -1;} @@ -214,7 +220,7 @@ int Signal::recursionCheck(const std::string& origId) const /// @return 0 if no infinite loop detected, -1 if not. int Signal::recursionCheck() const { - for (const auto& obs: Observers_) + for (const auto& obs: observersList_) { if( id_ == obs->id()) {return -1;} @@ -222,7 +228,7 @@ int Signal::recursionCheck() const {return -1;} } return 0; -} +}*/ /// @brief Make an average over the last X 'seconds' /// @@ -332,3 +338,40 @@ struct SignalValue Signal::last() const { return history_.rbegin()->second; } + +/* +template +Observable::~Observable() +{ + iterator_ itb = observerList_.begin(); + const_iterator_ ite = observerList_.end(); + + for(;itb!=ite;++itb) + { + (*itb)->delObservable(this); + } +} + +template +Observer::~Observer() +{ + const_iterator_ ite = observableList_.end(); + + for(iterator_ itb=observableList_.begin();itb!=ite;++itb) + { + (*itb)->delObserver(this); + } +} + +template +void Observable::notify() +{ + iterator_ itb=observerList_.begin(); + const_iterator_ ite=observerList_.end(); + + for(;itb!=ite;++itb) + { + (*itb)->update(static_cast(this)); + } +} +*/ diff --git a/signal-composer-binding/signal.hpp b/signal-composer-binding/signal.hpp index 870843e..8d0d1ab 100644 --- a/signal-composer-binding/signal.hpp +++ b/signal-composer-binding/signal.hpp @@ -22,8 +22,15 @@ #include #include +#include "observer-pattern.hpp" + class Composer; +/// @brief Structure holding a possible value of a Signal +/// as it could be different type of value, we declare all +/// possibility. +/// Not very efficient or optimized, maybe use of Variant in +/// C++17 but this is a bit too new to uses it for now struct SignalValue { bool hasBool = false; bool boolVal; @@ -33,7 +40,7 @@ struct SignalValue { std::string strVal; }; -class Signal +class Signal: public Observable, public Observer { private: std::string id_; @@ -47,12 +54,7 @@ private: CtlActionT* onReceived_; json_object* getSignalsArgs_; - std::vector Observers_; - - void notify() const; - void attach(Signal *obs); - int recursionCheck(const std::string& origId) const; - + //int recursionCheck(const std::string& origId) const; public: Signal(const std::string& id, const std::string& event, std::vector& depends, const std::string& unit, double frequency, CtlActionT* onReceived, json_object* getSignalsArgs); Signal(const std::string& id, std::vector& depends, const std::string& unit, double frequency, CtlActionT* onReceived); @@ -65,7 +67,7 @@ public: json_object* toJSON() const; void set(long long int timestamp, struct SignalValue& value); - void update(long long int timestamp, struct SignalValue value); + void update(Signal* sig); int onReceivedCB(json_object *queryJ); void attachToSourceSignals(Composer& composer); @@ -73,5 +75,5 @@ public: double minimum(int seconds = 0) const; double maximum(int seconds = 0) const; struct SignalValue last() const; - int recursionCheck() const; + //int recursionCheck() const; }; -- cgit 1.2.3-korg