ConfiguratorTemplate.h 16.4 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
12
13
14
15
16
#include <map>

#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/info_parser.hpp>
17
18
19
#include "ConfiguratorInterface.h"
#include "SensorBase.h"
#include "SensorGroupTemplate.h"
20

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

24
#define ADD						BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config)
25
#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
26
#define SETTING(name)				if (boost::iequals(val.first, name))
27

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

37
protected:
38
39
	typedef std::map<std::string, SGroup*> sGroupMap_t;
	typedef std::map<std::string, SEntity*> sEntityMap_t;
40

41
public:
42
	ConfiguratorTemplate() :
43
44
45
		_entityName("INVALID"),
		_groupName("INVALID"),
		_baseName("INVALID"),
46
47
48
		_cfgPath(""),
		_mqttPrefix(""),
		_cacheInterval(900000) {}
49

50
51
	ConfiguratorTemplate(const ConfiguratorTemplate&) = delete;

52
	virtual ~ConfiguratorTemplate() {
53
54
		for (auto g : _sensorGroups) {
			delete g;
55
		}
56
57
58
		for (auto e : _sensorEntitys) {
			delete e;
		}
59
		for (auto tg : _templateSensorGroups) {
60
			delete tg.second;
61
62
		}
		for (auto te : _templateSensorEntitys) {
63
			delete te.second;
64
		}
65
		_sensorGroupInterfaces.clear();
66
67
		_sensorGroups.clear();
		_sensorEntitys.clear();
68
		_templateSensorGroups.clear();
69
		_templateSensorEntitys.clear();
70
	}
71

72
73
	ConfiguratorTemplate& operator=(const ConfiguratorTemplate&) = delete;

Micha Mueller's avatar
Micha Mueller committed
74
75
	/**
	 * Read in the given configuration
76
	 *
77
78
	 * Overwriting this method is only required if a custom logic is really necessary!
	 *
Micha Mueller's avatar
Micha Mueller committed
79
	 * @param	cfgPath Path to the config-file
80
81
	 *
	 * @return	True on success, false otherwise
Micha Mueller's avatar
Micha Mueller committed
82
	 */
83
	bool readConfig(std::string cfgPath) {
84
		_cfgPath = cfgPath;
85
86
87
88
89

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

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

92
93
94
		//read groups and templates for groups. If present also entity/-template stuff
		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, cfg) {
			//TODO allow for template sensors?
95
			//TODO allow single sensors for convenience?
96
97
98
99
			//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
100
101
102
					//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)) {
103
						auto ret = _templateSensorEntitys.insert(std::pair<std::string, SEntity*>(val.second.data(), entity));
104
						if(!ret.second) {
105
							LOG(warning) << "Template " << _entityName << " " << val.second.data() << " already exists! Omitting...";
Micha Mueller's avatar
Micha Mueller committed
106
							delete entity;
107
108
109
						}
					} else {
						LOG(warning) << "Template " << _entityName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
Micha Mueller's avatar
Micha Mueller committed
110
						delete entity;
111
112
113
114
115
116
117
118
					}
				}
			//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)) {
119
						auto ret = _templateSensorGroups.insert(std::pair<std::string, SGroup*>(val.second.data(), group));
120
						if(!ret.second) {
121
							LOG(warning) << "Template " << _groupName << " " << val.second.data() << " already exists! Omitting...";
Micha Mueller's avatar
Micha Mueller committed
122
							delete group;
123
						}
124
125
					} else {
						LOG(warning) << "Template " << _groupName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
Micha Mueller's avatar
Micha Mueller committed
126
						delete group;
127
128
129
130
131
132
					}
				}
			//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
133
134
					SEntity* entity = new SEntity();
					if (readSensorEntity(*entity, val.second, false)) {
135
136
137
						_sensorEntitys.push_back(entity);
					} else {
						LOG(warning) << _entityName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
Micha Mueller's avatar
Micha Mueller committed
138
						delete entity;
139
					}
140
141
142
143
144
145
146
				}
			//group
			} else if (boost::iequals(val.first, _groupName)) {
				LOG(debug) << _groupName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					SGroup* group = new SGroup(val.second.data());
					if (readSensorGroup(*group, val.second)) {
147
						storeSensorGroup(group);
148
149
					} else {
						LOG(warning) << _groupName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
Micha Mueller's avatar
Micha Mueller committed
150
						delete group;
151
152
					}
				}
153
154
			}
		}
155
156
157
158
159
160
		//read of config finished. Now we build the mqtt-topic for every sensor
		for(auto g : _sensorGroups) {
			for(auto s : g->getSensors()) {
				s->setMqtt(_mqttPrefix + g->getMqttPart() + s->getMqtt());
			}
		}
161
		return true;
162
163
164
165
	}

	/**
	 * Clear internal storage and read in the configuration again.
166
167
	 *
	 * @return	True on success, false otherwise
168
	 */
