ConfiguratorTemplate.h 40.6 KB
Newer Older
1
2
//================================================================================
// Name        : ConfiguratorTemplate.h
3
// Author      : Micha Mueller, Carla Guillen
Micha Müller's avatar
Micha Müller committed
4
// Contact     : info@dcdb.it
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Copyright   : Leibniz Supercomputing Centre
// Description : Interface template for plugin configurators.
//================================================================================

//================================================================================
// This file is part of DCDB (DataCenter DataBase)
// Copyright (C) 2018-2019 Leibniz Supercomputing Centre
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//================================================================================
27

28
29
#ifndef SRC_CONFIGURATORTEMPLATE_H_
#define SRC_CONFIGURATORTEMPLATE_H_
30

31
#include <map>
Alessio Netti's avatar
Alessio Netti committed
32
#include <set>
33

34
#include "ConfiguratorInterface.h"
35
#include "EntityInterface.h"
36
37
38
39
40
41
#include "SensorGroupTemplate.h"
#include "sensorbase.h"
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <boost/property_tree/ptree.hpp>
42

43
#include <algorithm>
44
#include <iomanip>
Alessio Netti's avatar
Alessio Netti committed
45
46
47
#include <iostream>
#include <sstream>

Micha Mueller's avatar
Micha Mueller committed
48
/**
49
50
 * @brief Interface template for plugin configurator implementations with
 *        entities.
51
 *
52
 * @details There is a partial template specialization for SEntity = nullptr_t,
53
54
55
56
57
58
59
 *          i.e. for plugins without entities, see
 *          ConfiguratorTemplate<SBase, SGroup, nullptr_t>. Common code is moved
 *          to ConfiguratorInterface if possible. If not, common code has to be
 *          duplicated and maintained twice here and in
 *          ConfiguratorTemplate<SBase, SGroup, nullptr_t>. However, having two
 *          templates for cases with and without entities allows for simplified
 *          derived plugin configurators.
60
 *
61
 * @ingroup pusherplugins
Micha Mueller's avatar
Micha Mueller committed
62
 */
63
template <class SBase, class SGroup, class SEntity = nullptr_t>
64
class ConfiguratorTemplate : public ConfiguratorInterface {
65
	//the template shall only be instantiated for classes which derive from SensorBase/SensorGroup
66
	static_assert(std::is_base_of<SensorBase, SBase>::value, "SBase must derive from SensorBase!");
67
	static_assert(std::is_base_of<SensorGroupInterface, SGroup>::value, "SGroup must derive from SensorGroupInterface!");
68
	static_assert(std::is_base_of<EntityInterface, SEntity>::value, "SEntity must derive from EntityInterface!");
69

70
      protected:
71
	//TODO use smart pointers
72
73
74
	typedef std::map<std::string, SBase *>   sBaseMap_t;
	typedef std::map<std::string, SGroup *>  sGroupMap_t;
	typedef std::map<std::string, SEntity *> sEntityMap_t;
75

76
77
	using SB_Ptr = std::shared_ptr<SBase>;
	using SG_Ptr = std::shared_ptr<SGroup>;
78
79
80
81
82
83
84

