Commit e2c8026a authored by Alessio Netti's avatar Alessio Netti
Browse files

DA: auto build mode for SensorNavigator

- If no sensor hierarchy is specified, SensorNavigator will build a
tree by interpreting each sensor name as a dot-separated path
- Minor changes to README
parent 165af023
......@@ -160,6 +160,7 @@ file. The following is instead a list of configuration parameters that are avail
| interval | Specifies how often the analyzer will be invoked to perform computations, and thus the sampling interval of its output sensors. Only used for analyzers in _streaming_ mode.
| minValues | Minimum number of readings that need to be stored in output sensors before these are pushed as MQTT messages. Only used for analyzers in _streaming_ mode.
| mqttPart | Part of the MQTT topic associated to this analyzer. Only used when the Unit system is not employed (see this [section](#mqttTopics)).
| sync | If set to _true_, computation will be performed at time intervals synchronized with sensor readings.
| duplicate | If set to _false_, only one analyzer object will be instantiated. Such analyzer will perform computation over all units that are instantiated, at every interval, sequentially. If set to _true_, the analyzer object will be duplicated such that each copy will have one unit associated to it. This allows to exploit parallelism between units, but results in separate models to avoid race conditions.
| streaming | If set to _true_, the analyzer will operate in _streaming_ mode, pushing output sensors regularly. If set to _false_, the analyzer will instead operate in _on-demand_ mode.
| input | Block of input sensors that must be used to instantiate the units of this analyzer. These can both be a list of strings, or fully-qualified _Sensor_ blocks containing specific attributes (see DCDBPusher Readme).
......
......@@ -6,6 +6,7 @@
const string SensorNavigator::rootKey = "__root__";
const string SensorNavigator::templateKey = "__template__";
const string SensorNavigator::pathSeparator = ".";
void SensorNavigator::clearTree() {
if(_sensorTree) {
......@@ -70,9 +71,7 @@ void SensorNavigator::buildTree(const string& hierarchy, const vector<string> *s
vector<string> hierarchyVec;
string hBuf = hierarchy;
size_t pos;
if(hierarchy.empty())
hierarchyVec.push_back("");
else
if(!hierarchy.empty())
while( !hBuf.empty() ) {
pos = hBuf.find(delimiter);
hierarchyVec.push_back(hBuf.substr(0, pos));
......@@ -85,17 +84,25 @@ void SensorNavigator::buildTree(const string& hierarchy, const vector<string> *s
}
void SensorNavigator::buildTree(const vector<string> *hierarchy, const vector<string> *sensors, const vector<string> *topics) {
if(sensors->size() > 0 && hierarchy->size() > 0)
if(sensors && sensors->size() > 0)
clearTree();
else
throw invalid_argument("SensorNavigator: cannot build sensor hierarchy!");
_hierarchy = new vector<boost::regex>();
string s="";
//Each level in the hierarchy includes the regular expressions at the upper levels
for(const auto& l: *hierarchy) {
s += l;
_hierarchy->push_back(boost::regex(s));
bool autoMode = false;
if(!hierarchy || hierarchy->empty()) {
_hierarchy = NULL;
autoMode = true;
}
else {
_hierarchy = new vector<boost::regex>();
string s = "";
//Each level in the hierarchy includes the regular expressions at the upper levels
for (const auto &l: *hierarchy) {
s += l;
_hierarchy->push_back(boost::regex(s));
}
autoMode = false;
}
_usingTopics = topics != NULL;
......@@ -106,7 +113,10 @@ void SensorNavigator::buildTree(const vector<string> *hierarchy, const vector<st
_sensorTree->insert(make_pair(rootKey, rootNode));
//We iterate over all sensor names that were supplied and build up the tree
for(int i=0; i < (int)sensors->size(); i++)
_addSensor(sensors->at(i), topics ? topics->at(i) : "");
if(autoMode)
_addAutoSensor(sensors->at(i), topics ? topics->at(i) : "");
else
_addSensor(sensors->at(i), topics ? topics->at(i) : "");
}
void SensorNavigator::buildCassandraTree(const map<string, vector<string> > *table, const string& root, const string& ignore) {
......@@ -324,6 +334,47 @@ void SensorNavigator::_addSensor(const string& name, const string& topic) {
_sensorTree->insert(make_pair(name, sNode));
}
void SensorNavigator::_addAutoSensor(const string& name, const string& topic) {
string last=rootKey, prev="";
size_t sepPos = -1;
int d=0;
//We verify how deep in the hierarchy the input sensor is by searching for the appropriate path separators
while((sepPos = name.find(pathSeparator, sepPos+1)) != std::string::npos) {
//prev contains the name of the parent for the current node in the subtree
prev = last;
//last is the name of the node we are currently at; the sensor lies in this subtree
last = name.substr(0, sepPos+1);
//If this node has never been instantiated, we create it now
if( !_sensorTree->count(last) ) {
Node newNode = {d, set<string>(), set<string>(), prev, topic};
_sensorTree->insert(make_pair(last, newNode));
//we add the current node to the list of children of its parent
_sensorTree->at(prev).children.insert(last);
}
else if(_usingTopics) {
// Intersecting the topics from the new sensor and the existing hierarchical node
int pCtr = 0;
Node& target = _sensorTree->at(last);
while( pCtr < (int)topic.length() && pCtr < (int)target.topic.length() && target.topic[pCtr] == topic[pCtr])
pCtr++;
target.topic = target.topic.substr(0, pCtr);
}
//Refreshing tree depth
if( d > _treeDepth )
_treeDepth = d;
d++;
}
//Once out of the loop, we know to which node the sensor actually belongs
_sensorTree->at(last).sensors.insert(name);
//We add the sensor node corresponding to the current sensor
Node sNode = {d-1, set<string>(), set<string>(), last, topic};
_sensorTree->insert(make_pair(name, sNode));
}
void SensorNavigator::_addCassandraSensor(const string& name, const map<string, vector<string> > *table, int depth, boost::regex ignore) {
for(const auto& s : table->at(name)) {
if (!boost::regex_search(s.c_str(), _match, ignore)) {
......
......@@ -30,6 +30,8 @@ public:
static const string rootKey;
//Template identifier
static const string templateKey;
//Separator for tree levels used in auto mode
static const string pathSeparator;
//Internal structure to store nodes
struct Node {
......@@ -74,8 +76,10 @@ public:
* sensor organization. For example, for sensor name mpp3.rack01.chassis02.node03.cpu01.temp, the
* dots define the different levels in the architecture, and the corresponding list of regexes
* would be ["mpp3.", "rack\d{2}.", "chassis\d{2}.", "node\d{2}.", "cpu\d{2}"]. The last part
* of the sensor name is supposed to constitute a "leaf" in the tree.
* The functionality implemented here requires careful naming of the sensors by developers.
* of the sensor name is supposed to constitute a "leaf" in the tree. If the input vector is
* empty, the tree is built automatically by interpreting each "." in the sensor name as a tree
* level separator. The functionality implemented here requires careful naming of the sensors
* by developers.
*
* @param hierarchy The vector of regular expression strings that define the hierarchy
* @param sensors The vector of sensor names that must be organized in a hierarchical tree
......@@ -88,7 +92,9 @@ public:
* @brief Creates a sensor tree.
*
* Overloaded version of the method by the same name. Takes as input a string containing a
* comma-separated list of regular expressions defining the sensor hierarchy.
* space-separated list of regular expressions defining the sensor hierarchy. If the string is
* empty, the tree is built automatically by interpreting each "." in the sensor name as a tree
* level separator.
*
* @param hierarchy String of comma-separated expressions that define the hierarchy
* @param sensors The vector of sensor names that must be organized in a hierarchical tree
......@@ -96,7 +102,7 @@ public:
* @param delimiter Sequence of characters used to split the sensor hierarchy string in tokens
*
**/
void buildTree(const string& hierarchy, const vector<string> *sensors, const vector<string> *topics=NULL, const string& delimiter=",");
void buildTree(const string& hierarchy, const vector<string> *sensors, const vector<string> *topics=NULL, const string& delimiter=" ");
/**
* @brief Creates a sensor tree starting from a Cassandra-style table.
......@@ -287,6 +293,7 @@ public:
protected:
void _addSensor(const string& name, const string& topic);
void _addAutoSensor(const string& name, const string& topic);
void _addCassandraSensor(const string& name, const map<string, vector<string> > *table, int depth, boost::regex ignore);
bool _isSensorNode(const Node& node) { return node.sensors.empty() && node.children.empty(); }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment