ConfiguratorTemplate.h 28.1 KB
Newer Older
1
/*
2
 * ConfiguratorTemplate.h
3
4
 *
 *  Created on: 13.01.2018
5
 *      Author: Micha Mueller, Carla Guillen
6
7
 */

8
9
#ifndef SRC_CONFIGURATORTEMPLATE_H_
#define SRC_CONFIGURATORTEMPLATE_H_
10

11
#include <map>
Alessio Netti's avatar
Alessio Netti committed
12
#include <set>
13
14
15
16
17

#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/info_parser.hpp>
18
#include "ConfiguratorInterface.h"
19
#include "sensorbase.h"
20
#include "SensorGroupTemplate.h"
21
#include "version.h"
22

23
#include <algorithm>
Alessio Netti's avatar
Alessio Netti committed
24
25
26
27
#include <iostream>
#include <sstream>
#include <iomanip>

28
//#define STRCMP(node,str) boost::iequals(node.first,str) //DEPRECATED
Micha Mueller's avatar
Micha Mueller committed
29
#define CFG_VAL	boost::property_tree::iptree&
30

31
#define ADD						BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config)
32
#define ATTRIBUTE(name,setter)		do { if (boost::iequals(val.first, name)) { s.setter(val.second.data()); } } while(0)
Micha Mueller's avatar
Micha Mueller committed
33
#define SETTING(name)				if (boost::iequals(val.first, name))
34

35
36
#define DEFAULT_CACHE_INTERVAL 900000

Micha Mueller's avatar
Micha Mueller committed
37
/**
38
 * Non-virtual interface template for the configurators.
Micha Mueller's avatar
Micha Mueller committed
39
 */