      public:
	ConfiguratorTemplate()
	    : ConfiguratorInterface(),
	      _entityName("INVALID"),
	      _groupName("INVALID"),
	      _baseName("INVALID") {}
85

86
	virtual ~ConfiguratorTemplate() {
87
88
89
		for (auto e : _sensorEntitys) {
			delete e;
		}
Michael Ott's avatar
Michael Ott committed
90
91
92
		for (auto tb : _templateSensorBases) {
			delete tb.second;
		}
93
		for (auto tg : _templateSensorGroups) {
94
			delete tg.second;
95
96
		}
		for (auto te : _templateSensorEntitys) {
97
			delete te.second;
98
		}
99
100
		_sensorGroups.clear();
		_sensorEntitys.clear();
Michael Ott's avatar
Michael Ott committed
101
		_templateSensorBases.clear();
102
		_templateSensorGroups.clear();
103
		_templateSensorEntitys.clear();
104
	}
105

Micha Mueller's avatar
Micha Mueller committed
106
	/**
107
	 * @brief Read in the given configuration
108
	 *
109
	 * @details Overwriting this method is only required if a custom logic is really necessary!
110
	 *
Micha Mueller's avatar
Micha Mueller committed
111
	 * @param	cfgPath Path to the config-file
112
113
	 *
	 * @return	True on success, false otherwise
Micha Mueller's avatar
Micha Mueller committed
114
	 */
115
	bool readConfig(std::string cfgPath) {
116
		_cfgPath = cfgPath;
117
118
119
120
121

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

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

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

	/**
285
	 * @brief Clear internal storage and return plugin in unconfigured state.
286
	 */
Alessio Netti's avatar
Alessio Netti committed
287
	void clearConfig() final {
288
		ConfiguratorInterface::clearConfig();
289
		// Bring everything to a halt
290
		for (auto g : _sensorGroups)
291
			g->stop();
292
		// Wait for stop
293
		for (auto g : _sensorGroups)
294
			g->wait();
Micha Mueller's avatar
Micha Mueller committed
295

296
		//clean up sensors/groups/entitys and templates
297
		for (auto e : _sensorEntitys) {
298
299
			delete e;
		}
Michael Ott's avatar
Michael Ott committed
300
301
302
		for (auto tb : _templateSensorBases) {
			delete tb.second;
		}
303
		for (auto tg : _templateSensorGroups) {
304
			delete tg.second;
305
306
		}
		for (auto te : _templateSensorEntitys) {
307
			delete te.second;
308
		}
Micha Mueller's avatar
Micha Mueller committed
309
		_sensorGroups.clear();
310
		_sensorEntitys.clear();
Michael Ott's avatar
Michael Ott committed
311
		_templateSensorBases.clear();
312
		_templateSensorGroups.clear();
313
		_templateSensorEntitys.clear();
Alessio Netti's avatar
Alessio Netti committed
314
315
316
	}

	/**
317
318
319
320
     * @brief Print configuration.
     *
     * @param ll Log severity level to be used from logger.
     */
321
	void printConfig(LOG_LEVEL ll) final {
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
		ConfiguratorInterface::printConfig(ll);

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

		LOG_VAR(ll) << "    " << _entityName << "s:";
		if (_sensorEntitys.size() != 0) {
			for (auto e : _sensorEntitys) {
				e->printConfig(ll, 8);
				LOG_VAR(ll) << "            Sensor Groups:";
				for (auto g : _sensorGroups) {
					if (g->getEntity() == e) {
						g->printConfig(ll, 16);
					}
				}
			}
		} else {
			LOG_VAR(ll) << "            No " << _entityName << "s present!";
340
		}
341
		/*
342
343
344
345
        LOG_VAR(ll) << "    " << _groupName << "s:";
        for(auto g : _sensorGroups) {
            g->printConfig(ll);
        }
346
 */
347
348
	}

349
      protected:
350
351
	///@name Template-internal use only
	///@{
352
	/**
353
	 * @brief Read common values of a sensorbase.
354
	 *
355
356
357
358
359
360
361
	 * @details Reads and sets the common base values of a sensor base
	 *          (currently none), then calls sensorBase() to read plugin
	 *          specific values.
	 *
	 * @param sBase		 The sensor base for which to set the values.
	 * @param config	 A boost property (sub-)tree containing the values.
	 * @param isTemplate Are we parsing a template or a regular sensor?
362
363
364
	 *
	 * @return	True on success, false otherwise
	 */
365
	bool readSensorBase(SBase &sBase, CFG_VAL config, bool isTemplate = false) {
366
		sBase.setCacheInterval(_cacheInterval);
367
		if (!isTemplate) {
368
369
			boost::optional<boost::property_tree::iptree &> def = config.get_child_optional("default");
			if (def) {
370
				//we copy all values from default
371
372
				LOG(debug) << "  Using \"" << def.get().data() << "\" as default.";
				auto it = _templateSensorBases.find(def.get().data());
373
				if (it != _templateSensorBases.end()) {
374
375
376
					sBase = *(it->second);
					sBase.setName(config.data());
				} else {
377
					LOG(warning) << "Template " << _baseName << "\" " << def.get().data() << "\" not found! Using standard values.";
378
				}
Michael Ott's avatar
Michael Ott committed
379
380
			}
		}
381
382

		BOOST_FOREACH (boost::property_tree::iptree::value_type &val, config) {
383
384
			if (boost::iequals(val.first, "mqttsuffix")) {
				sBase.setMqtt(val.second.data());
385
			} else if (boost::iequals(val.first, "skipConstVal")) {
Alessio Netti's avatar
Alessio Netti committed
386
				sBase.setSkipConstVal(to_bool(val.second.data()));
387
			} else if (boost::iequals(val.first, "delta")) {
Alessio Netti's avatar
Alessio Netti committed
388
				sBase.setDelta(to_bool(val.second.data()));
389
			} else if (boost::iequals(val.first, "subSampling")) {
Alessio Netti's avatar
Alessio Netti committed
390
				sBase.setSubsampling(std::stoi(val.second.data()));
391
392
			} else if (boost::iequals(val.first, "publish")) {
				sBase.setPublish(to_bool(val.second.data()));
393
394
			} else if (boost::iequals(val.first, "metadata")) {
				SensorMetadata sm;
Alessio Netti's avatar
Alessio Netti committed
395
396
				if(sBase.getMetadata())
					sm = *sBase.getMetadata();
397
398
399
				try {
					sm.parsePTREE(val.second);
					sBase.setMetadata(sm);
400
				} catch (const std::exception &e) {
401
402
					LOG(error) << "  Metadata parsing failed for sensor " << sBase.getName() << "!" << std::endl;
				}
403
			}
404
405
406
		}
		sensorBase(sBase, config);
		return true;
407
408
409
	}

	/**
410
411
412
413
414
415
416
417
418
419
420
421
     * @brief Read common values of a sensorGroup.
     *
     * @details Reads and sets the common base values of a sensor group
     *          (currently none), then calls sensorGroup() to read plugin
     *          specific values.
     *
     * @param sGroup     The sensor group for which to set the values.
     * @param config     A boost property (sub-)tree containing the values.
     * @param isTemplate Are we parsing a template or a regular group?
     *
     * @return  True on success, false otherwise
     */
422
	bool readSensorGroup(SGroup &sGroup, CFG_VAL config, bool isTemplate = false) {
423
424
		//first check if default group is given, unless we are reading a template already
		if (!isTemplate) {
425
426
			boost::optional<boost::property_tree::iptree &> def = config.get_child_optional("default");
			if (def) {
427
428
429
430
				//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());
431
				if (it != _templateSensorGroups.end()) {
432
433
434
					sGroup = *(it->second);
					sGroup.setGroupName(config.data());
				} else {
435
					LOG(warning) << "Template " << _groupName << "\" "
436
						     << def.get().data() << "\" not found! Using standard values.";
437
				}
438
439
440
			}
		}

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

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

471
	/**
472
473
474
475
476
477
478
479
480
481
482
483
484
     * @brief Read common values of a sensor entity.
     *
     * @details Reads and sets the common base values of a sensor entity
     *          (currently none), then calls sensorEntity() to read plugin
     *          specific values.
     *
     * @param sEntity    The aggregating entity for which to set the values.
     * @param config     A boost property (sub-)tree containing the values.
     * @param isTemplate Indicate if sEntity is a template. If so, also store
     *                   the corresponding sGroups in the template map.
     *
     * @return  True on success, false otherwise
     */
485
	bool readSensorEntity(SEntity &sEntity, CFG_VAL config, bool isTemplate = false) {
486
		//first check if default entity is given
487
488
		boost::optional<boost::property_tree::iptree &> def = config.get_child_optional("default");
		if (def) {
489
490
491
			//we copy all values from default
			LOG(debug) << "  Using \"" << def.get().data() << "\" as default.";
			auto it = _templateSensorEntitys.find(def.get().data());
492
			if (it != _templateSensorEntitys.end()) {
493
				sEntity = *(it->second);
494
				sEntity.setName(config.data());
495
				for (auto g : _templateSensorGroups) {
496
					if (it->second == g.second->getEntity()) {
497
						SG_Ptr group = std::make_shared<SGroup>(*(g.second));
498
						group->setEntity(&sEntity);
499
500
501
502
503
						if (group->getGroupName().size() > 0) {
							group->setGroupName(sEntity.getName() + "::" + group->getGroupName());
						} else {
							group->setGroupName(sEntity.getName());
						}
504
						storeSensorGroup(group);
Micha Mueller's avatar
Micha Mueller committed
505
506
					}
				}
507
			} else {
508
				LOG(warning) << "Template " << _entityName << "\""
509
					     << def.get().data() << "\" not found! Using standard values.";
510
511
			}
		}
Micha Mueller's avatar
Micha Mueller committed
512

513
514
		//read in values inherited from EntityInterface
		BOOST_FOREACH (boost::property_tree::iptree::value_type &val, config) {
515
516
517
			if (boost::iequals(val.first, "mqttPart")) {
				sEntity.setMqttPart(val.second.data());
			} else if (boost::iequals(val.first, "disabled")) {
518
				sEntity.setDisabled(to_bool(val.second.data()));
519
520
			}
		}
Micha Müller's avatar
Micha Müller committed
521

522
		sensorEntity(sEntity, config);
Micha Mueller's avatar
Micha Mueller committed
523

524
		BOOST_FOREACH (boost::property_tree::iptree::value_type &val, config) {
Micha Mueller's avatar
Micha Mueller committed
525
526
			if (boost::iequals(val.first, _groupName)) {
				LOG(debug) << "  " << _groupName << " " << val.second.data();
527
				if (!val.second.empty()) {
528
					if (isTemplate) {
529
530
531
532
533
						SGroup *group = new SGroup(val.second.data());
						if (readSensorGroup(*group, val.second)) {
							group->setEntity(&sEntity);
							auto ret = _templateSensorGroups.insert(std::pair<std::string, SGroup *>(sEntity.getName() + "::" + group->getGroupName(), group));
							if (!ret.second) {
534
								LOG(warning) << "Template " << _groupName << " "
535
									     << sEntity.getName() + "::" + group->getGroupName() << " already exists! Omitting...";
536
537
538
								delete group;
							}
						} else {
539
							LOG(warning) << _groupName << " " << group->getGroupName()
540
								     << " could not be read! Omitting";
541
							delete group;
Micha Mueller's avatar
Micha Mueller committed
542
543
						}
					} else {
544
						SG_Ptr group = std::make_shared<SGroup>(val.second.data());
545
546
547
548
549
550
551
552
						if (readSensorGroup(*group, val.second)) {
							group->setEntity(&sEntity);
							if (group->getGroupName().size() > 0) {
								group->setGroupName(sEntity.getName() + "::" + group->getGroupName());
							} else {
								group->setGroupName(sEntity.getName());
							}
							storeSensorGroup(group);
553
						} else {
554
							LOG(warning) << _groupName << " " << group->getGroupName()
555
								     << " could not be read! Omitting";
556
						}
557
558
559
560
561
					}
				}
			} else if (boost::iequals(val.first, "single_" + _baseName)) {
				LOG(debug) << "Single " << _baseName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
562
					if (isTemplate) {
563
						SGroup *group = new SGroup(val.second.data());
564
565
						//group which consists of only one sensor
						if (readSensorGroup(*group, val.second)) {
566
							group->setEntity(&sEntity);
567
							SB_Ptr sensor = std::make_shared<SBase>(val.second.data());
568
569
							if (readSensorBase(*sensor, val.second)) {
								group->pushBackSensor(sensor);
570
571
								auto ret = _templateSensorGroups.insert(std::pair<std::string, SGroup *>(val.second.data(), group));
								if (!ret.second) {
572
									LOG(warning) << "Template single " << _baseName << " "
573
										     << val.second.data() << " already exists! Omitting...";
574
575
576
									delete group;
								}
							} else {
577
								LOG(warning) << "Template single " << _baseName << " "
578
									     << val.second.data() << " could not be read! Omitting";
579
580
581
								delete group;
							}
						} else {
582
							LOG(warning) << "Single " << _baseName << " \""
583
								     << val.second.data() << "\" has bad values! Ignoring...";
584
585
586
587
588
589
							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)) {
590
							group->setEntity(&sEntity);
591
							SB_Ptr sensor;
592
							//perhaps one sensor is already present because it was copied from the template group
593
							if (group->acquireSensors().size() != 0) {
594
								group->releaseSensors();
595
596
								sensor = std::dynamic_pointer_cast<SBase>(group->acquireSensors()[0]);
								group->releaseSensors();
597
598
599
600
601
602
								//check if cast was successful (sensor != nullptr)
								if (sensor) {
									sensor->setName(val.second.data());
									if (readSensorBase(*sensor, val.second)) {
										storeSensorGroup(group);
									} else {
603
										LOG(warning) << "Single " << _baseName << " "
604
											     << val.second.data() << " could not be read! Omitting";
605
									}
606
								} else {
607
									LOG(warning) << "Single " << _baseName << " "
608
										     << val.second.data() << " had a type mismatch when casting! Omitting";
609
610
								}
							} else {
611
								group->releaseSensors();
612
								sensor = std::make_shared<SBase>(val.second.data());
613
614
615
616
								if (readSensorBase(*sensor, val.second)) {
									group->pushBackSensor(sensor);
									storeSensorGroup(group);
								} else {
617
									LOG(warning) << "Single " << _baseName << " "
618
										     << val.second.data() << " could not be read! Omitting";
619
620
								}
							}
621
						} else {
622
							LOG(warning) << "Single " << _baseName << " \""
623
								     << val.second.data() << "\" has bad values! Ignoring...";
624
						}
Micha Mueller's avatar
Micha Mueller committed
625
626
627
628
629
					}
				}
			}
		}

630
		return true;
631
	}
632
	///@}
633

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

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

652
	/**
653
	 * Method responsible for reading plugin-specific sensor entity values.
654
	 *
Micha Mueller's avatar
Micha Mueller committed
655
	 * @param s			The sensor entity for which to set the values
656
657
	 * @param config	A boost property (sub-)tree containing the entity values
	 */
658
	virtual void sensorEntity(SEntity &s, CFG_VAL config) = 0;
659
	///@}
660

661
662
	///@name Utility
	///@{
663
	/**
664
665
666
667
     * @brief Store a sensor group internally.
     *
     * @param sGroup Group to store.
     */
668
669
670
671
	void storeSensorGroup(SG_Ptr sGroup) {
		_sensorGroups.push_back(sGroup);
		_sensorGroupInterfaces.push_back(sGroup);
	}
Micha Mueller's avatar
Micha Mueller committed
672
673

