ConfiguratorTemplate.h 27.4 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
19
20
#include "ConfiguratorInterface.h"
#include "SensorBase.h"
#include "SensorGroupTemplate.h"
21
#include "version.h"
22

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

Alessio Netti's avatar
Alessio Netti committed
27
28
29
30
#include <iostream>
#include <sstream>
#include <iomanip>

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

34
#define ADD						BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config)
35
#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
36
#define SETTING(name)				if (boost::iequals(val.first, name))
37

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

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

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

55
56
57
58
59
	const char COMMA = ',';
	const char OPEN_SQBRKET = '[';
	const char CLOSE_SQBRKET = ']';
	const char DASH = '-';

60
public:
61
	ConfiguratorTemplate() :
62
63
64
		_entityName("INVALID"),
		_groupName("INVALID"),
		_baseName("INVALID"),
65
66
67
		_cfgPath(""),
		_mqttPrefix(""),
		_cacheInterval(900000) {}
68

69
70
	ConfiguratorTemplate(const ConfiguratorTemplate&) = delete;

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

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

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

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

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

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

	/**
	 * Clear internal storage and read in the configuration again.
265
266
	 *
	 * @return	True on success, false otherwise
267
	 */
268
	bool reReadConfig() final {
Micha Mueller's avatar
Micha Mueller committed
269
270
		//bring everything to a halt
		for(auto g : _sensorGroups) {
271
			g->stop();
Micha Mueller's avatar
Micha Mueller committed
272
273
274
275
276
277
		}

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

279
280
281
282
		//clean up sensors/groups/entitys and templates
		for(auto e : _sensorEntitys) {
			delete e;
		}
Michael Ott's avatar
Michael Ott committed
283
284
285
		for (auto tb : _templateSensorBases) {
			delete tb.second;
		}
286
		for (auto tg : _templateSensorGroups) {
287
			delete tg.second;
288
289
		}
		for (auto te : _templateSensorEntitys) {
290
			delete te.second;
291
		}
292
		_sensorGroupInterfaces.clear();
Micha Mueller's avatar
Micha Mueller committed
293
		_sensorGroups.clear();
294
		_sensorEntitys.clear();
Michael Ott's avatar
Michael Ott committed
295
		_templateSensorBases.clear();
296
		_templateSensorGroups.clear();
297
		_templateSensorEntitys.clear();
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
308
309
	/**
	 * 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.
	 */
310
	void setGlobalSettings(const pluginSettings_t& pluginSettings) final {
311
312
		_mqttPrefix = pluginSettings.mqttPrefix;
		_cacheInterval = pluginSettings.cacheInterval;
313
314

		derivedSetGlobalSettings(pluginSettings);
315
316
	}

Micha Mueller's avatar
Micha Mueller committed
317
318
319
320
321
	/**
	 * Get all sensor groups
	 *
	 * @return	Vector containing pointers to all sensor groups of this plugin
	 */
322
	std::vector<SGroupPtr>& getSensorGroups() final {
323
324
325
		return _sensorGroupInterfaces;
	}

326
protected:
327
	void storeSensorGroup(SG_Ptr sGroup) {
328
329
		_sensorGroups.push_back(sGroup);
		_sensorGroupInterfaces.push_back(sGroup);
Micha Mueller's avatar
Micha Mueller committed
330
331
	}

332
333
	/**
	 * Non-virtual interface method for class-internal use only.
334
	 * Reads and sets the common base values of a sensor base (currently none),
335
336
	 * then calls the corresponding derived function to read plugin specific
	 * values.
337
	 *
338
	 * @param sBase		The sensor base for which to set the values
339
340
341
342
	 * @param config	A boost property (sub-)tree containing the sensor values
	 *
	 * @return	True on success, false otherwise
	 */
Micha Mueller's avatar
Micha Mueller committed
343
	bool readSensorBase(SBase& sBase, CFG_VAL config) {
344
		sBase.setCacheInterval(_cacheInterval);
Michael Ott's avatar
Michael Ott committed
345
346
347
348
349
350
351
352
353
354
		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 = _templateSensorBases.find(def.get().data());
			if(it != _templateSensorBases.end()) {
				sBase = *(it->second);
				sBase.setName(config.data());
			} else {
355
				LOG(warning) << "Template " << _baseName << "\" " << def.get().data() << "\" not found! Using standard values.";
Michael Ott's avatar
Michael Ott committed
356
357
358
			}
		}
		
359
360
361
		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
			if (boost::iequals(val.first, "mqttsuffix")) {
				sBase.setMqtt(val.second.data());
362
			} else if (boost::iequals(val.first, "skipConstVal")) {
363
364
365
366
367
				if (val.second.data() == "on") {
					sBase.setSkipConstVal(true);
				} else {
					sBase.setSkipConstVal(false);
				}
368
			}
369
370
371
			else if (boost::iequals(val.first, "delta")) {
				sBase.setDelta( val.second.data() == "on" );
			}
372
373
374
		}
		sensorBase(sBase, config);
		return true;