40
template <class SBase, class SGroup, class SEntity = nullptr_t>
41
class ConfiguratorTemplate : public ConfiguratorInterface {
42
	//the template shall only be instantiated for classes which derive from SensorBase/SensorGroup
43
	static_assert(std::is_base_of<SensorBase, SBase>::value, "SBase must derive from SensorBase!");
44
	static_assert(std::is_base_of<SensorGroupInterface, SGroup>::value, "SGroup must derive from SensorGroupInterface!");
45

46
protected:
Michael Ott's avatar
Michael Ott committed
47
	typedef std::map<std::string, SBase*> sBaseMap_t;
48
49
	typedef std::map<std::string, SGroup*> sGroupMap_t;
	typedef std::map<std::string, SEntity*> sEntityMap_t;
50

51
52
53
	using SB_Ptr = std::shared_ptr<SBase>;
	using SG_Ptr = std::shared_ptr<SGroup>;

54
55
56
57
	const char COMMA = ',';
	const char OPEN_SQBRKET = '[';
	const char CLOSE_SQBRKET = ']';
	const char DASH = '-';
58
	
59
public:
60
	ConfiguratorTemplate() :
61
62
63
		_entityName("INVALID"),
		_groupName("INVALID"),
		_baseName("INVALID"),
64
65
		_cfgPath(""),
		_mqttPrefix(""),
66
		_cacheInterval(DEFAULT_CACHE_INTERVAL) {}
67

68
69
	ConfiguratorTemplate(const ConfiguratorTemplate&) = delete;

70
	virtual ~ConfiguratorTemplate() {
71
72
73
		for (auto e : _sensorEntitys) {
			delete e;
		}
Michael Ott's avatar
Michael Ott committed
74
75
76
		for (auto tb : _templateSensorBases) {
			delete tb.second;
		}
77
		for (auto tg : _templateSensorGroups) {
78
			delete tg.second;
79
80
		}
		for (auto te : _templateSensorEntitys) {
81
			delete te.second;
82
		}
83
		_sensorGroupInterfaces.clear();
84
85
		_sensorGroups.clear();
		_sensorEntitys.clear();
Michael Ott's avatar
Michael Ott committed
86
		_templateSensorBases.clear();
87
		_templateSensorGroups.clear();
88
		_templateSensorEntitys.clear();
89
	}
90

91
92
	ConfiguratorTemplate& operator=(const ConfiguratorTemplate&) = delete;

93
94
95
	std::string getVersion() {
		return std::string(VERSION);
	}
Micha Mueller's avatar
Micha Mueller committed
96
97
	/**
	 * Read in the given configuration
98
	 *
99
100
	 * Overwriting this method is only required if a custom logic is really necessary!
	 *
Micha Mueller's avatar
Micha Mueller committed
101
	 * @param	cfgPath Path to the config-file
102
103
	 *
	 * @return	True on success, false otherwise
Micha Mueller's avatar
Micha Mueller committed
104
	 */
105
	bool readConfig(std::string cfgPath) {
106
		_cfgPath = cfgPath;
107
108
109
110
111

		boost::property_tree::iptree cfg;
		boost::property_tree::read_info(cfgPath, cfg);

		//read global variables (if present overwrite those from global.conf)
112
113
		readGlobal(cfg);

114
115
116
117
118
119
		//read groups and templates for groups. If present also entity/-template stuff
		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, cfg) {
			//template entity
			if (boost::iequals(val.first, "template_" + _entityName)) {
				LOG(debug) << "Template " << _entityName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
Micha Mueller's avatar
Micha Mueller committed
120
121
122
					//name of an entity is only used to identify templates and is not stored otherwise
					SEntity* entity = new SEntity();
					if (readSensorEntity(*entity, val.second, true)) {
123
						auto ret = _templateSensorEntitys.insert(std::pair<std::string, SEntity*>(val.second.data(), entity));
124
						if(!ret.second) {
125
							LOG(warning) << "Template " << _entityName << " " << val.second.data() << " already exists! Omitting...";
Micha Mueller's avatar
Micha Mueller committed
126
							delete entity;
127
128
129
						}
					} else {
						LOG(warning) << "Template " << _entityName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
Micha Mueller's avatar
Micha Mueller committed
130
						delete entity;
131
132
133
134
135
136
137
					}
				}
			//template group
			} else if (boost::iequals(val.first, "template_" + _groupName)) {
				LOG(debug) << "Template " << _groupName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					SGroup* group = new SGroup(val.second.data());
138
					if (readSensorGroup(*group, val.second, true)) {
139
						auto ret = _templateSensorGroups.insert(std::pair<std::string, SGroup*>(val.second.data(), group));
140
						if(!ret.second) {
141
							LOG(warning) << "Template " << _groupName << " " << val.second.data() << " already exists! Omitting...";
Micha Mueller's avatar
Micha Mueller committed
142
							delete group;
143
						}
144
145
					} else {
						LOG(warning) << "Template " << _groupName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
Micha Mueller's avatar
Micha Mueller committed
146
						delete group;
147
148
					}
				}
Michael Ott's avatar
Michael Ott committed
149
150
151
152
153
			//template base
			} else if (boost::iequals(val.first, "template_" + _baseName)) {
				LOG(debug) << "Template " << _baseName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					SBase* base = new SBase(val.second.data());
154
					if (readSensorBase(*base, val.second, true)) {
Michael Ott's avatar
Michael Ott committed
155
156
157
158
159
160
161
162
163
164
						auto ret = _templateSensorBases.insert(std::pair<std::string, SBase*>(val.second.data(), base));
						if(!ret.second) {
							LOG(warning) << "Template " << _baseName << " " << val.second.data() << " already exists! Omitting...";
							delete base;
						}
					} else {
						LOG(warning) << "Template " << _baseName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
						delete base;
					}
				}
165
166
167
168
169
			//template single sensor
			} else if (boost::iequals(val.first, "template_single_" + _baseName)) {
				LOG(debug) << "Template single " << _baseName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					SGroup* group = new SGroup(val.second.data());
170
					if (readSensorGroup(*group, val.second, true)) {
171
						//group which consists of only one sensor
172
						SB_Ptr sensor = std::make_shared<SBase>(val.second.data());
173
						if (readSensorBase(*sensor, val.second, true)) {
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
							group->pushBackSensor(sensor);
							auto ret = _templateSensorGroups.insert(std::pair<std::string, SGroup*>(val.second.data(), group));
							if(!ret.second) {
								LOG(warning) << "Template single " << _baseName << " " << val.second.data() << " already exists! Omitting...";
								delete group;
							}
						} else {
							LOG(warning) << "Template single " << _baseName << " " << val.second.data() << " could not be read! Omitting";
							delete group;
						}
					} else {
						LOG(warning) << "Template single " << _baseName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
						delete group;
					}
				}
189
190
191
192
			//entity
			} else if (boost::iequals(val.first, _entityName)) {
				LOG(debug) << _entityName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
Micha Mueller's avatar
Micha Mueller committed
193
194
					SEntity* entity = new SEntity();
					if (readSensorEntity(*entity, val.second, false)) {
195
196
197
						_sensorEntitys.push_back(entity);
					} else {
						LOG(warning) << _entityName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
Micha Mueller's avatar
Micha Mueller committed
198
						delete entity;
199
					}
200
201
202
203
204
				}
			//group
			} else if (boost::iequals(val.first, _groupName)) {
				LOG(debug) << _groupName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
205
					SG_Ptr group = std::make_shared<SGroup>(val.second.data());
206
					if (readSensorGroup(*group, val.second)) {
207
						storeSensorGroup(group);
208
209
210
211
					} else {
						LOG(warning) << _groupName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
					}
				}