	/**
674
	 * @brief Adjusts the names of the sensors in generated groups.
Micha Mueller's avatar
Micha Mueller committed
675
	 *
676
	 * @return True if successful, false otherwise.
Alessio Netti's avatar
Alessio Netti committed
677
	 */
678
679
	bool constructSensorTopics() {
		// Sensor names are adjusted according to the respective MQTT topics
680
681
		for (auto &g : _sensorGroups) {
			for (auto &s : g->acquireSensors()) {
682
				s->setMqtt(MQTTChecker::formatTopic(_mqttPrefix) +
683
684
685
					   MQTTChecker::formatTopic(g->getEntity()->getMqttPart()) +
					   MQTTChecker::formatTopic(g->getMqttPart()) +
					   MQTTChecker::formatTopic(s->getMqtt()));
686
				s->setName(s->getMqtt());
687
688
				SensorMetadata *sm = s->getMetadata();
				if (sm) {
689
					sm->publicName = s->getMqtt();
690
					sm->pattern = s->getMqtt();
691
					sm->isVirtual = false;
Alessio Netti's avatar
Alessio Netti committed
692
693
					if(sm->interval==0)
						sm->interval = (unsigned long long)g->getInterval() * 1000000;
694
				}
695
			}
696
697
			g->releaseSensors();
		}
Alessio Netti's avatar
Alessio Netti committed
698
		return true;
Alessio Netti's avatar
Alessio Netti committed
699
	}
700
	///@}
Alessio Netti's avatar
Alessio Netti committed
701

702
703
704
	std::string _entityName;
	std::string _groupName;
	std::string _baseName;
705

706
707
708
709
710
	std::vector<SG_Ptr>    _sensorGroups;
	std::vector<SEntity *> _sensorEntitys;
	sBaseMap_t             _templateSensorBases;
	sGroupMap_t            _templateSensorGroups;
	sEntityMap_t           _templateSensorEntitys;
711
712
};