169
	bool reReadConfig() final {
Micha Mueller's avatar
Micha Mueller committed
170
171
		//bring everything to a halt
		for(auto g : _sensorGroups) {
172
			g->stop();
Micha Mueller's avatar
Micha Mueller committed
173
174
175
176
177
178
		}

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

180
		//clean up sensors/groups/entitys and templates
Micha Mueller's avatar
Micha Mueller committed
181
182
183
		for(auto g : _sensorGroups) {
			delete g;
		}
184
185
186
		for(auto e : _sensorEntitys) {
			delete e;
		}
187
		for (auto tg : _templateSensorGroups) {
188
			delete tg.second;
189
190
		}
		for (auto te : _templateSensorEntitys) {
191
			delete te.second;
192
		}
193
		_sensorGroupInterfaces.clear();
Micha Mueller's avatar
Micha Mueller committed
194
		_sensorGroups.clear();
195
		_sensorEntitys.clear();
196
		_templateSensorGroups.clear();
197
		_templateSensorEntitys.clear();
198

Micha Mueller's avatar
Micha Mueller committed
199
		//back to the very beginning
200
		return readConfig(_cfgPath);
201
	}
202

203
204
205
206
207
208
209
	/**
	 * 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.
	 */
210
	void setGlobalSettings(const pluginSettings_t& pluginSettings) final {
211
212
		_mqttPrefix = pluginSettings.mqttPrefix;
		_cacheInterval = pluginSettings.cacheInterval;
213
214

		derivedSetGlobalSettings(pluginSettings);
215
216
	}

Micha Mueller's avatar
Micha Mueller committed
217
218
219
220
221
	/**
	 * Get all sensor groups
	 *
	 * @return	Vector containing pointers to all sensor groups of this plugin
	 */
Micha Mueller's avatar
Micha Mueller committed
222
	std::vector<SensorGroupInterface*>& getSensorGroups() final {
223
224
225
		return _sensorGroupInterfaces;
	}

226
protected:
227
228
229
	void storeSensorGroup(SGroup* sGroup) {
		_sensorGroups.push_back(sGroup);
		_sensorGroupInterfaces.push_back(sGroup);
Micha Mueller's avatar
Micha Mueller committed
230
231
	}

232
233
	/**
	 * Non-virtual interface method for class-internal use only.
234
	 * Reads and sets the common base values of a sensor base (currently none),
235
236
	 * then calls the corresponding derived function to read plugin specific
	 * values.
237
	 *
238
	 * @param sBase		The sensor base for which to set the values
239
240
241
242
	 * @param config	A boost property (sub-)tree containing the sensor values
	 *
	 * @return	True on success, false otherwise
	 */
Micha Mueller's avatar
Micha Mueller committed
243
	bool readSensorBase(SBase& sBase, CFG_VAL config) {
244
245
246
247
248
249
250
251
		//TODO default templates useful?
		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
			if (boost::iequals(val.first, "mqttsuffix")) {
				sBase.setMqtt(val.second.data());
			}
		}
		sensorBase(sBase, config);
		return true;
252
253
254
255
	}

	/**
	 * Non-virtual interface method for class-internal use only.
256
257
	 * Reads and sets the common base values of a sensor group, then calls
	 * the corresponding derived function to read in plugin specific values.
258
	 *
259
	 * @param sGroup	The sensor group for which to set the values
260
261
262
263
	 * @param config	A boost property (sub-)tree containing the sensor values
	 *
	 * @return	True on success, false otherwise
	 */
Micha Mueller's avatar
Micha Mueller committed
264
	bool readSensorGroup(SGroup& sGroup, CFG_VAL config) {
265
		sGroup.setCacheInterval(_cacheInterval);
266
267
268
269
270
271
272
273
		//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()) {
274
275
				sGroup = *(it->second);
				sGroup.setGroupName(config.data());
276
277
278
279
280
			} else {
				LOG(warning) << "Template " << _groupName << "\"" << def.get().data() << "\" not found! Using standard values.";
			}
		}

281
		//read in values inherited from SensorGroupInterface
282
		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
283
			if (boost::iequals(val.first, "interval")) {
284
				sGroup.setInterval(stoull(val.second.data()));
285
			} else if (boost::iequals(val.first, "minValues")) {
286
				sGroup.setMinValues(stoull(val.second.data()));
287
288
			} else if (boost::iequals(val.first, "mqttPart")) {
				sGroup.setMqttPart(val.second.data());
289
290
			} else if (boost::iequals(val.first, _baseName)) {
				LOG(debug) << "  " << _baseName << " " << val.second.data();
291
292
				SBase* sensor = new SBase(val.second.data());
				if (readSensorBase(*sensor, val.second)) {
293
					sGroup.pushBackSensor(sensor);
294
				} else {
295
					LOG(warning) << _baseName << " " << sGroup.getGroupName() << "::" << sensor->getName() << " could not be read! Omitting";
Micha Mueller's avatar
Micha Mueller committed
296
					delete sensor;
297
				}
298
299
300
			}
		}

301
302
303
		//TODO keep debug logging for config?
//		LOG(debug) << "  Interval : " << sGroup.getInterval();
//		LOG(debug) << "  minValues: " << sGroup.getMinValues();
304

