ConfiguratorTemplate.h 27.6 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
			} else if (boost::iequals(val.first, "delta")) {
369
				sBase.setDelta( val.second.data() == "on" );
370
371
372
373
			} else if (boost::iequals(val.first, "sink")) {
				sBase.setSinkPath( val.second.data() );
			} else if (boost::iequals(val.first, "subSampling")) {
				sBase.setSubsampling( std::stoul(val.second.data()) );
374
			}
375
376
377
		}
		sensorBase(sBase, config);
		return true;
378
379
380
381
	}

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

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

431
432
433
		//TODO keep debug logging for config?
//		LOG(debug) << "  Interval : " << sGroup.getInterval();
//		LOG(debug) << "  minValues: " << sGroup.getMinValues();
434

435
436
		sensorGroup(sGroup, config);
		return true;
437
438
	}

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

472
		sensorEntity(sEntity, config);
Micha Mueller's avatar
Micha Mueller committed
473
474
475
476

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

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

Micha Mueller's avatar
Micha Mueller committed
572
	bool readGlobal(CFG_VAL config) {
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
		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;
				}
			}
588
			global(config.get_child("global"));
589
		}
590
		return true;
591
592
	}

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

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

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

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

Micha Mueller's avatar
Micha Mueller committed
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
668
669
670
	/**
	 * 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";
	}

671
672
673
674
675
	/**
	 * 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
676
	virtual void global(CFG_VAL config) {}
677

Alessio Netti's avatar
Alessio Netti committed
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
778
779
780
	/**
	 * 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;
	}

781
782
	std::string		_entityName;
	std::string		_groupName;
783
	std::string		_baseName;
784
785
786
787

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

796
#endif /* SRC_CONFIGURATORTEMPLATE_H_ */