Commit df2120b4 authored by Micha Müller's avatar Micha Müller
Browse files

Pusher SensorGroupTemplate: Also replace partial template specialization with...

Pusher SensorGroupTemplate: Also replace partial template specialization with derived SensorGroupTemplate for entities
parent 1b2b109a
......@@ -28,7 +28,6 @@
#ifndef SENSORGROUPTEMPLATE_H_
#define SENSORGROUPTEMPLATE_H_
#include "EntityInterface.h"
#include "SensorGroupInterface.h"
#include "timestamp.h"
......@@ -37,251 +36,10 @@
#include <vector>
/**
* @brief Interface template for sensor group implementations with entities.
* @brief Interface template for sensor group implementations without entities.
*
* @details There is a partial template specialization for E = nullptr_t, i.e.
* for groups without entities, see SensorGroupTemplate<S, nullptr_t>.
* Common code has to be duplicated and maintained twice here and in
* the SensorGroupTemplate<S, nullptr_t>. However, having two templates
* for cases with and without entities greatly reduces code duplication
* in derived plugin sensor groups.
* Internal usage of _baseSensors is not synchronized via
* acquireSensors()/releaseSensors() as it is only accessed (e.g. in
* (de-)constructor) when no other caller is expected to access
* _baseSensors.
*
* @ingroup pusherplugins
*/
template <class S, class E = nullptr_t>
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<SensorBase, S>::value, "S must derive from SensorBase!");
static_assert(std::is_base_of<EntityInterface, E>::value, "E must derive from EntityInterface!");
protected:
using S_Ptr = std::shared_ptr<S>;
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>(*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>(*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 Does a busy wait until all dispatched handlers are finished
* (_pendingTasks == 0).
*
* @details If the wait takes longer than a reasonable amount of time we
* return anyway, to not block termination of dcdbpusher.
*/
virtual void wait() final override {
uint64_t sleepms = 10, i = 0;
uint64_t timeout = _interval < 10000 ? 10000 : _interval;
while (sleepms * i++ < timeout) {
if (_pendingTasks)
std::this_thread::sleep_for(std::chrono::milliseconds(sleepms));
else {
this->execOnStop();
LOG(info) << "Sensorgroup " << _groupName << " stopped.";
return;
}
}
LOG(warning) << "Group " << _groupName << " will not finish! Skipping it";
}
/**
* @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 (_disabled) {
return;
}
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) {
if (!_entity->isDisabled()) {
_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(debug) << "Sensorgroup " << _groupName << " already stopped.";
return;
}
_keepRunning = false;
//cancel any outstanding readAsync()
_timer->cancel();
}
/**
* @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>(s)) {
_sensors.push_back(dSensor);
_baseSensors.push_back(s);
} else {
LOG(warning) << "Group " << _groupName << ": Type mismatch when storing sensor! Sensor omitted";
}
}
/**
* @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, unsigned leadingSpaces = 8) final override {
//print common base attributes
SensorGroupInterface::printConfig(ll, leadingSpaces);
//print plugin specific group attributes
this->printGroupConfig(ll, leadingSpaces + 4);
//print associated sensors
std::string leading(leadingSpaces + 4, ' ');
LOG_VAR(ll) << leading << "Sensors:";
for (auto s : _sensors) {
s->SensorBase::printConfig(ll, lg, leadingSpaces + 8);
s->printConfig(ll, lg, leadingSpaces + 8);
}
}
protected:
/**
* @brief Asynchronous callback if _timer expires.
*
* @details Issues a read() and sets the timer again if _keepRunning is true.
*/
void readAsync() {
this->read();
if (_timer && _keepRunning && !_disabled && !_entity->isDisabled()) {
_timer->expires_at(timestamp2ptime(nextReadingTime()));
_pendingTasks++;
_timer->async_wait(_entity->getStrand()->wrap(std::bind(&SensorGroupTemplate::readAsync, this)));
}
_pendingTasks--;
}
std::vector<S_Ptr> _sensors; ///< Store pointers to actual sensor objects in addition to general sensor base pointers
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.
* @details There is a derived template for groups with entities, see
* SensorGroupTemplateEntity.
* Internal usage of _baseSensors is not synchronized via
* acquireSensors()/releaseSensors() as it is only accessed (e.g. in
* (de-)constructor) when no other caller is expected to access
......@@ -290,7 +48,7 @@ class SensorGroupTemplate : public SensorGroupInterface {
* @ingroup pusherplugins
*/
template <class S>
class SensorGroupTemplate<S, nullptr_t> : public SensorGroupInterface {
class SensorGroupTemplate : public SensorGroupInterface {
//the template shall only be instantiated for classes which derive from SensorBase
static_assert(std::is_base_of<SensorBase, S>::value, "S must derive from SensorBase!");
......@@ -337,13 +95,13 @@ class SensorGroupTemplate<S, nullptr_t> : public SensorGroupInterface {
/**
* @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.
* @details This method must not be overwritten by plugins. See execOnInit()
* if custom actions are required during initialization.
* Initializes associated sensors.
*
* @param io IO service to initialize the timer with.
*/
virtual void init(boost::asio::io_service &io) final override {
virtual void init(boost::asio::io_service &io) override {
SensorGroupInterface::init(io);
for (auto s : _sensors) {
......@@ -380,10 +138,10 @@ class SensorGroupTemplate<S, nullptr_t> : public SensorGroupInterface {
/**
* @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.
* @details This method must not be overwritten by plugins. See
* execOnStart() if custom actions are required during startup.
*/
virtual void start() final override {
virtual void start() override {
if (_disabled) {
return;
}
......@@ -471,7 +229,7 @@ class SensorGroupTemplate<S, nullptr_t> : public SensorGroupInterface {
*
* @details Issues a read() and sets the timer again if _keepRunning is true.
*/
void readAsync() {
virtual void readAsync() {
this->read();
if (_timer && _keepRunning && !_disabled) {
_timer->expires_at(timestamp2ptime(nextReadingTime()));
......
//================================================================================
// Name : SensorGroupTemplateEntity.h
// Author : Micha Mueller
// Contact : info@dcdb.it
// Copyright : Leibniz Supercomputing Centre
// Description : Interface template for sensor group functionality with entities.
//================================================================================
//================================================================================
// This file is part of DCDB (DataCenter DataBase)
// Copyright (C) 2019-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 DCDBPUSHER_INCLUDES_SENSORGROUPTEMPLATEENTITY_H_
#define DCDBPUSHER_INCLUDES_SENSORGROUPTEMPLATEENTITY_H_
#include "SensorGroupTemplate.h"
#include "EntityInterface.h"
/**
* @brief Interface template for sensor group implementations with entities.
*
* @details This is a derived template of SensorGroupTemplate.
* Internal usage of _baseSensors is not synchronized via
* acquireSensors()/releaseSensors() as it is only accessed (e.g. in
* (de-)constructor) when no other caller is expected to access
* _baseSensors.
*
* @ingroup pusherplugins
*/
template <class S, class E>
class SensorGroupTemplateEntity : public SensorGroupTemplate<S> {
//the template shall only be instantiated for classes which derive from EntityInterface
static_assert(std::is_base_of<EntityInterface, E>::value, "E must derive from EntityInterface!");
protected:
//mention all required parent attributes and functions here to avoid compiler errors
using SensorGroupInterface::_disabled;
using SensorGroupInterface::_groupName;
using SensorGroupInterface::_interval;
using SensorGroupInterface::_keepRunning;
using SensorGroupInterface::_pendingTasks;
using SensorGroupInterface::_timer;
using SensorGroupInterface::lg;
using SensorGroupInterface::nextReadingTime;
using SensorGroupTemplate<S>::_sensors;
public:
SensorGroupTemplateEntity(const std::string groupName)
: SensorGroupTemplate<S>(groupName),
_entity(nullptr) {}
SensorGroupTemplateEntity(const SensorGroupTemplateEntity &other)
: SensorGroupTemplate<S>(other),
_entity(other._entity) {}
virtual ~SensorGroupTemplateEntity() {}
SensorGroupTemplateEntity &operator=(const SensorGroupTemplateEntity &other) {
SensorGroupTemplate<S>::operator=(other);
_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 (_disabled) {
return;
}
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) {
if (!_entity->isDisabled()) {
_keepRunning = true;
_pendingTasks++;
_timer->async_wait(_entity->getStrand()->wrap(std::bind(&SensorGroupTemplateEntity::readAsync, this)));
LOG(info) << "Sensorgroup " << _groupName << " started.";
}
} else {
LOG(error) << "No entity set for group " << _groupName << "! Cannot start polling.";
}
}
protected:
/**
* @brief Asynchronous callback if _timer expires.
*
* @details Issues a read() and sets the timer again if _keepRunning is true.
*/
void readAsync() final override {
this->read();
if (_timer && _keepRunning && !_disabled && !_entity->isDisabled()) {
_timer->expires_at(timestamp2ptime(nextReadingTime()));
_pendingTasks++;
_timer->async_wait(_entity->getStrand()->wrap(std::bind(&SensorGroupTemplateEntity::readAsync, this)));
}
_pendingTasks--;
}
E *_entity; ///< Entity this group is associated to
};
#endif /* DCDBPUSHER_INCLUDES_SENSORGROUPTEMPLATEENTITY_H_ */
......@@ -35,7 +35,16 @@ cat << EOF > ${PLUGIN_NAME}SensorGroup.cpp
#include "timestamp.h"
${PLUGIN_NAME}SensorGroup::${PLUGIN_NAME}SensorGroup(const std::string& name) :
SensorGroupTemplate(name) {
EOF
if [ "$enableEntities" = true ]
then
echo " SensorGroupTemplateEntity(name) {" >> ${PLUGIN_NAME}SensorGroup.cpp
else
echo " SensorGroupTemplate(name) {" >> ${PLUGIN_NAME}SensorGroup.cpp
fi
cat << EOF >> ${PLUGIN_NAME}SensorGroup.cpp
/*
* TODO
* Construct attributes
......@@ -43,7 +52,16 @@ ${PLUGIN_NAME}SensorGroup::${PLUGIN_NAME}SensorGroup(const std::string& name) :
}
${PLUGIN_NAME}SensorGroup::${PLUGIN_NAME}SensorGroup(const ${PLUGIN_NAME}SensorGroup& other) :
SensorGroupTemplate(other) {
EOF
if [ "$enableEntities" = true ]
then
echo " SensorGroupTemplateEntity(other) {" >> ${PLUGIN_NAME}SensorGroup.cpp
else
echo " SensorGroupTemplate(other) {" >> ${PLUGIN_NAME}SensorGroup.cpp
fi
cat << EOF >> ${PLUGIN_NAME}SensorGroup.cpp
/*
* TODO
* Copy construct attributes
......
......@@ -33,8 +33,16 @@ cat << EOF > ${PLUGIN_NAME}SensorGroup.h
#ifndef ${PLUGIN_NAME_UPC}_${PLUGIN_NAME_UPC}SENSORGROUP_H_
#define ${PLUGIN_NAME_UPC}_${PLUGIN_NAME_UPC}SENSORGROUP_H_
#include "../../includes/SensorGroupTemplate.h"
EOF
if [ "$enableEntities" = true ]
then
echo "#include \"../../includes/SensorGroupTemplateEntity.h\"" >> ${PLUGIN_NAME}SensorGroup.h
else
echo "#include \"../../includes/SensorGroupTemplate.h\"" >> ${PLUGIN_NAME}SensorGroup.h
fi
cat << EOF >> ${PLUGIN_NAME}SensorGroup.h
#include "${PLUGIN_NAME}SensorBase.h"
/**
......@@ -46,7 +54,7 @@ EOF
if [ "$enableEntities" = true ]
then
echo "class ${PLUGIN_NAME}SensorGroup : public SensorGroupTemplate<${PLUGIN_NAME}SensorBase, ${PLUGIN_NAME}${ENTITY_NAME}> {" >> ${PLUGIN_NAME}SensorGroup.h
echo "class ${PLUGIN_NAME}SensorGroup : public SensorGroupTemplateEntity<${PLUGIN_NAME}SensorBase, ${PLUGIN_NAME}${ENTITY_NAME}> {" >> ${PLUGIN_NAME}SensorGroup.h
else
echo "class ${PLUGIN_NAME}SensorGroup : public SensorGroupTemplate<${PLUGIN_NAME}SensorBase> {" >> ${PLUGIN_NAME}SensorGroup.h
fi
......
......@@ -30,11 +30,11 @@
#include <functional>
BACnetSensorGroup::BACnetSensorGroup(const std::string &name)
: SensorGroupTemplate(name), _deviceInstance(0) {
: SensorGroupTemplateEntity(name), _deviceInstance(0) {
}
BACnetSensorGroup::BACnetSensorGroup(const BACnetSensorGroup &other)
: SensorGroupTemplate(other),
: SensorGroupTemplateEntity(other),
_deviceInstance(other._deviceInstance) {}
BACnetSensorGroup::~BACnetSensorGroup() {}
......
......@@ -28,7 +28,7 @@
#ifndef BACNETSENSORGROUP_H_
#define BACNETSENSORGROUP_H_
#include "../../includes/SensorGroupTemplate.h"
#include "../../includes/SensorGroupTemplateEntity.h"
#include "BACnetSensorBase.h"
/**
......@@ -36,7 +36,7 @@
*
* @ingroup bacnet
*/
class BACnetSensorGroup : public SensorGroupTemplate<BACnetSensorBase, BACnetClient> {
class BACnetSensorGroup : public SensorGroupTemplateEntity<BACnetSensorBase, BACnetClient> {
public:
BACnetSensorGroup(const std::string &name);
......
......@@ -34,11 +34,11 @@
#include <iostream>
IPMISensorGroup::IPMISensorGroup(const std::string &name)
: SensorGroupTemplate(name) {
: SensorGroupTemplateEntity(name) {
}
IPMISensorGroup::IPMISensorGroup(const IPMISensorGroup &other)
: SensorGroupTemplate(other) {
: SensorGroupTemplateEntity(other) {
}
IPMISensorGroup::~IPMISensorGroup() {}
......
......@@ -28,7 +28,7 @@
#ifndef IPMISENSORGROUP_H_
#define IPMISENSORGROUP_H_
#include "../../includes/SensorGroupTemplate.h"
#include "../../includes/SensorGroupTemplateEntity.h"
#include "IPMISensorBase.h"
/**
......@@ -36,7 +36,7 @@
*
* @ingroup ipmi
*/
class IPMISensorGroup : public SensorGroupTemplate<IPMISensorBase, IPMIHost> {
class IPMISensorGroup : public SensorGroupTemplateEntity<IPMISensorBase, IPMIHost> {
public:
IPMISensorGroup(const std::string &name);
......
......@@ -33,11 +33,11 @@
#include <boost/property_tree/xml_parser.hpp>