212
213
214
215
			//single sensor
			} else if (boost::iequals(val.first, "single_" + _baseName)) {
				LOG(debug) << "Single " << _baseName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
216
					SG_Ptr group = std::make_shared<SGroup>(val.second.data());
217
218
					if (readSensorGroup(*group, val.second)) {
						//group which consists of only one sensor
219
						SB_Ptr sensor;
220
221
						//perhaps one sensor is already present because it was copied from the template group
						if (group->getSensors().size() != 0) {
222
223
224
225
226
227
228
229
230
							sensor = std::dynamic_pointer_cast<SBase>(group->getSensors()[0]);
							//check if cast was successful (sensor != nullptr)
							if (sensor) {
								sensor->setName(val.second.data());
								if (readSensorBase(*sensor, val.second)) {
									storeSensorGroup(group);
								} else {
									LOG(warning) << "Single " << _baseName << " " << val.second.data() << " could not be read! Omitting";
								}
231
							} else {
232
								LOG(warning) << "Single " << _baseName << " " << val.second.data() << " had a type mismatch when casting! Omitting";
233
234
							}
						} else {
235
							sensor = std::make_shared<SBase>(val.second.data());
236
237
238
239
240
241
242
243
244
245
246
							if (readSensorBase(*sensor, val.second)) {
								group->pushBackSensor(sensor);
								storeSensorGroup(group);
							} else {
								LOG(warning) << "Single " << _baseName << " " << val.second.data() << " could not be read! Omitting";
							}
						}
					} else {
						LOG(warning) << "Single " << _baseName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
					}
				}
Alessio Netti's avatar
Alessio Netti committed
247
			} else if( !boost::iequals(val.first, "global") ) {
248
249
			    LOG(error) << "\"" << val.first << "\": unknown construct!";
			    return false;
250
251
			}
		}
252
		//read of config finished. Now we build the mqtt-topic for every sensor
253
		return constructSensorTopics();
254
255
256
	}

	/**
Alessio Netti's avatar
Alessio Netti committed
257
	 * Clear internal storage and returns the plugin to the uninitialized state.
258
	 */
Alessio Netti's avatar
Alessio Netti committed
259
	void clearConfig() final {
Micha Mueller's avatar
Micha Mueller committed
260
261
		//bring everything to a halt
		for(auto g : _sensorGroups) {
262
			g->stop();
Micha Mueller's avatar
Micha Mueller committed
263
264
265
266
267
268
		}

		//wait until everything is halted
		for(auto g : _sensorGroups) {
			g->wait();
		}
269

270
271
272
273
		//clean up sensors/groups/entitys and templates
		for(auto e : _sensorEntitys) {
			delete e;
		}
Michael Ott's avatar
Michael Ott committed
274
275
276
		for (auto tb : _templateSensorBases) {
			delete tb.second;
		}
277
		for (auto tg : _templateSensorGroups) {
278
			delete tg.second;
279
280
		}
		for (auto te : _templateSensorEntitys) {
281
			delete te.second;
282
		}
283
		_sensorGroupInterfaces.clear();
Micha Mueller's avatar
Micha Mueller committed
284
		_sensorGroups.clear();
285
		_sensorEntitys.clear();
Michael Ott's avatar
Michael Ott committed
286
		_templateSensorBases.clear();
287
		_templateSensorGroups.clear();
288
		_templateSensorEntitys.clear();
Alessio Netti's avatar
Alessio Netti committed
289
290
291
292
293
294
295
296
297
	}

	/**
	 * Clear internal storage and read in the configuration again.
	 *
	 * @return	True on success, false otherwise
	 */
	bool reReadConfig() final {
		clearConfig();
298

Micha Mueller's avatar
Micha Mueller committed
299
		//back to the very beginning
300
		return readConfig(_cfgPath);
301
	}
302

303
304
305
306
307
	/**
	 * Print configuration as read in.
	 *
	 * @param ll    Logging level to log with
	 */
