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

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

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

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

29
#define ADD						BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config)
30
#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
31
#define SETTING(name)				if (boost::iequals(val.first, name))
32

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

42
protected:
Michael Ott's avatar
Michael Ott committed
43
	typedef std::map<std::string, SBase*> sBaseMap_t;
44
45
	typedef std::map<std::string, SGroup*> sGroupMap_t;
	typedef std::map<std::string, SEntity*> sEntityMap_t;
46

47
48
49
	using SB_Ptr = std::shared_ptr<SBase>;
	using SG_Ptr = std::shared_ptr<SGroup>;

50
public:
51
	ConfiguratorTemplate() :
52
53
54
		_entityName("INVALID"),
		_groupName("INVALID"),
		_baseName("INVALID"),
55
56
57
		_cfgPath(""),
		_mqttPrefix(""),
		_cacheInterval(900000) {}
58

59
60
	ConfiguratorTemplate(const ConfiguratorTemplate&) = delete;

61
	virtual ~ConfiguratorTemplate() {
62
63
64
		for (auto e : _sensorEntitys) {
			delete e;
		}
Michael Ott's avatar
Michael Ott committed
65
66
67
		for (auto tb : _templateSensorBases) {
			delete tb.second;
		}
68
		for (auto tg : _templateSensorGroups) {
69
			delete tg.second;
70
71
		}
		for (auto te : _templateSensorEntitys) {
72
			delete te.second;
73
		}
74
		_sensorGroupInterfaces.clear();
75
76
		_sensorGroups.clear();
		_sensorEntitys.clear();
Michael Ott's avatar
Michael Ott committed
77
		_templateSensorBases.clear();
78
		_templateSensorGroups.clear();
79
		_templateSensorEntitys.clear();
80
	}
81

82
83
	ConfiguratorTemplate& operator=(const ConfiguratorTemplate&) = delete;

Micha Mueller's avatar
Micha Mueller committed
84
85
	/**
	 * Read in the given configuration
86
	 *
87
88
	 * Overwriting this method is only required if a custom logic is really necessary!
	 *
Micha Mueller's avatar
Micha Mueller committed
89
	 * @param	cfgPath Path to the config-file
90
91
	 *
	 * @return	True on success, false otherwise
Micha Mueller's avatar
Micha Mueller committed
92
	 */
93
	bool readConfig(std::string cfgPath) {
94
		_cfgPath = cfgPath;
95
96
97
98
99

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

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

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

	/**
	 * Clear internal storage and read in the configuration again.
249
250
	 *
	 * @return	True on success, false otherwise
251
	 */
252
	bool reReadConfig() final {
Micha Mueller's avatar
Micha Mueller committed
253
254
		//bring everything to a halt
		for(auto g : _sensorGroups) {
255
			g->stop();
Micha Mueller's avatar
Micha Mueller committed
256
257
258
259
260
261
		}

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

263
264
265
266
		//clean up sensors/groups/entitys and templates
		for(auto e : _sensorEntitys) {
			delete e;
		}
Michael Ott's avatar
Michael Ott committed
267
268
269
		for (auto tb : _templateSensorBases) {
			delete tb.second;
		}
270
		for (auto tg : _templateSensorGroups) {
271
			delete tg.second;
272
273
		}
		for (auto te : _templateSensorEntitys) {
274
			delete te.second;
275
		}
276
		_sensorGroupInterfaces.clear();
Micha Mueller's avatar
Micha Mueller committed
277
		_sensorGroups.clear();
278
		_sensorEntitys.clear();
Michael Ott's avatar
Michael Ott committed
279
		_templateSensorBases.clear();
280
		_templateSensorGroups.clear();
281
		_templateSensorEntitys.clear();
282

Micha Mueller's avatar
Micha Mueller committed
283
		//back to the very beginning
284
		return readConfig(_cfgPath);
285
	}
286

287
288
289
290
291
292
293
	/**
	 * 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.
	 */
294
	void setGlobalSettings(const pluginSettings_t& pluginSettings) final {
295
296
		_mqttPrefix = pluginSettings.mqttPrefix;
		_cacheInterval = pluginSettings.cacheInterval;
297
298

		derivedSetGlobalSettings(pluginSettings);
299
300
	}

Micha Mueller's avatar
Micha Mueller committed
301
302
303
304
305
	/**
	 * Get all sensor groups
	 *
	 * @return	Vector containing pointers to all sensor groups of this plugin
	 */
306
	std::vector<SGroupPtr>& getSensorGroups() final {
307
308
309
		return _sensorGroupInterfaces;
	}

310
protected:
311
	void storeSensorGroup(SG_Ptr sGroup) {
312
313
		_sensorGroups.push_back(sGroup);
		_sensorGroupInterfaces.push_back(sGroup);
Micha Mueller's avatar
Micha Mueller committed
314
315
	}

316
317
	/**
	 * Non-virtual interface method for class-internal use only.
318
	 * Reads and sets the common base values of a sensor base (currently none),
319
320
	 * then calls the corresponding derived function to read plugin specific
	 * values.
321
	 *
322
	 * @param sBase		The sensor base for which to set the values
323
324
325
326
	 * @param config	A boost property (sub-)tree containing the sensor values
	 *
	 * @return	True on success, false otherwise
	 */
Micha Mueller's avatar
Micha Mueller committed
327
	bool readSensorBase(SBase& sBase, CFG_VAL config) {
Michael Ott's avatar
Michael Ott committed
328
329
330
331
332
333
334
335
336
337
338
339
340
341
		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 {
				LOG(warning) << "Template " << _groupName << "\"" << def.get().data() << "\" not found! Using standard values.";
			}
		}
		
342
343
344
345
		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
			if (boost::iequals(val.first, "mqttsuffix")) {
				sBase.setMqtt(val.second.data());
			}
346
347
348
			else if (boost::iequals(val.first, "delta")) {
				sBase.setDelta( val.second.data() == "on" );
			}
349
350
351
		}
		sensorBase(sBase, config);
		return true;