305
306
		sensorGroup(sGroup, config);
		return true;
307
308
	}

309
310
311
312
313
314
315
	/**
	 * 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
316
317
	 * @param isTemplate	Indicate if sEntity is a template. If so, also store
	 * 						the corresponding sGroups in the template map
318
319
320
	 *
	 * @return	True on success, false otherwise
	 */
Micha Mueller's avatar
Micha Mueller committed
321
	bool readSensorEntity(SEntity& sEntity, CFG_VAL config, bool isTemplate) {
322
323
324
325
326
327
328
		//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()) {
329
				sEntity = *(it->second);
Micha Mueller's avatar
Micha Mueller committed
330
331
332
333
				for(auto g : _templateSensorGroups) {
					if (isEntityOfGroup(*(it->second), *(g.second))) {
						SGroup* group = new SGroup(*(g.second));
						setEntityForGroup(sEntity, *group);
334
						storeSensorGroup(group);
Micha Mueller's avatar
Micha Mueller committed
335
336
					}
				}
337
338
339
340
			} else {
				LOG(warning) << "Template " << _entityName << "\"" << def.get().data() << "\" not found! Using standard values.";
			}
		}
Micha Mueller's avatar
Micha Mueller committed
341

342
		sensorEntity(sEntity, config);
Micha Mueller's avatar
Micha Mueller committed
343
344
345
346
347
348
349
350
351
352
353

		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
			if (boost::iequals(val.first, _groupName)) {
				LOG(debug) << "  " << _groupName << " " << val.second.data();
				SGroup* group = new SGroup(val.second.data());
				if(readSensorGroup(*group, val.second)) {
					setEntityForGroup(sEntity, *group);
					if (isTemplate) {
						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...";
Micha Mueller's avatar
Micha Mueller committed
354
							delete group;
Micha Mueller's avatar
Micha Mueller committed
355
356
						}
					} else {
357
						storeSensorGroup(group);
Micha Mueller's avatar
Micha Mueller committed
358
359
360
					}
				} else {
					LOG(warning) << _groupName << " " << group->getGroupName() << " could not be read! Omitting";
Micha Mueller's avatar
Micha Mueller committed
361
					delete group;
Micha Mueller's avatar
Micha Mueller committed
362
363
364
365
				}
			}
		}

366
367
368
369
370
		if(!isTemplate) {
			for(auto g : _sensorGroups) {
				if(isEntityOfGroup(sEntity, *g)) {
					finalizeGroup(*g);
				}
Micha Mueller's avatar
Micha Mueller committed
371
372
			}
		}
373
		return true;
374
375
	}

Micha Mueller's avatar
Micha Mueller committed
376
	bool readGlobal(CFG_VAL config) {
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
		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;
				}
			}
392
			global(config.get_child("global"));
393
		}
394
		return true;
395
396
	}

397
	/**
398
	 * Virtual interface method, responsible for setting global values specifically
399
400
401
402
	 * for its plugin.
	 *
	 * @param pluginSettings	The struct with global default plugin settings
	 */
403
404
405
	virtual void derivedSetGlobalSettings(const pluginSettings_t& pluginSettings) {
		//Overwrite if necessary
	}
406
407
408

	/**
	 * Pure virtual interface method, responsible for reading plugin-specific sensor
409
	 * base values.
410
	 *
Micha Mueller's avatar
Micha Mueller committed
411
	 * @param s			The sensor base for which to set the values
412
413
	 * @param config	A boost property (sub-)tree containing the sensor values
	 */
Micha Mueller's avatar
Micha Mueller committed
414
	virtual void sensorBase(SBase& s, CFG_VAL config) = 0;
415
416
417
418
419

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

425
426
427
428
	/**
	 * Virtual interface method, responsible for reading plugin-specific sensor
	 * entity values.
	 *
Micha Mueller's avatar
Micha Mueller committed
429
	 * @param s			The sensor entity for which to set the values
430
431
	 * @param config	A boost property (sub-)tree containing the entity values
	 */
Micha Mueller's avatar
Micha Mueller committed
432
	virtual void sensorEntity(SEntity& s, CFG_VAL config) {
433
434
435
		//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";
	}
436

Micha Mueller's avatar
Micha Mueller committed
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
	/**
	 * 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";
	}

475
476
477
478
479
	/**
	 * 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
480
	virtual void global(CFG_VAL config) {}
481

482
483
	std::string		_entityName;
	std::string		_groupName;
484
	std::string		_baseName;
485
486
487
488

	std::string 	_cfgPath;
	std::string		_mqttPrefix;
	unsigned int	_cacheInterval;
489
490
491
	std::vector<SensorGroupInterface*> _sensorGroupInterfaces;
	std::vector<SGroup*>	_sensorGroups;
	std::vector<SEntity*>	_sensorEntitys;
492
493
	sGroupMap_t		_templateSensorGroups;
	sEntityMap_t	_templateSensorEntitys;
494
495
};

496
#endif /* SRC_CONFIGURATORTEMPLATE_H_ */