308
	void printConfig(LOG_LEVEL ll) final {
309
	  LOG_VAR(ll) << "    General: ";
310
	  if (_mqttPrefix != "") {
311
	    LOG_VAR(ll) << "        MQTT-Prefix: " << _mqttPrefix;
312
	  } else {
313
	    LOG_VAR(ll) << "        MQTT-Prefix: DEFAULT";
314
315
	  }
	  if (_cacheInterval != DEFAULT_CACHE_INTERVAL) {
316
	    LOG_VAR(ll) << "        Cache interval: " << _cacheInterval << " ms";
317
	  } else {
318
	    LOG_VAR(ll) << "        Cache interval: DEFAULT";
319
320
	  }

321
	  //prints plugin specific configurator attributes and entities if present
322
323
	  printConfiguratorConfig(ll);

324
	  LOG_VAR(ll) << "    Groups:";
325
	  for(auto g : _sensorGroups) {
326
327
328
	    g->SensorGroupInterface::printConfig(ll);
	    g->printConfig(ll);
	    g->SensorGroupTemplate<SBase>::printConfig(ll);
329
330
331
	  }
	}

332
333
334
335
336
337
338
	/**
	 * Sets internal variables with the ones provided by pluginSettings.
	 * This method should be called once after constructing a configurator
	 * to provide him with the global default values.
	 *
	 * @param pluginSettings	Struct with global default settings for the plugins.
	 */
339
	void setGlobalSettings(const pluginSettings_t& pluginSettings) final {
340
341
		_mqttPrefix = pluginSettings.mqttPrefix;
		_cacheInterval = pluginSettings.cacheInterval;
342
		derivedSetGlobalSettings(pluginSettings);
343
344
	}

Micha Mueller's avatar
Micha Mueller committed
345
346
347
348
349
	/**
	 * Get all sensor groups
	 *
	 * @return	Vector containing pointers to all sensor groups of this plugin
	 */
350
	std::vector<SGroupPtr>& getSensorGroups() final {
351
352
353
		return _sensorGroupInterfaces;
	}

354
protected:
355
	void storeSensorGroup(SG_Ptr sGroup) {
356
357
		_sensorGroups.push_back(sGroup);
		_sensorGroupInterfaces.push_back(sGroup);
Micha Mueller's avatar
Micha Mueller committed
358
359
	}

360
361
	/**
	 * Non-virtual interface method for class-internal use only.
362
	 * Reads and sets the common base values of a sensor base (currently none),
363
364
	 * then calls the corresponding derived function to read plugin specific
	 * values.
365
	 *
366
	 * @param sBase		The sensor base for which to set the values
367
	 * @param config	A boost property (sub-)tree containing the sensor values
368
	 * @param isTemplate	Are we parsing a template or a regular sensor?
369
370
371
	 *
	 * @return	True on success, false otherwise
	 */
372
	bool readSensorBase(SBase& sBase, CFG_VAL config, bool isTemplate=false) {
373
		sBase.setCacheInterval(_cacheInterval);
374
375
376
		if (!isTemplate) {
			boost::optional<boost::property_tree::iptree&> def = config.get_child_optional("default");
			if(def) {
377
				//we copy all values from default
378
379
380
381
382
383
384
385
				LOG(debug) << "  Using \"" << def.get().data() << "\" as default.";
				auto it = _templateSensorBases.find(def.get().data());
				if(it != _templateSensorBases.end()) {
					sBase = *(it->second);
					sBase.setName(config.data());
				} else {
					LOG(warning) << "Template " << _baseName << "\" " << def.get().data() << "\" not found! Using standard values.";
				}
Michael Ott's avatar
Michael Ott committed
386
387
388
			}
		}
		
389
390
391
		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
			if (boost::iequals(val.first, "mqttsuffix")) {
				sBase.setMqtt(val.second.data());
392
			} else if (boost::iequals(val.first, "skipConstVal")) {
Alessio Netti's avatar
Alessio Netti committed
393
				sBase.setSkipConstVal(to_bool(val.second.data()));
394
			} else if (boost::iequals(val.first, "delta")) {
Alessio Netti's avatar
Alessio Netti committed
395
				sBase.setDelta(to_bool(val.second.data()));
396
397
398
			} else if (boost::iequals(val.first, "sink")) {
				sBase.setSinkPath( val.second.data() );
			} else if (boost::iequals(val.first, "subSampling")) {
Alessio Netti's avatar
Alessio Netti committed
399
				sBase.setSubsampling(std::stoul(val.second.data()));
400
			}
401
402
403
		}
		sensorBase(sBase, config);
		return true;
