//================================================================================ // Name : SensorGroupTemplate.h // Author : Micha Mueller // Copyright : Leibniz Supercomputing Centre // Description : Interface template for sensor group functionality. //================================================================================ //================================================================================ // This file is part of DCDB (DataCenter DataBase) // Copyright (C) 2018-2019 Leibniz Supercomputing Centre // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //================================================================================ #ifndef SENSORGROUPTEMPLATE_H_ #define SENSORGROUPTEMPLATE_H_ #include "SensorGroupInterface.h" #include "EntityInterface.h" #include "timestamp.h" #include #include /** * @brief Interface template for sensor group implementations with entities. * * @details There is a partial template specialization for E = nullptr_t, i.e. * for groups without entities, see SensorGroupTemplate. * Common code has to be duplicated and maintained twice here and in * the SensorGroupTemplate. However, having two templates * for cases with and without entities greatly reduces code duplication * in derived plugin sensor groups. * * @ingroup pusherplugins */ template class SensorGroupTemplate : public SensorGroupInterface { //the template shall only be instantiated for classes which derive from SensorBase and EntityInterface respectively static_assert(std::is_base_of::value, "S must derive from SensorBase!"); static_assert(std::is_base_of::value, "E must derive from EntityInterface!"); protected: using S_Ptr = std::shared_ptr; public: SensorGroupTemplate(const std::string groupName) : SensorGroupInterface(groupName), _entity(nullptr) {} SensorGroupTemplate(const SensorGroupTemplate& other) : SensorGroupInterface(other), _entity(other._entity) { for(auto s : other._sensors) { S_Ptr sensor = std::make_shared(*s); _sensors.push_back(sensor); _baseSensors.push_back(sensor); } } virtual ~SensorGroupTemplate() { if(_keepRunning) { stop(); } _sensors.clear(); _baseSensors.clear(); } SensorGroupTemplate& operator=(const SensorGroupTemplate& other) { SensorGroupInterface::operator=(other); _sensors.clear(); _baseSensors.clear(); for(auto s : other._sensors) { S_Ptr sensor = std::make_shared(*s); _sensors.push_back(sensor); _baseSensors.push_back(sensor); } _entity = other._entity; return *this; } void setEntity(E* entity) { _entity = entity; } E* const getEntity() const { return _entity; } /** * @brief Initialize the sensor group. * * @details This method must not be overwritten. See execOnInit() if custom * actions are required during initialization. Initializes * associated sensors and entity. * * @param io IO service to initialize the timer with. */ virtual void init(boost::asio::io_service& io) final override { if(!_entity) { LOG(error) << "No entity set for group " << _groupName << "! Cannot initialize group"; return; } SensorGroupInterface::init(io); for(auto s : _sensors) { s->initSensor(_interval); } _entity->init(io); this->execOnInit(); } /** * @brief Start the sensor group (i.e. start collecting data). * * @details This method must not be overwritten. See execOnStart() if custom * actions are required during startup. */ virtual void start() final override { if (_keepRunning) { //we have been started already LOG(info) << "Sensorgroup " << _groupName << " already running."; return; } if (!this->execOnStart()) { LOG(error) << "Sensorgroup " << _groupName << ": Startup failed."; return; } if (_entity) { _keepRunning = true; _pendingTasks++; _timer->async_wait(_entity->getStrand()->wrap(std::bind(&SensorGroupTemplate::readAsync, this))); LOG(info) << "Sensorgroup " << _groupName << " started."; } else { LOG(error) << "No entity set for group " << _groupName << "! Cannot start polling."; } } /** * @brief Stop the sensor group (i.e. stop collecting data). * * @details This method must not be overwritten. See execOnStop() if custom * actions are required during shutdown. */ virtual void stop() final override { if (!_keepRunning) { LOG(info) << "Sensorgroup " << _groupName << " already stopped."; return; } _keepRunning = false; //cancel any outstanding readAsync() _timer->cancel(); wait(); this->execOnStop(); LOG(info) << "Sensorgroup " << _groupName << " stopped."; } /** * @brief Add a sensor to this group. * * @details Not intended to be overwritten. * * @param s Shared pointer to the sensor. */ virtual void pushBackSensor(SBasePtr s) final override { //check if dynamic cast returns nullptr if (S_Ptr dSensor = std::dynamic_pointer_cast(s)) { _sensors.push_back(dSensor); _baseSensors.push_back(s); } else { LOG(warning) << "Group " << _groupName << ": Type mismatch when storing sensor! Sensor omitted"; } } /** * @brief Retrieve all sensors of this group. * * @details Not intended to be overwritten. * * @return A std::vector with shared pointers to all associated sensors. */ virtual std::vector& getSensors() final override { return _baseSensors; } /** * @brief Print SensorGroup configuration. * * @details Always call %SensorGroupTemplate::printConfig() to print * complete configuration of a sensor group. This method takes * care of calling SensorGroupInterface::printConfig() and * derived %printConfig() methods in correct order. * * @param ll Log severity level to be used from logger. */ virtual void printConfig(LOG_LEVEL ll) final override { //print common base attributes SensorGroupInterface::printConfig(ll); //print plugin specific group attributes this->printGroupConfig(ll); if (_entity) { LOG_VAR(ll) << " Entity " << _entity->getName(); } else { LOG_VAR(ll) << " No entity set!"; } //print associated sensors LOG_VAR(ll) << " Sensors:"; for(auto s : _sensors) { s->SensorBase::printConfig(ll, lg); s->printConfig(ll, lg); } } protected: /** * @brief Asynchronous callback if _timer expires. * * @details Issues a read() and sets the timer again if _keepRunning is true. * Not intended to be overwritten. */ void readAsync() final override { this->read(); if (_timer && _keepRunning) { _timer->expires_at(timestamp2ptime(nextReadingTime())); _pendingTasks++; _timer->async_wait(_entity->getStrand()->wrap(std::bind(&SensorGroupTemplate::readAsync, this))); } _pendingTasks--; } //TODO move common logic to interface ///@name Can be overwritten ///@{ /** * @brief Implement plugin specific actions to initialize a group here. * * @details If a derived class (i.e. a plugin group) requires further custom * actions for initialization, this should be implemented here. * %execOnInit() is appropriately called by this template during * init(). */ virtual void execOnInit() { /* do nothing if not overwritten */ } /** * @brief Implement plugin specific actions to start a group here. * * @details If a derived class (i.e. a plugin group) requires further custom * actions to start polling data (e.g. open a file descriptor), * this should be implemented here. %execOnStart() is appropriately * called by this template during start(). * * @return True on success, false otherwise. */ virtual bool execOnStart() { return true; } /** * @brief Implement plugin specific actions to stop a group here. * * @details If a derived class (i.e. a plugin group) requires further custom * actions to stop polling data (e.g. close a file descriptor), * this should be implemented here. %execOnStop() is appropriately * called by this template during stop(). */ virtual void execOnStop() { /* do nothing if not overwritten */ } ///@} std::vector _sensors; ///< Sensors associated with this group std::vector _baseSensors; ///< Maintain vector with SensorBase pointers for fast getSensors() implementation E* _entity; ///< Entity this group is associated to }; //////////////////////////////////////////////////////////////////////////////// // Partial template specialization for E = nullptr_t //////////////////////////////////////////////////////////////////////////////// /** * @brief Interface partial template specialization for sensor group * implementations without entities. * * @details There is a general template for groups with entities, see * SensorGroupTemplate. Common code has to be duplicated and maintained * twice here and in SensorGroupTemplate. However, having two templates * for cases with and without entities greatly reduces code duplication * in derived plugin sensor groups. * * @ingroup pusherplugins */ template class SensorGroupTemplate : public SensorGroupInterface { //the template shall only be instantiated for classes which derive from SensorBase static_assert(std::is_base_of::value, "S must derive from SensorBase!"); protected: using S_Ptr = std::shared_ptr; public: SensorGroupTemplate(const std::string groupName) : SensorGroupInterface(groupName){} SensorGroupTemplate(const SensorGroupTemplate& other) : SensorGroupInterface(other){ for(auto s : other._sensors) { S_Ptr sensor = std::make_shared(*s); _sensors.push_back(sensor); _baseSensors.push_back(sensor); } } virtual ~SensorGroupTemplate() { if(_keepRunning) { stop(); } _sensors.clear(); _baseSensors.clear(); } SensorGroupTemplate& operator=(const SensorGroupTemplate& other) { SensorGroupInterface::operator=(other); _sensors.clear(); _baseSensors.clear(); for(auto s : other._sensors) { S_Ptr sensor = std::make_shared(*s); _sensors.push_back(sensor); _baseSensors.push_back(sensor); } return *this; } /** * @brief Initialize the sensor group. * * @details This method must not be overwritten. See execOnInit() if custom * actions are required during initialization. Initializes * associated sensors and entity. * * @param io IO service to initialize the timer with. */ virtual void init(boost::asio::io_service& io) final override { SensorGroupInterface::init(io); for(auto s : _sensors) { s->initSensor(_interval); } this->execOnInit(); } /** * @brief Start the sensor group (i.e. start collecting data). * * @details This method must not be overwritten. See execOnStart() if custom * actions are required during startup. */ virtual void start() final override { if (_keepRunning) { //we have been started already LOG(info) << "Sensorgroup " << _groupName << " already running."; return; } if (!this->execOnStart()) { LOG(error) << "Sensorgroup " << _groupName << ": Startup failed."; return; } _keepRunning = true; _pendingTasks++; _timer->async_wait(std::bind(&SensorGroupTemplate::readAsync, this)); LOG(info) << "Sensorgroup " << _groupName << " started."; } /** * @brief Stop the sensor group (i.e. stop collecting data). * * @details This method must not be overwritten. See execOnStop() if custom * actions are required during shutdown. */ virtual void stop() final override { if (!_keepRunning) { LOG(info) << "Sensorgroup " << _groupName << " already stopped."; return; } _keepRunning = false; //cancel any outstanding readAsync() _timer->cancel(); wait(); this->execOnStop(); LOG(info) << "Sensorgroup " << _groupName << " stopped."; } /** * @brief Add a sensor to this group. * * @details Not intended to be overwritten. * * @param s Shared pointer to the sensor. */ virtual void pushBackSensor(SBasePtr s) final override { //check if dynamic cast returns nullptr if (S_Ptr dSensor = std::dynamic_pointer_cast(s)) { _sensors.push_back(dSensor); _baseSensors.push_back(s); } else { LOG(warning) << "Group " << _groupName << ": Type mismatch when storing sensor! Sensor omitted"; } } /** * @brief Retrieve all sensors of this group. * * @details Not intended to be overwritten. * * @return A std::vector with shared pointers to all associated sensors. */ virtual std::vector& getSensors() final override { return _baseSensors; } /** * @brief Print SensorGroup configuration. * * @details Always call %SensorGroupTemplate::printConfig() to print * complete configuration of a sensor group. This method takes * care of calling SensorGroupInterface::printConfig() and * derived %printConfig() methods in correct order. * * @param ll Log severity level to be used from logger. */ virtual void printConfig(LOG_LEVEL ll) final override { //print common base attributes SensorGroupInterface::printConfig(ll); //print plugin specific group attributes this->printGroupConfig(ll); //print associated sensors LOG_VAR(ll) << " Sensors:"; for(auto s : _sensors) { s->SensorBase::printConfig(ll, lg); s->printConfig(ll, lg); } } protected: /** * @brief Asynchronous callback if _timer expires. * * @details Issues a read() and sets the timer again if _keepRunning is true. * Not intended to be overwritten. */ void readAsync() final override { this->read(); if (_timer && _keepRunning) { _timer->expires_at(timestamp2ptime(nextReadingTime())); _pendingTasks++; _timer->async_wait(std::bind(&SensorGroupTemplate::readAsync, this)); } _pendingTasks--; } ///@name Can be overwritten ///@{ /** * @brief Implement plugin specific actions to initialize a group here. * * @details If a derived class (i.e. a plugin group) requires further custom * actions for initialization, this should be implemented here. * %initGroup() is appropriately called by this template during * init(). */ virtual void execOnInit() { /* do nothing if not overwritten */ } /** * @brief Implement plugin specific actions to start a group here. * * @details If a derived class (i.e. a plugin group) requires further custom * actions to start polling data (e.g. open a file descriptor), * this should be implemented here. %startGroup() is appropriately * called by this template during start(). * * @return True on success, false otherwise. */ virtual bool execOnStart() { return true; } /** * @brief Implement plugin specific actions to stop a group here. * * @details If a derived class (i.e. a plugin group) requires further custom * actions to stop polling data (e.g. close a file descriptor), * this should be implemented here. %stopGroup() is appropriately * called by this template during stop(). */ virtual void execOnStop() { /* do nothing if not overwritten */ } ///@} std::vector _sensors; ///< Sensors associated with this group std::vector _baseSensors; ///< Maintain vector with SensorBase pointers for fast getSensors() implementation }; #endif /* SENSORGROUPTEMPLATE_H_ */