ConfiguratorTemplate.h 13.1 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 "ConfiguratorInterface.h"
12

13
14
#include "SensorBase.h"
#include "SensorGroupTemplate.h"
15

16
17
18
19
20
21
#include <map>

#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/info_parser.hpp>
22

23
24
//#define STRCMP(node,str) boost::iequals(node.first,str) //DEPRECATED
#define CFG_TREE	boost::property_tree::iptree&
25

26
27
#define ADD						BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config)
#define ATTRIBUTE(name,setter)		if (boost::iequals(val.first, name)) { s.setter(val.second.data()); }
28

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

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

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

51
52
	ConfiguratorTemplate(const ConfiguratorTemplate&) = delete;

53
	virtual ~ConfiguratorTemplate() {
54
55
		for (auto g : _sensorGroups) {
			delete g;
56
		}
57
58
59
		for (auto e : _sensorEntitys) {
			delete e;
		}
60
		for (auto tg : _templateSensorGroups) {
61
			delete tg.second;
62
63
		}
		for (auto te : _templateSensorEntitys) {
64
			delete te.second;
65
		}
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
	 *
Micha Mueller's avatar
Micha Mueller committed
77
	 * @param	cfgPath Path to the config-file
78
79
	 *
	 * @return	True on success, false otherwise
Micha Mueller's avatar
Micha Mueller committed
80
	 */
81
	bool readConfig(std::string cfgPath) final {
82
		_cfgPath = cfgPath;
83
84
85
86
87

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

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

90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
		//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?
			//template entity
			if (boost::iequals(val.first, "template_" + _entityName)) {
				LOG(debug) << "Template " << _entityName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					SEntity* entity = new SEntity(val.second.data());
					if (readSensorEntity(*entity, val.second)) {
						auto ret = _templateSensorEntitys.insert(std::pair<std::string, SEntity*>(entity->getName(), entity));
						if(!ret.second) {
							LOG(warning) << "Template " << _entityName << " " << entity->getName() << " already exists! Omitting...";
						}
					} else {
						LOG(warning) << "Template " << _entityName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
					}
				}
			//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)) {
						auto ret = _templateSensorGroups.insert(std::pair<std::string, SGroup*>(group->getName(), group));
						if(!ret.second) {
							LOG(warning) << "Template " << _groupName << " " << group->getName() << " already exists! Omitting...";
116
						}
117
118
119
120
121
122
123
124
125
126
127
128
129
					} else {
						LOG(warning) << "Template " << _groupName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
					}
				}
			//entity
			} else if (boost::iequals(val.first, _entityName)) {
				LOG(debug) << _entityName << " \"" << val.second.data() << "\"";
				if (!val.second.empty()) {
					SEntity* entity = new SEntity(val.second.data());
					if (readSensorEntity(*entity, val.second)) {
						_sensorEntitys.push_back(entity);
					} else {
						LOG(warning) << _entityName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
130
					}
131
132
133
134
135
136
137
138
139
140
141
142
				}
			//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)) {
						_sensorGroups.push_back(group);
					} else {
						LOG(warning) << _groupName << " \"" << val.second.data() << "\" has bad values! Ignoring...";
					}
				}
143
144
			}
		}
145
		return true;
146
147
148
149
	}

	/**
	 * Clear internal storage and read in the configuration again.
150
151
	 *
	 * @return	True on success, false otherwise
152
	 */
153
	bool reReadConfig() final {
Micha Mueller's avatar
Micha Mueller committed
154
155
		//bring everything to a halt
		for(auto g : _sensorGroups) {
156
			g->stop();
Micha Mueller's avatar
Micha Mueller committed
157
158
159
160
161
162
		}

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

164
		//clean up sensors/groups/entitys and templates
Micha Mueller's avatar
Micha Mueller committed
165
166
167
		for(auto g : _sensorGroups) {
			delete g;
		}
168
169
170
		for(auto e : _sensorEntitys) {
			delete e;
		}
171
		for (auto tg : _templateSensorGroups) {
172
			delete tg.second;
173
174
		}
		for (auto te : _templateSensorEntitys) {
175
			delete te.second;
176
		}
Micha Mueller's avatar
Micha Mueller committed
177
		_sensorGroups.clear();
178
		_sensorEntitys.clear();
179
		_templateSensorGroups.clear();
180
		_templateSensorEntitys.clear();
181

Micha Mueller's avatar
Micha Mueller committed
182
		//back to the very beginning
183
		return readConfig(_cfgPath);
184
	}
185

186
187
188
189
190
191
192
	/**
	 * 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.
	 */
193
	void setGlobalSettings(const pluginSettings_t& pluginSettings) final {
194
195
		_mqttPrefix = pluginSettings.mqttPrefix;
		_cacheInterval = pluginSettings.cacheInterval;
196
197

		derivedSetGlobalSettings(pluginSettings);
198
199
	}

Micha Mueller's avatar
Micha Mueller committed
200
201
202
203
204
	/**
	 * Get all sensor groups
	 *
	 * @return	Vector containing pointers to all sensor groups of this plugin
	 */
205
	std::vector<SensorGroupInterface*>& getSensorGroups() {
Micha Mueller's avatar
Micha Mueller committed
206
207
208
		return _sensorGroups;
	}