404
405
406
407
	}

	/**
	 * Non-virtual interface method for class-internal use only.
408
409
	 * Reads and sets the common base values of a sensor group, then calls
	 * the corresponding derived function to read in plugin specific values.
410
	 *
411
	 * @param sGroup	The sensor group for which to set the values
412
	 * @param config	A boost property (sub-)tree containing the sensor values
413
	 * @param isTemplate	Is this a template or a regular group?
414
415
416
	 *
	 * @return	True on success, false otherwise
	 */
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
	bool readSensorGroup(SGroup& sGroup, CFG_VAL config, bool isTemplate=false) {
		//first check if default group is given, unless we are reading a template already
		if (!isTemplate) {
			boost::optional<boost::property_tree::iptree&> def = config.get_child_optional("default");
			if(def) {
				//we copy all values from default (including copy constructing its sensors)
				//if own sensors are specified they are appended
				LOG(debug) << "  Using \"" << def.get().data() << "\" as default.";
				auto it = _templateSensorGroups.find(def.get().data());
				if(it != _templateSensorGroups.end()) {
					sGroup = *(it->second);
					sGroup.setGroupName(config.data());
				} else {
					LOG(warning) << "Template " << _groupName << "\" " << def.get().data() << "\" not found! Using standard values.";
				}
432
433
434
			}
		}

435
		//read in values inherited from SensorGroupInterface
436
		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
437
			if (boost::iequals(val.first, "interval")) {
438
				sGroup.setInterval(stoull(val.second.data()));
439
			} else if (boost::iequals(val.first, "minValues")) {
440
				sGroup.setMinValues(stoull(val.second.data()));
441
442
			} else if (boost::iequals(val.first, "mqttPart")) {
				sGroup.setMqttPart(val.second.data());
443
444
445
446
447
448
			} else if (boost::iequals(val.first, "sync")) {
				if (val.second.data() == "off") {
					sGroup.setSync(false);
				} else {
					sGroup.setSync(true);
				}
449
			} else if (boost::iequals(val.first, _baseName)) {
450
451
452
				if (!isTemplate) {
					LOG(debug) << "  " << _baseName << " " << val.second.data();
				}
453
				SB_Ptr sensor = std::make_shared<SBase>(val.second.data());
454
				if (readSensorBase(*sensor, val.second, isTemplate)) {
455
					sGroup.pushBackSensor(sensor);
456
				} else if (!isTemplate) {
457
					LOG(warning) << _baseName << " " << sGroup.getGroupName() << "::" << sensor->getName() << " could not be read! Omitting";
458
				}
459
460
461
			}
		}

462
463
464
		//TODO keep debug logging for config?
//		LOG(debug) << "  Interval : " << sGroup.getInterval();
//		LOG(debug) << "  minValues: " << sGroup.getMinValues();
465

466
467
		sensorGroup(sGroup, config);
		return true;
468
469
	}

470
471
472
473
474
475
476
	/**
	 * Non-virtual interface method for class-internal use only.
	 * Reads and sets the common base values of a sensor entity, then calls
	 * the corresponding derived function to read in plugin specific values.
	 *
	 * @param sEntity	The aggregating entity for which to set the values
	 * @param config	A boost property (sub-)tree containing the sensor values
Micha Mueller's avatar
Micha Mueller committed
477
478
	 * @param isTemplate	Indicate if sEntity is a template. If so, also store
	 * 						the corresponding sGroups in the template map
479
480
481
	 *
	 * @return	True on success, false otherwise
	 */