352
353
354
355
	}

	/**
	 * Non-virtual interface method for class-internal use only.
356
357
	 * Reads and sets the common base values of a sensor group, then calls
	 * the corresponding derived function to read in plugin specific values.
358
	 *
359
	 * @param sGroup	The sensor group for which to set the values
360
361
362
363
	 * @param config	A boost property (sub-)tree containing the sensor values
	 *
	 * @return	True on success, false otherwise
	 */
Micha Mueller's avatar
Micha Mueller committed
364
	bool readSensorGroup(SGroup& sGroup, CFG_VAL config) {
365
		sGroup.setCacheInterval(_cacheInterval);
366
367
368
369
370
371
372
373
		//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()) {
374
375
				sGroup = *(it->second);
				sGroup.setGroupName(config.data());
376
377
378
379
380
			} else {
				LOG(warning) << "Template " << _groupName << "\"" << def.get().data() << "\" not found! Using standard values.";
			}
		}

381
		//read in values inherited from SensorGroupInterface
382
		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
383
			if (boost::iequals(val.first, "interval")) {
384
				sGroup.setInterval(stoull(val.second.data()));
385
			} else if (boost::iequals(val.first, "minValues")) {
386
				sGroup.setMinValues(stoull(val.second.data()));
387
388
			} else if (boost::iequals(val.first, "mqttPart")) {
				sGroup.setMqttPart(val.second.data());
389
390
391
392
393
394
			} else if (boost::iequals(val.first, "sync")) {
				if (val.second.data() == "off") {
					sGroup.setSync(false);
				} else {
					sGroup.setSync(true);
				}
395
396
			} else if (boost::iequals(val.first, _baseName)) {
				LOG(debug) << "  " << _baseName << " " << val.second.data();
397
				SB_Ptr sensor = std::make_shared<SBase>(val.second.data());
398
				if (readSensorBase(*sensor, val.second)) {
399
					sGroup.pushBackSensor(sensor);
400
				} else {
401
					LOG(warning) << _baseName << " " << sGroup.getGroupName() << "::" << sensor->getName() << " could not be read! Omitting";
402
				}
403
404
405
			}
		}

406
407
408
		//TODO keep debug logging for config?
//		LOG(debug) << "  Interval : " << sGroup.getInterval();
//		LOG(debug) << "  minValues: " << sGroup.getMinValues();
409

410
411
		sensorGroup(sGroup, config);
		return true;
412
413
	}

414
415
416
417
418
419
420
	/**
	 * 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
421
422
	 * @param isTemplate	Indicate if sEntity is a template. If so, also store
	 * 						the corresponding sGroups in the template map
423
424
425
	 *
	 * @return	True on success, false otherwise
	 */
Micha Mueller's avatar
Micha Mueller committed
426
	bool readSensorEntity(SEntity& sEntity, CFG_VAL config, bool isTemplate) {
427
428
429
430
431
432
433
		//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()) {
434
				sEntity = *(it->second);
Micha Mueller's avatar
Micha Mueller committed
435
436
				for(auto g : _templateSensorGroups) {
					if (isEntityOfGroup(*(it->second), *(g.second))) {
437
						SG_Ptr group = std::make_shared<SGroup>(*(g.second));
Micha Mueller's avatar
Micha Mueller committed
438
						setEntityForGroup(sEntity, *group);
439
						storeSensorGroup(group);
Micha Mueller's avatar
Micha Mueller committed
440
441
					}
				}
442
443
444
445
			} else {
				LOG(warning) << "Template " << _entityName << "\"" << def.get().data() << "\" not found! Using standard values.";
			}
		}
Micha Mueller's avatar
Micha Mueller committed
446

447
		sensorEntity(sEntity, config);