375
376
377
378
	}

	/**
	 * Non-virtual interface method for class-internal use only.
379
380
	 * Reads and sets the common base values of a sensor group, then calls
	 * the corresponding derived function to read in plugin specific values.
381
	 *
382
	 * @param sGroup	The sensor group for which to set the values
383
384
385
386
	 * @param config	A boost property (sub-)tree containing the sensor values
	 *
	 * @return	True on success, false otherwise
	 */
Micha Mueller's avatar
Micha Mueller committed
387
	bool readSensorGroup(SGroup& sGroup, CFG_VAL config) {
388
389
390
391
392
393
394
395
		//first check if default group is given
		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()) {
396
397
				sGroup = *(it->second);
				sGroup.setGroupName(config.data());
398
			} else {
399
				LOG(warning) << "Template " << _groupName << "\" " << def.get().data() << "\" not found! Using standard values.";
400
401
402
			}
		}

403
		//read in values inherited from SensorGroupInterface
404
		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
405
			if (boost::iequals(val.first, "interval")) {
406
				sGroup.setInterval(stoull(val.second.data()));
407
			} else if (boost::iequals(val.first, "minValues")) {
408
				sGroup.setMinValues(stoull(val.second.data()));
409
410
			} else if (boost::iequals(val.first, "mqttPart")) {
				sGroup.setMqttPart(val.second.data());
411
412
413
414
415
416
			} else if (boost::iequals(val.first, "sync")) {
				if (val.second.data() == "off") {
					sGroup.setSync(false);
				} else {
					sGroup.setSync(true);
				}
417
418
			} else if (boost::iequals(val.first, _baseName)) {
				LOG(debug) << "  " << _baseName << " " << val.second.data();
419
				SB_Ptr sensor = std::make_shared<SBase>(val.second.data());
420
				if (readSensorBase(*sensor, val.second)) {
421
					sGroup.pushBackSensor(sensor);
422
				} else {
423
					LOG(warning) << _baseName << " " << sGroup.getGroupName() << "::" << sensor->getName() << " could not be read! Omitting";
424
				}
425
426
427
			}
		}

428
429
430
		//TODO keep debug logging for config?
//		LOG(debug) << "  Interval : " << sGroup.getInterval();
//		LOG(debug) << "  minValues: " << sGroup.getMinValues();
431

432
433
		sensorGroup(sGroup, config);
		return true;
434
435
	}

436
437
438
439
440
441
442
	/**
	 * 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
443
444
	 * @param isTemplate	Indicate if sEntity is a template. If so, also store
	 * 						the corresponding sGroups in the template map
445
446
447
	 *
	 * @return	True on success, false otherwise
	 */
Micha Mueller's avatar
Micha Mueller committed
448
	bool readSensorEntity(SEntity& sEntity, CFG_VAL config, bool isTemplate) {
449
450
451
452
453
454
455
		//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()) {
456
				sEntity = *(it->second);
Micha Mueller's avatar
Micha Mueller committed
457
458
				for(auto g : _templateSensorGroups) {
					if (isEntityOfGroup(*(it->second), *(g.second))) {
459
						SG_Ptr group = std::make_shared<SGroup>(*(g.second));
Micha Mueller's avatar
Micha Mueller committed
460
						setEntityForGroup(sEntity, *group);
461
						storeSensorGroup(group);
Micha Mueller's avatar
Micha Mueller committed
462
463
					}
				}
464
465
466
467
			} else {
				LOG(warning) << "Template " << _entityName << "\"" << def.get().data() << "\" not found! Using standard values.";
			}
		}
Micha Mueller's avatar
Micha Mueller committed
468

469
		sensorEntity(sEntity, config);
Micha Mueller's avatar
Micha Mueller committed
470
471
472
473

		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
			if (boost::iequals(val.first, _groupName)) {
				LOG(debug) << "  " << _groupName << " " << val.second.data();
474
				if (!val.second.empty()) {
475
476
477
478
					if (isTemplate) {
						SGroup* group = new SGroup(val.second.data());
						if(readSensorGroup(*group, val.second)) {
							setEntityForGroup(sEntity, *group);
479
480
481
482
483
484
							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 {
485
486
							LOG(warning) << _groupName << " " << group->getGroupName() << " could not be read! Omitting";
							delete group;
Micha Mueller's avatar
Micha Mueller committed
487
488
						}
					} else {
489
490
491
492
493
494
495
						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";
						}
496
497
498
499
500
					}
				}
			} else if (boost::iequals(val.first, "single_" + _baseName)) {
				LOG(debug) << "Single " << _baseName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
501
502
503
504
505
506
					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());
507
508
509
510
511
512
513
514
515
516
517
518
							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 {
519
520
521
522
523
524
525
526
527
							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;
528
529
							//perhaps one sensor is already present because it was copied from the template group
							if (group->getSensors().size() != 0) {
530
531
532
533
534
535
536
537
538
								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";
									}
539
								} else {
540
									LOG(warning) << "Single " << _baseName << " " << val.second.data() << " had a type mismatch when casting! Omitting";
541
542
								}
							} else {
543
								sensor = std::make_shared<SBase>(val.second.data());
544
545
546
547
548
549
550
								if (readSensorBase(*sensor, val.second)) {
									group->pushBackSensor(sensor);
									storeSensorGroup(group);
								} else {
									LOG(warning) << "Single " << _baseName << " " << val.second.data() << " could not be read! Omitting";
								}
							}