Micha Mueller's avatar
Micha Mueller committed
482
	bool readSensorEntity(SEntity& sEntity, CFG_VAL config, bool isTemplate) {
483
484
485
486
487
488
489
		//first check if default entity is given
		boost::optional<boost::property_tree::iptree&> def = config.get_child_optional("default");
		if(def) {
			//we copy all values from default
			LOG(debug) << "  Using \"" << def.get().data() << "\" as default.";
			auto it = _templateSensorEntitys.find(def.get().data());
			if(it != _templateSensorEntitys.end()) {
490
				sEntity = *(it->second);
Micha Mueller's avatar
Micha Mueller committed
491
492
				for(auto g : _templateSensorGroups) {
					if (isEntityOfGroup(*(it->second), *(g.second))) {
493
						SG_Ptr group = std::make_shared<SGroup>(*(g.second));
Micha Mueller's avatar
Micha Mueller committed
494
						setEntityForGroup(sEntity, *group);
495
						storeSensorGroup(group);
Micha Mueller's avatar
Micha Mueller committed
496
497
					}
				}
498
499
500
501
			} else {
				LOG(warning) << "Template " << _entityName << "\"" << def.get().data() << "\" not found! Using standard values.";
			}
		}
Micha Mueller's avatar
Micha Mueller committed
502

503
		sensorEntity(sEntity, config);
Micha Mueller's avatar
Micha Mueller committed
504
505
506
507

		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
			if (boost::iequals(val.first, _groupName)) {
				LOG(debug) << "  " << _groupName << " " << val.second.data();
508
				if (!val.second.empty()) {
509
510
511
512
					if (isTemplate) {
						SGroup* group = new SGroup(val.second.data());
						if(readSensorGroup(*group, val.second)) {
							setEntityForGroup(sEntity, *group);
513
514
515
516
517
518
							auto ret = _templateSensorGroups.insert(std::pair<std::string, SGroup*>(val.second.data(), group));
							if(!ret.second) {
								LOG(warning) << "Template " << _groupName << " " << val.second.data() << " already exists! Omitting...";
								delete group;
							}
						} else {
519
520
							LOG(warning) << _groupName << " " << group->getGroupName() << " could not be read! Omitting";
							delete group;
Micha Mueller's avatar
Micha Mueller committed
521
522
						}
					} else {
523
524
525
526
527
528
529
						SG_Ptr group = std::make_shared<SGroup>(val.second.data());
						if(readSensorGroup(*group, val.second)) {
							setEntityForGroup(sEntity, *group);
							storeSensorGroup(group);
						} else {
							LOG(warning) << _groupName << " " << group->getGroupName() << " could not be read! Omitting";
						}
530
531
532
533
534
					}
				}
			} else if (boost::iequals(val.first, "single_" + _baseName)) {
				LOG(debug) << "Single " << _baseName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
535
536
537
538
539
540
					if (isTemplate) {
						SGroup* group = new SGroup(val.second.data());
						//group which consists of only one sensor
						if (readSensorGroup(*group, val.second)) {
							setEntityForGroup(sEntity, *group);
							SB_Ptr sensor = std::make_shared<SBase>(val.second.data());
541
542
543
544
545
546
547
548
549
550
551
552
							if (readSensorBase(*sensor, val.second)) {
								group->pushBackSensor(sensor);
								auto ret = _templateSensorGroups.insert(std::pair<std::string, SGroup*>(val.second.data(), group));
								if(!ret.second) {
									LOG(warning) << "Template single " << _baseName << " " << val.second.data() << " already exists! Omitting...";
									delete group;
								}
							} else {
								LOG(warning) << "Template single " << _baseName << " " << val.second.data() << " could not be read! Omitting";
								delete group;
							}
						} else {
553
554
555
556
557
558
559
560
561
							LOG(warning) << "Single " << _baseName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
							delete group;
						}
					} else {
						SG_Ptr group = std::make_shared<SGroup>(val.second.data());
						//group which consists of only one sensor
						if (readSensorGroup(*group, val.second)) {
							setEntityForGroup(sEntity, *group);
							SB_Ptr sensor;
562
563
							//perhaps one sensor is already present because it was copied from the template group
							if (group->getSensors().size() != 0) {
564
565
566
567
568
569
570
571
572
								sensor = std::dynamic_pointer_cast<SBase>(group->getSensors()[0]);
								//check if cast was successful (sensor != nullptr)
								if (sensor) {
									sensor->setName(val.second.data());
									if (readSensorBase(*sensor, val.second)) {
										storeSensorGroup(group);
									} else {
										LOG(warning) << "Single " << _baseName << " " << val.second.data() << " could not be read! Omitting";
									}
573
								} else {
574
									LOG(warning) << "Single " << _baseName << " " << val.second.data() << " had a type mismatch when casting! Omitting";
575
576
								}
							} else {
577
								sensor = std::make_shared<SBase>(val.second.data());
578
579
580
581
582
583
584
								if (readSensorBase(*sensor, val.second)) {
									group->pushBackSensor(sensor);
									storeSensorGroup(group);
								} else {
									LOG(warning) << "Single " << _baseName << " " << val.second.data() << " could not be read! Omitting";
								}
							}
585
586
						} else {
							LOG(warning) << "Single " << _baseName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
587
						}
Micha Mueller's avatar
Micha Mueller committed
588
589
590
591
592
					}
				}
			}
		}

593
		if(!isTemplate) {
594
			for(const auto& g : _sensorGroups) {
595
596
597
				if(isEntityOfGroup(sEntity, *g)) {
					finalizeGroup(*g);
				}
Micha Mueller's avatar
Micha Mueller committed
598
599
			}
		}