Micha Mueller's avatar
Micha Mueller committed
448
449
450
451

		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
			if (boost::iequals(val.first, _groupName)) {
				LOG(debug) << "  " << _groupName << " " << val.second.data();
452
				if (!val.second.empty()) {
453
454
455
456
					if (isTemplate) {
						SGroup* group = new SGroup(val.second.data());
						if(readSensorGroup(*group, val.second)) {
							setEntityForGroup(sEntity, *group);
457
458
459
460
461
462
							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 {
463
464
							LOG(warning) << _groupName << " " << group->getGroupName() << " could not be read! Omitting";
							delete group;
Micha Mueller's avatar
Micha Mueller committed
465
466
						}
					} else {
467
468
469
470
471
472
473
						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";
						}
474
475
476
477
478
					}
				}
			} else if (boost::iequals(val.first, "single_" + _baseName)) {
				LOG(debug) << "Single " << _baseName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
479
480
481
482
483
484
					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());
485
486
487
488
489
490
491
492
493
494
495
496
							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 {
497
498
499
500
501
502
503
504
505
							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;
506
507
							//perhaps one sensor is already present because it was copied from the template group
							if (group->getSensors().size() != 0) {
508
509
510
511
512
513
514
515
516
								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";
									}
517
								} else {
518
									LOG(warning) << "Single " << _baseName << " " << val.second.data() << " had a type mismatch when casting! Omitting";
519
520
								}
							} else {
521
								sensor = std::make_shared<SBase>(val.second.data());
522
523
524
525
526
527
528
								if (readSensorBase(*sensor, val.second)) {
									group->pushBackSensor(sensor);
									storeSensorGroup(group);
								} else {
									LOG(warning) << "Single " << _baseName << " " << val.second.data() << " could not be read! Omitting";
								}
							}
529
530
						} else {
							LOG(warning) << "Single " << _baseName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
531
						}
Micha Mueller's avatar
Micha Mueller committed
532
533
534
535
536
					}
				}
			}
		}

537
		if(!isTemplate) {
538
			for(const auto& g : _sensorGroups) {
539
540
541
				if(isEntityOfGroup(sEntity, *g)) {
					finalizeGroup(*g);
				}
Micha Mueller's avatar
Micha Mueller committed
542
543
			}
		}
544
		return true;
545
546
	}

Micha Mueller's avatar
Micha Mueller committed
547
	bool readGlobal(CFG_VAL config) {
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
		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;
				}
			}
563
			global(config.get_child("global"));
564
		}
565
		return true;
566
567
	}

568
	/**
569
	 * Virtual interface method, responsible for setting global values specifically
570
571
572
573
	 * for its plugin.
	 *
	 * @param pluginSettings	The struct with global default plugin settings
	 */
574
575
576
	virtual void derivedSetGlobalSettings(const pluginSettings_t& pluginSettings) {
		//Overwrite if necessary
	}
577
578
579

	/**
	 * Pure virtual interface method, responsible for reading plugin-specific sensor
580
	 * base values.
581
	 *
Micha Mueller's avatar
Micha Mueller committed
582
	 * @param s			The sensor base for which to set the values
583
584
	 * @param config	A boost property (sub-)tree containing the sensor values
	 */
Micha Mueller's avatar
Micha Mueller committed
585
	virtual void sensorBase(SBase& s, CFG_VAL config) = 0;
586
587
588
589
590

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

596
597
598
599
	/**
	 * Virtual interface method, responsible for reading plugin-specific sensor
	 * entity values.
	 *
Micha Mueller's avatar
Micha Mueller committed
600
	 * @param s			The sensor entity for which to set the values
601
602
	 * @param config	A boost property (sub-)tree containing the entity values
	 */
Micha Mueller's avatar
Micha Mueller committed
603
	virtual void sensorEntity(SEntity& s, CFG_VAL config) {
604
605
606
		//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";
	}
607

Micha Mueller's avatar
Micha Mueller committed
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
	/**
	 * 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";
	}

646
647
648
649
650
	/**
	 * 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
651
	virtual void global(CFG_VAL config) {}
652

Alessio Netti's avatar
Alessio Netti committed
653
654
655
656
657
658
659
660
661
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
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
	/**
	 * 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;
	}

756
757
	std::string		_entityName;
	std::string		_groupName;
758
	std::string		_baseName;
759
760
761
762

	std::string 	_cfgPath;
	std::string		_mqttPrefix;
	unsigned int	_cacheInterval;
763
764
	std::vector<SGroupPtr> 	_sensorGroupInterfaces;
	std::vector<SG_Ptr>		_sensorGroups;
765
	std::vector<SEntity*>	_sensorEntitys;
Michael Ott's avatar
Michael Ott committed
766
	sBaseMap_t		_templateSensorBases;
767
768
	sGroupMap_t		_templateSensorGroups;
	sEntityMap_t	_templateSensorEntitys;
769
770
};

771
#endif /* SRC_CONFIGURATORTEMPLATE_H_ */