vermont/src/core/Cfg.h

470 lines
13 KiB
C++

#ifndef CFG_H_
#define CFG_H_
#include "core/XMLElement.h"
#include "core/Module.h"
#include "core/ConnectionQueue.h"
#include "core/ConnectionSplitter.h"
#include "core//Timer.h"
#include <exception>
#include <string>
#include <typeinfo>
/**
* This excecption is thrown if someone try's to get an element which
* doesn't exist
*/
class IllegalEntry
: public std::exception
{
};
enum timeUnit {
SEC = 1,
mSEC= 1000,
uSEC= 1000000,
};
// some constant values (bitmaks
#define NO_ADAPTER 0
#define NEED_SPLITTER 1 << 0
#define NEED_TIMEOUT 1 << 1
class CfgBase
{
public:
CfgBase(XMLElement* e): _elem(e) { }
virtual ~CfgBase() { }
/** return a string value of an elemen
* @param name the name of the element
* @param elem the XMLElement we want to start the search, default is the root of the node
*/
std::string get(const std::string& name, XMLElement* elem = NULL);
/** searches for an optinal config entry, returns the emtpy string if not found
* @param name the name of the element
* @param elem the XMLElement we want to start the search, default is the root of the node
*/
std::string getOptional(const std::string&name, XMLElement* elem = NULL);
/** returns the integer value of an XML config entry
* @param name the name of the element
* If there is no such element in the XML file, it returns def
*/
int getInt(const std::string& name, int def = 0, XMLElement* elem = NULL);
/** returns the unsigned integer value with 32bit of an XML config entry
* @param name the name of the element
* If there is no such element in the XML file, it returns def
*/
uint32_t getUInt32(const std::string& name, uint32_t def = 0, XMLElement* elem = NULL);
/** returns the 64-bit integer value of an XML config entry
* @param name the name of the element
* If there is no such element in the XML file, it returns def
*/
int64_t getInt64(const std::string& name, int64_t def = 0, XMLElement* elem = NULL);
/** returns the bool value of an XML config entry
* @param name the name of the element
* If there is no such element in the XML file, it returns def
*/
bool getBool(const std::string& name, bool def = false, XMLElement* elem = NULL);
/** returns the double value of an XML config entry
* @param name the name of the element
* If there is no such element in the XML file, it returns def
*/
double getDouble(const std::string& name, double def = 0, XMLElement* elem = NULL);
/**
* returns the time value specified in the element in a given unit
* @param name the name of the element
* @param unit the unit in which the time should be returned
* @param elem the element whose children are search for the given name
*/
unsigned int getTimeInUnit(const std::string& name, timeUnit unit, uint32_t def = 0,
XMLElement* elem = NULL);
XMLElement* _elem;
private:
std::string _get(const std::string& name, XMLElement* elem);
};
class Cfg : public CfgBase
{
public:
friend class ConfigFile;
virtual Cfg* create(XMLElement* e) = 0;//{ return NULL; };
virtual ~Cfg() { }
/** returns the name (as written in the XML file) */
virtual std::string getName() = 0;
/** ID given in the XML file */
unsigned int getID();
/** returns all the ID's of the next processing elements in the pipeline */
std::vector<unsigned int> getNext();
/** returns true if we could reuse the other instance
* @param other Cfg describing the old/other elemen
*/
virtual bool deriveFrom(Cfg* other) = 0;
virtual void transferInstance(Cfg* other) = 0;
/** connects this module with the module from \other
* @param other the other Cfg
*/
virtual void connectInstances(Cfg* other) = 0;
virtual void setupWithoutSuccessors() = 0;
virtual void disconnectInstances() = 0;
/* start/stop the instance */
virtual void start(bool fail_if_already_running = true) = 0;
virtual void shutdown(bool fail_if_not_running = true, bool finishProperly = false) = 0;
/* see in Module for the documentation for these functions */
//virtual void postReconfiguration() = 0;
virtual void onReconfiguration1() = 0;
virtual void onReconfiguration2() = 0;
/** this method will delete the instance */
virtual void freeInstance() = 0;
// THE METHODS BELOW SHOULD NEVER BE CALLED DIRECTLY; THE SHOULD BE PRIVATE, BUT WE
// NEED A GENERIC BASE CLASS IN CfgHelper, AND MAKING THEM PROTECTED DOESN'T ALLOW THEM TO BE
// CALLED IN CfgHelper, SO THEY ARE PUBLIC. YES, WE COULD CREATE AN INTERMEDIATE CLASS WHICH EXPOSES
// THESE FUNCTIONS, BUT I DON'T THINK THIS IS WORTH THE COMPLEXITY
/** returns an instance of the module which the config element describes */
virtual Module* getInstance() = 0;
/** returns a Queue, which is used as a TimoutAdapter */
virtual Module* getQueueInstance() = 0;
protected:
Cfg(XMLElement* e) : CfgBase(e) { }
};
/**
* This is a helper class to avoid code duplication over all the other Cfg subclasses
* It helps to manage the adapter classes like Splitter, which are otherwise very difficult
* to represent.
*
* The idea is that we create the following module hierarchy if neccessary
*
* -----------------------------------------
* | QUEUE -> MODULE -> SPLITTER |
* -----------------------------------------
*
* From an outside view this box with its 3 modules looks like one module.
*/
template <typename InstanceType, typename ConfigType>
class CfgHelper
: public Cfg
{
public:
CfgHelper(XMLElement* elem, std::string name, bool freeInstance = true)
: Cfg(elem),
instance(NULL),
splitter(NULL),
queue(NULL),
notifiable(NULL),
name(name),
instanceDeleteAllowed(freeInstance)
{
}
virtual ~CfgHelper()
{
DPRINTF_INFO("~CfgHelper [%s]\n", this->getName().c_str());
shutdown(false);
freeTimeoutAdapter();
freeInstance();
freeSplitter();
}
virtual std::string getName()
{
return name;
}
/** starts the module */
virtual void start(bool fail_if_already_running = true)
{
// create instance, if not already present
if ((splitter==NULL) && (instance==NULL) && (queue==NULL))
createInstance();
if (splitter)
splitter->start(fail_if_already_running);
if (instance)
instance->start(fail_if_already_running);
if (queue)
queue->start(fail_if_already_running);
}
/* stops the module */
virtual void shutdown(bool fail_if_not_running = true, bool shutdownProperly = false)
{
if (queue)
queue->shutdown(fail_if_not_running, shutdownProperly);
if (instance)
instance->shutdown(fail_if_not_running, shutdownProperly);
if (splitter)
splitter->shutdown(fail_if_not_running, shutdownProperly);
}
/** returns the module instance (if neccessary, it will create it */
InstanceType* getInstance()
{
if (instance != NULL)
return instance;
return createInstance();
}
//void postReconfiguration() {
// instance = getInstance();
// instance->postReconfiguration();
//}
void onReconfiguration1() {
if (instance == NULL)
return;
instance->onReconfiguration1();
}
void onReconfiguration2() {
if (instance == NULL)
return;
instance->onReconfiguration2();
}
/** this method gets called *ONLY* if the instance needs a timer and the
* in the configuration there was no timer in front of the instance
*/
ConnectionQueue<typename InstanceType::dst_value_type>* getQueueInstance()
{
if (!queue) {
msg(LOG_WARNING, "queue is required by module id=%u but is not configured. Inserting a default queue with max size 1 (attention: this is inefficient!)", getID());
queue = new ConnectionQueue<typename InstanceType::dst_value_type>(1);
}
queue->connectTo(getInstance());
Notifiable* n = dynamic_cast<Notifiable*>(instance);
if (n)
n->useTimer(queue);
return queue;
}
/** returns true if we could reuse the other instance
* @param other Cfg describing the old/other element
*/
virtual bool deriveFrom(Cfg* old)
{
ConfigType* cfg = dynamic_cast<ConfigType*>(old);
if (cfg)
return this->deriveFrom(cfg);
THROWEXCEPTION("Can't derive %s from %s",
this->getName().c_str(), old->getName().c_str());
return false;
}
/** every derived class has to implement its specalised deriveFrom method */
virtual bool deriveFrom(ConfigType* old) = 0;
/** Every derived class must provide a method to actually create a instance */
virtual InstanceType* createInstance() = 0;
void freeInstance()
{
if (!instance || !instanceDeleteAllowed)
return;
delete instance;
instance = NULL;
}
/**
* tells this module that there aren't any succeeding modules during module connection
*/
virtual void setupWithoutSuccessors()
{
if (typeid(typename InstanceType::src_value_type)!=typeid(NullEmitable*)) {
msg(LOG_NOTICE, "module %s (id=%u) is source for data elements, but has no successor", getName().c_str(), getID());
getInstance()->connectToNothing();
}
}
/** connects the instances hold in the to configurations
* This method takes care to also create the neccessary adapters/wrappers
*/
virtual void connectInstances(Cfg* other)
{
// create an instance if neccessary
instance = getInstance();
// the other side has to be of type Destination<I::src_value_type>
Destination<typename InstanceType::src_value_type >* dest = NULL;
// check if we need a ConnectionQueue as TimeoutAdapter
notifiable = dynamic_cast<Notifiable*>(other->getInstance());
Timer* timer = dynamic_cast<Timer*>(instance);
if (notifiable) { // the other one is a notifiable and needs a timer set
if (timer == NULL) { // we are not a timer
ConnectionQueue<typename InstanceType::src_value_type >* destQueue;
destQueue = dynamic_cast<ConnectionQueue< typename InstanceType::src_value_type>* >
(other->getQueueInstance());
if (!destQueue)
THROWEXCEPTION("Queue is not a timer; this should never happen. Perhaps modules "
"with incompatible output/input types are connected in configuration?");
timer = destQueue;
dest = destQueue;
}
notifiable->useTimer(timer);
}
if (!dest) { // dest wasn't set yet
dest = dynamic_cast<Destination< typename InstanceType::src_value_type>* >
(other->getInstance());
if (!dest) {
msg(LOG_CRIT, "Trying to connect incompatible types: %s -> %s! Check your configuration for incompabible connections!", this->getName().c_str(), other->getName().c_str());
THROWEXCEPTION("Unexpected error: can't cast %s to matching Destination<>",
other->getName().c_str());
}
}
// call postReconfiguration(), e.g. to tell the module to resend its template
//Gerhard: postReconfiguration() is now called in Module::start()
//this->postReconfiguration();
// check if we need a splitter
if (this->getNext().size() > 1) {
if (!splitter) {
splitter = new ConnectionSplitter<typename InstanceType::src_value_type>();
instance->connectTo(splitter);
}
splitter->connectTo(dest);
} else {
instance->connectTo(dest);
}
}
/** disconnect the instances and deletes an automaticly created splitter */
void disconnectInstances()
{
// reset the timer to NULL so that the object could be deleted
Timer* timer = dynamic_cast<Timer*>(instance);
Notifiable* n = dynamic_cast<Notifiable*>(instance);
if (timer && n) {
n->useTimer(NULL);
}
/* dont disconnect/delete the queue, we could be still connected to a source
if (queue) {
queue->disconnect();
freeTimeoutAdapter();
}
*/
// this will disconnect either the splitter or the other module
if (instance)
instance->disconnect();
freeSplitter();
}
/** helper method to transfer one instance to another Cfg
* @param other the Cfg entry we want to transfer the instance to
*/
inline void transferInstance(Cfg* old)
{
ConfigType* cfg = dynamic_cast<ConfigType*>(old);
if (cfg)
return this->transferInstance(cfg);
THROWEXCEPTION("Can't transfer %s from %s",
this->getName().c_str(), old->getName().c_str());
}
/** helper method to transfer one instance to another Cfg
* @param other the Cfg entry we want to transfer the instance to
*/
inline void transferInstance(CfgHelper<InstanceType, ConfigType>* other)
{
if (other->queue) {
other->queue->disconnect();
queue = other->queue; // get the queue from the other element
other->queue = NULL; // and remove it from the other side
}
instance = other->instance;
other->instance = NULL;
}
/** returns this module's instance as a Module
* if instance does not inherit a module, NULL is returned
*/
Module* getInstanceAsModule()
{
Module* m = dynamic_cast<Module*>(instance);
return m;
}
protected:
InstanceType* instance;
ConnectionSplitter<typename InstanceType::src_value_type>* splitter;
ConnectionQueue<typename InstanceType::dst_value_type>* queue;
Notifiable* notifiable;
std::string name;
private:
bool instanceDeleteAllowed; /** if set to true, managed module will not be freed automatically */
// this methods are helper functions to clean our mess up and reset the values to sane default
inline void freeTimeoutAdapter()
{
if (!queue)
return;
delete queue;
queue = NULL;
}
/** delete the splitter if any */
inline void freeSplitter()
{
if (!splitter)
return;
delete splitter;
splitter = NULL;
}
};
#endif /*CFG_H_*/