713
714
715
716
717
718
////////////////////////////////////////////////////////////////////////////////
// Partial template specialization for SEntity = nullptr_t
////////////////////////////////////////////////////////////////////////////////

/**
 * @brief Partial interface template specialization for plugin configurator
719
 *        implementations without entities.
720
 *
721
722
723
724
725
726
 * @details There is a general template for plugins with entities, see
 *          ConfiguratorTemplate. Common code is moved to ConfiguratorInterface
 *          if possible. If not, common code has to be duplicated and maintained
 *          twice here and in ConfiguratorTemplate. However, having two
 *          templates for cases with and without entities allows for simplified
 *          derived plugin configurators.
727
728
729
730
731
 *
 * @ingroup pusherplugins
 */
template <class SBase, class SGroup>
class ConfiguratorTemplate<SBase, SGroup, nullptr_t> : public ConfiguratorInterface {
732
733
734
	//the template shall only be instantiated for classes which derive from SensorBase/SensorGroup
	static_assert(std::is_base_of<SensorBase, SBase>::value, "SBase must derive from SensorBase!");
	static_assert(std::is_base_of<SensorGroupInterface, SGroup>::value, "SGroup must derive from SensorGroupInterface!");
735

736
737
738
739
      protected:
	//TODO use smart pointers
	typedef std::map<std::string, SBase *>  sBaseMap_t;
	typedef std::map<std::string, SGroup *> sGroupMap_t;
740

741
742
	using SB_Ptr = std::shared_ptr<SBase>;
	using SG_Ptr = std::shared_ptr<SGroup>;
743

744
745
746
747
748
      public:
	ConfiguratorTemplate()
	    : ConfiguratorInterface(),
	      _groupName("INVALID"),
	      _baseName("INVALID") {}
749

750
751
752
753
754
755
756
757
758
759
760
	virtual ~ConfiguratorTemplate() {
		for (auto tb : _templateSensorBases) {
			delete tb.second;
		}
		for (auto tg : _templateSensorGroups) {
			delete tg.second;
		}
		_sensorGroups.clear();
		_templateSensorBases.clear();
		_templateSensorGroups.clear();
	}
761

762
	/**
763
764
765
766
767
768
769
770
     * @brief Read in the given configuration
     *
     * @details Overwriting this method is only required if a custom logic is really necessary!
     *
     * @param   cfgPath Path to the config-file
     *
     * @return  True on success, false otherwise
     */
771
772
	bool readConfig(std::string cfgPath) {
		_cfgPath = cfgPath;
773

774
775
		boost::property_tree::iptree cfg;
		boost::property_tree::read_info(cfgPath, cfg);
776

777
778
		//read global variables (if present overwrite those from global.conf)
		readGlobal(cfg);
779

780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860