600
		return true;
601
602
	}

Micha Mueller's avatar
Micha Mueller committed
603
	bool readGlobal(CFG_VAL config) {
604
605
606
607
608
609
610
611
612
613
614
		boost::optional<boost::property_tree::iptree&> globalVals = config.get_child_optional("global");
		if (globalVals) {
			BOOST_FOREACH(boost::property_tree::iptree::value_type &global, config.get_child("global")) {
				if (boost::iequals(global.first, "mqttprefix")) {
					_mqttPrefix = global.second.data();
					if (_mqttPrefix[_mqttPrefix.length()-1] != '/') {
						_mqttPrefix.append("/");
					}
				} else if (boost::iequals(global.first, "cacheInterval")) {
					_cacheInterval = stoul(global.second.data());
					_cacheInterval *= 1000;
615
				} 
616
			}
617
			global(config.get_child("global"));
618
		}
619
		return true;
620
621
	}

622
	/**
623
	 * Virtual interface method, responsible for setting global values specifically
624
625
626
627
	 * for its plugin.
	 *
	 * @param pluginSettings	The struct with global default plugin settings
	 */
628
629
630
	virtual void derivedSetGlobalSettings(const pluginSettings_t& pluginSettings) {
		//Overwrite if necessary
	}
631
632
633

	/**
	 * Pure virtual interface method, responsible for reading plugin-specific sensor
634
	 * base values.
635
	 *
Micha Mueller's avatar
Micha Mueller committed
636
	 * @param s			The sensor base for which to set the values
637
638
	 * @param config	A boost property (sub-)tree containing the sensor values
	 */
Micha Mueller's avatar
Micha Mueller committed
639
	virtual void sensorBase(SBase& s, CFG_VAL config) = 0;
640
641
642
643
644

	/**
	 * Pure virtual interface method, responsible for reading plugin-specific sensor
	 * group values.
	 *
Micha Mueller's avatar
Micha Mueller committed
645
	 * @param s			The sensor group for which to set the values
646
	 * @param config	A boost property (sub-)tree containing the group values
647
	 */
Micha Mueller's avatar
Micha Mueller committed
648
	virtual void sensorGroup(SGroup& s, CFG_VAL config) = 0;
649

650
651
652
653
	/**
	 * Virtual interface method, responsible for reading plugin-specific sensor
	 * entity values.
	 *
Micha Mueller's avatar
Micha Mueller committed
654
	 * @param s			The sensor entity for which to set the values
655
656
	 * @param config	A boost property (sub-)tree containing the entity values
	 */
Micha Mueller's avatar
Micha Mueller committed
657
	virtual void sensorEntity(SEntity& s, CFG_VAL config) {
658
659
660
		//Overwrite if necessary
		LOG(warning) << "Method sensorEntity called, but was not overwritten! Either you have unwanted entitys in your config file or forgot to overwrite this method";
	}
661

Micha Mueller's avatar
Micha Mueller committed
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
	/**
	 * Check if e is the corresponding entity of g
	 *
	 * @param e
	 * @param g
	 *
	 * @return	True if (g.entity == &e)
	 */
	//TODO not very convenient for writing plugins. Are there better solutions?
	virtual bool isEntityOfGroup(SEntity& e, SGroup& g) {
		//Overwrite if necessary
		LOG(warning) << "Method isEntityOfGroup called, but was not overwritten! Either you have unwanted entitys in your config file or forgot to overwrite this method";
		return false;
	}

	/**
	 * Sets e as entity for group g
	 *
	 * @param e
	 * @param g
	 */
	//TODO not very convenient for writing plugins. Are there better solutions?
	virtual void setEntityForGroup(SEntity& e, SGroup& g) {
		//Overwrite if necessary
		LOG(warning) << "Method setEntityForGroup called, but was not overwritten! Either you have unwanted entitys in your config file or forgot to overwrite this method";
	}

	/**
	 * Finalize the group g with everything it needs from its entity (set e.g. the mqttPart for entity)
	 *
	 * @param g
	 */
	//TODO not very convenient for writing plugins. Are there better solutions?
	virtual void finalizeGroup(SGroup& g) {
		//Overwrite if necessary
		LOG(warning) << "Method finalizeEntityGroup called, but was not overwritten! Either you have unwanted entitys in your config file or forgot to overwrite this method";
	}