551
552
						} else {
							LOG(warning) << "Single " << _baseName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
553
						}
Micha Mueller's avatar
Micha Mueller committed
554
555
556
557
558
					}
				}
			}
		}

559
		if(!isTemplate) {
560
			for(const auto& g : _sensorGroups) {
561
562
563
				if(isEntityOfGroup(sEntity, *g)) {
					finalizeGroup(*g);
				}
Micha Mueller's avatar
Micha Mueller committed
564
565
			}
		}
566
		return true;
567
568
	}

Micha Mueller's avatar
Micha Mueller committed
569
	bool readGlobal(CFG_VAL config) {
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
		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("/");
					}
					LOG(debug) << "  Using own MQTT-Prefix " << _mqttPrefix;
				} else if (boost::iequals(global.first, "cacheInterval")) {
					_cacheInterval = stoul(global.second.data());
					LOG(debug) << "  Using own caching interval " << _cacheInterval << " [s]";
					_cacheInterval *= 1000;
				}
			}
585
			global(config.get_child("global"));
586
		}
587
		return true;
588
589
	}

590
	/**
591
	 * Virtual interface method, responsible for setting global values specifically
592
593
594
595
	 * for its plugin.
	 *
	 * @param pluginSettings	The struct with global default plugin settings
	 */
596
597
598
	virtual void derivedSetGlobalSettings(const pluginSettings_t& pluginSettings) {
		//Overwrite if necessary
	}
599
600
601

	/**
	 * Pure virtual interface method, responsible for reading plugin-specific sensor
602
	 * base values.
603
	 *
Micha Mueller's avatar
Micha Mueller committed
604
	 * @param s			The sensor base for which to set the values
605
606
	 * @param config	A boost property (sub-)tree containing the sensor values
	 */
Micha Mueller's avatar
Micha Mueller committed
607
	virtual void sensorBase(SBase& s, CFG_VAL config) = 0;
608
609
610
611
612

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

618
619
620
621
	/**
	 * Virtual interface method, responsible for reading plugin-specific sensor
	 * entity values.
	 *
Micha Mueller's avatar
Micha Mueller committed
622
	 * @param s			The sensor entity for which to set the values
623
624
	 * @param config	A boost property (sub-)tree containing the entity values
	 */
Micha Mueller's avatar
Micha Mueller committed
625
	virtual void sensorEntity(SEntity& s, CFG_VAL config) {
626
627
628
		//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";
	}
629

Micha Mueller's avatar
Micha Mueller committed
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
	/**
	 * 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";
	}

668
669
670
671
672
	/**
	 * 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
673
	virtual void global(CFG_VAL config) {}
674

Alessio Netti's avatar
Alessio Netti committed
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
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
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
	/**
	 * Increases by a certain value the input MQTT hex topic.
	 *
	 * Example: a mqtt="AAB7" and val=5 produce "AAC2" as output.
	 *
	 * @param mqtt: the MQTT hex string whose value has to be increased
	 * @param val: the value by which mqtt has to be increased
	 *
	 * @return the increased MQTT string
	 *
	 */
	const std::string increaseMqtt(const std::string& mqtt, int val) {
		unsigned long mqttDigits = stoul(mqtt, 0, 16);
		mqttDigits += val;
		std::stringstream stream;
		stream << std::setfill ('0') << std::setw(mqtt.length()) << std::uppercase << std::hex << mqttDigits;
		return stream.str();
	}

	/**
	 * Formats a numerical CPU core ID into a hex string of specified length.
	 *
	 * Example: a mqttPart="xx" and val=11 produce "0B" as output.
	 *
	 * @param mqttPart: a template MQTT string, defines the length of the final string
	 * @param val: the value of the CPU core ID
	 *
	 * @return the hex string representation of the input CPU core ID
	 *
	 */
	const std::string formatMqttCPU(const std::string& mqttPart, unsigned int val) {
		std::stringstream stream;
		stream << std::setfill ('0') << std::setw(mqttPart.length()) << std::uppercase << std::hex << val;
		return stream.str();
	}

	/**
	 * 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;
	}

778
779
	std::string		_entityName;
	std::string		_groupName;
780
	std::string		_baseName;
781
782
783
784

	std::string 	_cfgPath;
	std::string		_mqttPrefix;
	unsigned int	_cacheInterval;
785
786
	std::vector<SGroupPtr> 	_sensorGroupInterfaces;
	std::vector<SG_Ptr>		_sensorGroups;
787
	std::vector<SEntity*>	_sensorEntitys;
Michael Ott's avatar
Michael Ott committed
788
	sBaseMap_t		_templateSensorBases;
789
790
	sGroupMap_t		_templateSensorGroups;
	sEntityMap_t	_templateSensorEntitys;
791
792
};

793
#endif /* SRC_CONFIGURATORTEMPLATE_H_ */