ConfiguratorTemplate.h 40.3 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
395
396
397
			} else if (boost::iequals(val.first, "metadata")) {
				SensorMetadata sm;
				try {
					sm.parsePTREE(val.second);
					sBase.setMetadata(sm);
398
				} catch (const std::exception &e) {
399
400
					LOG(error) << "  Metadata parsing failed for sensor " << sBase.getName() << "!" << std::endl;
				}
401
			}
402
403
404
		}
		sensorBase(sBase, config);
		return true;
405
406
407
	}

	/**
408
409
410
411
412
413
414
415
416
417
418
419
     * @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
     */
420
	bool readSensorGroup(SGroup &sGroup, CFG_VAL config, bool isTemplate = false) {
421
422
		//first check if default group is given, unless we are reading a template already
		if (!isTemplate) {
423
424
			boost::optional<boost::property_tree::iptree &> def = config.get_child_optional("default");
			if (def) {
425
426
427
428
				//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());
429
				if (it != _templateSensorGroups.end()) {
430
431
432
					sGroup = *(it->second);
					sGroup.setGroupName(config.data());
				} else {
433
					LOG(warning) << "Template " << _groupName << "\" "
434
						     << def.get().data() << "\" not found! Using standard values.";
435
				}
436
437
438
			}
		}

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

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

469
	/**
470
471
472
473
474
475
476
477
478
479
480
481
482
     * @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
     */
483
	bool readSensorEntity(SEntity &sEntity, CFG_VAL config, bool isTemplate = false) {
484
		//first check if default entity is given
485
486
		boost::optional<boost::property_tree::iptree &> def = config.get_child_optional("default");
		if (def) {
487
488
489
			//we copy all values from default
			LOG(debug) << "  Using \"" << def.get().data() << "\" as default.";
			auto it = _templateSensorEntitys.find(def.get().data());
490
			if (it != _templateSensorEntitys.end()) {
491
				sEntity = *(it->second);
492
				sEntity.setName(config.data());
493
				for (auto g : _templateSensorGroups) {
494
					if (it->second == g.second->getEntity()) {
495
						SG_Ptr group = std::make_shared<SGroup>(*(g.second));
496
						group->setEntity(&sEntity);
497
498
499
500
501
						if (group->getGroupName().size() > 0) {
							group->setGroupName(sEntity.getName() + "::" + group->getGroupName());
						} else {
							group->setGroupName(sEntity.getName());
						}
502
						storeSensorGroup(group);
Micha Mueller's avatar
Micha Mueller committed
503
504
					}
				}
505
			} else {
506
				LOG(warning) << "Template " << _entityName << "\""
507
					     << def.get().data() << "\" not found! Using standard values.";
508
509
			}
		}
Micha Mueller's avatar
Micha Mueller committed
510

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

520
		sensorEntity(sEntity, config);
Micha Mueller's avatar
Micha Mueller committed
521

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

628
		return true;
629
	}
630
	///@}
631

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

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

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

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

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

698
699
700
	std::string _entityName;
	std::string _groupName;
	std::string _baseName;
701

702
703
704
705
706
	std::vector<SG_Ptr>    _sensorGroups;
	std::vector<SEntity *> _sensorEntitys;
	sBaseMap_t             _templateSensorBases;
	sGroupMap_t            _templateSensorGroups;
	sEntityMap_t           _templateSensorEntitys;
707
708
};

709
710
711
712
713
714
////////////////////////////////////////////////////////////////////////////////
// Partial template specialization for SEntity = nullptr_t
////////////////////////////////////////////////////////////////////////////////

/**
 * @brief Partial interface template specialization for plugin configurator
715
 *        implementations without entities.
716
 *
717
718
719
720
721
722
 * @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.
723
724
725
726
727
 *
 * @ingroup pusherplugins
 */
template <class SBase, class SGroup>
class ConfiguratorTemplate<SBase, SGroup, nullptr_t> : public ConfiguratorInterface {
728
729
730
	//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!");
731

732
733
734
735
      protected:
	//TODO use smart pointers
	typedef std::map<std::string, SBase *>  sBaseMap_t;
	typedef std::map<std::string, SGroup *> sGroupMap_t;
736

737
738
	using SB_Ptr = std::shared_ptr<SBase>;
	using SG_Ptr = std::shared_ptr<SGroup>;
739

740
741
742
743
744
      public:
	ConfiguratorTemplate()
	    : ConfiguratorInterface(),
	      _groupName("INVALID"),
	      _baseName("INVALID") {}
745

746
747
748
749
750
751
752
753
754
755
756
	virtual ~ConfiguratorTemplate() {
		for (auto tb : _templateSensorBases) {
			delete tb.second;
		}
		for (auto tg : _templateSensorGroups) {
			delete tg.second;
		}
		_sensorGroups.clear();
		_templateSensorBases.clear();
		_templateSensorGroups.clear();
	}
757

758
	/**
759
760
761
762
763
764
765
766
     * @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
     */
767
768
	bool readConfig(std::string cfgPath) {
		_cfgPath = cfgPath;
769

770
771
		boost::property_tree::iptree cfg;
		boost::property_tree::read_info(cfgPath, cfg);
772

773
774
		//read global variables (if present overwrite those from global.conf)
		readGlobal(cfg);
775

776
777
778
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
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
		//read groups and templates for groups. If present also entity/-template stuff
		BOOST_FOREACH (boost::property_tree::iptree::value_type &val, cfg) {
			//template group
			if (boost::iequals(val.first, "template_" + _groupName)) {
				LOG(debug) << "Template " << _groupName << " \"" << val.second.data() << "\"";