209
protected:
210
211
	/**
	 * Non-virtual interface method for class-internal use only.
212
	 * Reads and sets the common base values of a sensor base (currently none),
213
214
	 * then calls the corresponding derived function to read plugin specific
	 * values.
215
	 *
216
	 * @param sBase		The sensor base for which to set the values
217
218
219
220
	 * @param config	A boost property (sub-)tree containing the sensor values
	 *
	 * @return	True on success, false otherwise
	 */
221
	bool readSensorBase(SBase& sBase, boost::property_tree::iptree& config) {
222
223
224
225
226
227
228
229
230
		//TODO set full mqtt-topic if reading config finished!
		//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;
231
232
233
234
	}

	/**
	 * Non-virtual interface method for class-internal use only.
235
236
	 * Reads and sets the common base values of a sensor group, then calls
	 * the corresponding derived function to read in plugin specific values.
237
	 *
238
	 * @param sGroup	The sensor group 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
	 */
243
244
	bool readSensorGroup(SGroup& sGroup, boost::property_tree::iptree& config) {
		sGroup.setCacheInterval(_cacheInterval);
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
		//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()) {
				sGroup = it->second;
				sGroup->setName(config.data());
			} else {
				LOG(warning) << "Template " << _groupName << "\"" << def.get().data() << "\" not found! Using standard values.";
			}
		}

260
		//read in values inherited from SensorGroupInterface
261
		BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
262
			if (boost::iequals(val.first, "interval")) {
263
				sGroup.setInterval(stoull(val.second.data()));
264
			} else if (boost::iequals(val.first, "minValues")) {
265
				sGroup.setMinValues(stoull(val.second.data()));
266
267
268
			} else if (boost::iequals(val.first, "mqttPart")) {
				sGroup.setMqttPart(val.second.data());
			} else if (boost::iequals(val.first, "sensor")) {
269
270
				SBase* sensor = new SBase(val.second.data());
				if (readSensorBase(*sensor, val.second)) {
271
					sGroup.pushBackSensor(sensor);
272
				} else {
273
					LOG(warning) << _baseName << " " << sGroup.getGroupName() << "::" << sensor->getName() << " could not be read! Omitting";
274
				}
275
276
277
			}
		}

278
279
280
		//TODO keep debug logging for config?
//		LOG(debug) << "  Interval : " << sGroup.getInterval();
//		LOG(debug) << "  minValues: " << sGroup.getMinValues();
281

282
283
		sensorGroup(sGroup, config);
		return true;
284
285
	}

286
287
288
289
290
291
292
293
294
295
	/**
	 * 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
	 *
	 * @return	True on success, false otherwise
	 */
296
	bool readSensorEntity(SEntity& sEntity, boost::property_tree::iptree& config) {
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
		//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()) {
				sEntity = it->second;
				sEntity->setName(config.data());
			} else {
				LOG(warning) << "Template " << _entityName << "\"" << def.get().data() << "\" not found! Using standard values.";
			}
		}
		sensorEntity(sEntity, config);
		return true;
312
313
	}

314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
	bool readGlobal(boost::property_tree::iptree& config) {
		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;
				}
			}
		}
331
332
		global(config);
		return true;
333
334
	}

335
	/**
336
	 * Virtual interface method, responsible for setting global values specifically
337
338
339
340
	 * for its plugin.
	 *
	 * @param pluginSettings	The struct with global default plugin settings
	 */
341
342
343
	virtual void derivedSetGlobalSettings(const pluginSettings_t& pluginSettings) {
		//Overwrite if necessary
	}
344
345
346

	/**
	 * Pure virtual interface method, responsible for reading plugin-specific sensor
347
	 * base values.
348
	 *
349
350
351
	 * @param sBase		The sensor base for which to set the values
	 * @param config	A boost property (sub-)tree containing the sensor values
	 */
352
	virtual void sensorBase(SBase& s, boost::property_tree::iptree& config) = 0;
353
354
355
356
357
358

	/**
	 * Pure virtual interface method, responsible for reading plugin-specific sensor
	 * group values.
	 *
	 * @param sGroup	The sensor group for which to set the values
359
	 * @param config	A boost property (sub-)tree containing the group values
360
	 */
361
	virtual void sensorGroup(SGroup& s, boost::property_tree::iptree& config) = 0;
362

363
364
365
366
367
368
369
370
371
372
373
	/**
	 * Virtual interface method, responsible for reading plugin-specific sensor
	 * entity values.
	 *
	 * @param sEntity	The sensor entity for which to set the values
	 * @param config	A boost property (sub-)tree containing the entity values
	 */
	virtual void sensorEntity(SEntity& s, boost::property_tree::iptree& config) {
		//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";
	}
374

375
376
377
378
379
380
	/**
	 * Virtual interface method, responsible for reading plugin-specific global values.
	 *
	 * @param config	A boost property (sub-)tree containing the global values
	 */
	virtual void global(boost::property_tree::iptree& config) {}
381

382
383
	std::string		_entityName;
	std::string		_groupName;
384
	std::string		_baseName;
385
386
387
388

	std::string 	_cfgPath;
	std::string		_mqttPrefix;
	unsigned int	_cacheInterval;
389
	std::vector<SensorGroupInterface*> _sensorGroups;
390
	std::vector<SEntity*> _sensorEntitys;
391
392
	sGroupMap_t		_templateSensorGroups;
	sEntityMap_t	_templateSensorEntitys;
393
394
};

395
#endif /* SRC_CONFIGURATORTEMPLATE_H_ */