700
701
	/**
	 * Print information about configurable configurator attributes (or nothing if no such attributes are required)
702
	 * and associated entities.
703
704
705
706
707
	 *
	 * @param ll    Severity level to log with
	 */
	virtual void printConfiguratorConfig(LOG_LEVEL ll) {
	  //Overwrite if necessary
708
	  LOG_VAR(ll) << "        No other plugin-specific general parameters or entities defined";
709
710
	}

711
712
713
714
715
	/**
	 * Virtual interface method, responsible for reading plugin-specific global values.
	 *
	 * @param config	A boost property (sub-)tree containing the global values
	 */
Micha Mueller's avatar
Micha Mueller committed
716
	virtual void global(CFG_VAL config) {}
717
	
Alessio Netti's avatar
Alessio Netti committed
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
	/**
	 * Tries to parse the given cpuString as integer numbers. On success, the specified numbers will be inserted
	 * into a set, which will be returned. On failure, an empty set is returned. A set is used to maintain uniqueness
	 * and an ascending order among the numbers although this is not strictly required.
	 *
	 * @param cpuString	String which specifies a range and/or set of numbers (e.g. "1,2,3-5,7-9,10")
	 * @return	A set of integers as specified in the cpuString. If the string could not be parsed the set will be empty.
	 */
	std::set<int> parseCpuString(const std::string& cpuString) {
		std::set<int> cpus;
		int maxCpu = 512;

		std::vector<std::string> subStrings;

		std::stringstream ssComma(cpuString);
		std::string item;
		while (std::getline(ssComma, item, ',')) {
			subStrings.push_back(item);
		}

		for (auto s : subStrings) {
			if (s.find('-') != std::string::npos) { //range of values (e.g. 1-5) specified
				std::stringstream ssHyphen(s);
				std::string min, max;
				std::getline(ssHyphen, min, '-');
				std::getline(ssHyphen, max);

				try {
					int minVal = stoi(min);
					int maxVal = stoi(max);

					for (int i = minVal; i <= maxVal; i++) {
						if (i >= 0 && i < maxCpu) {
							cpus.insert((int)i);
						}
					}
				} catch (const std::exception& e) {
					LOG(debug) << "Could not parse values \"" << min << "-" << max << "\"";
				}
			} else { //single value
				try {
					int val = stoi(s);
					if (val >= 0 && val < maxCpu) {
						cpus.insert((int)val);
					}
				} catch (const std::exception& e) {
					LOG(debug) << "Could not parse value \"" << s << "\"";
				}
			}
		}

		if (cpus.empty()) {
			LOG(warning) << "  CPUs could not be parsed!";
		} else {
			std::stringstream sstream;
			sstream << "  CPUS: ";
			for (auto i : cpus) {
				sstream << i << ", ";
			}
			std::string msg = sstream.str();
			msg.pop_back();
			msg.pop_back();
			LOG(debug) << msg;
		}
		return cpus;
	}

Alessio Netti's avatar
Alessio Netti committed
785
	/**
786
	 * @brief 		Adjusts the names of the sensors in generated groups.
Alessio Netti's avatar
Alessio Netti committed
787
788
	 *
	 * @return		true if successful, false otherwise
Alessio Netti's avatar
Alessio Netti committed
789
	 */
790
791
	bool constructSensorTopics() {
		// Sensor names are adjusted according to the respective MQTT topics
Alessio Netti's avatar
Alessio Netti committed
792
793
		for(auto& g: _sensorGroups)
			for(auto& s: g->getSensors()) {
794
795
796
				s->setMqtt(MQTTChecker::formatTopic(_mqttPrefix) + MQTTChecker::formatTopic(g->getMqttPart()) + MQTTChecker::formatTopic(s->getMqtt()));
				s->setName(s->getMqtt());
			}
Alessio Netti's avatar
Alessio Netti committed
797
		return true;
Alessio Netti's avatar
Alessio Netti committed
798
799
	}

800
801
	std::string		_entityName;
	std::string		_groupName;
802
	std::string		_baseName;
803
804
805
806

	std::string 	_cfgPath;
	std::string		_mqttPrefix;
	unsigned int	_cacheInterval;
807
808
	std::vector<SGroupPtr> 	_sensorGroupInterfaces;
	std::vector<SG_Ptr>		_sensorGroups;
809
	std::vector<SEntity*>	_sensorEntitys;
Michael Ott's avatar
Michael Ott committed
810
	sBaseMap_t		_templateSensorBases;
811
812
	sGroupMap_t		_templateSensorGroups;
	sEntityMap_t	_templateSensorEntitys;
813
814
};

815
#endif /* SRC_CONFIGURATORTEMPLATE_H_ */