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
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
22
//#define STRCMP(node,str) boost::iequals(node.first,str) //DEPRECATED
#define CFG_TREE	boost::property_tree::iptree&
23

24
25
#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()); }
26

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

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

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

49
50
	ConfiguratorTemplate(const ConfiguratorTemplate&) = delete;

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

70
71
	ConfiguratorTemplate& operator=(const ConfiguratorTemplate&) = delete;

Micha Mueller's avatar
Micha Mueller committed
72
73
	/**
	 * Read in the given configuration
74
	 *
Micha Mueller's avatar
Micha Mueller committed
75
	 * @param	cfgPath Path to the config-file
76
77
	 *
	 * @return	True on success, false otherwise
Micha Mueller's avatar
Micha Mueller committed
78
	 */
79
	bool readConfig(std::string cfgPath) final {
80
		_cfgPath = cfgPath;
81
82
83
84
85

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

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

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
		//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...";
114
						}
115
116
117
118
119
120
121
122
123
124
125
126
127
					} 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...";
128
					}
129
130
131
132
133
134
135
136
137
138
139
140
				}
			//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...";
					}
				}
141
142
			}
		}
143
		return true;
144
145
146
147
	}

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

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

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

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

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

		derivedSetGlobalSettings(pluginSettings);
196
197
	}

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

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

	/**
	 * Non-virtual interface method for class-internal use only.
233
234
	 * Reads and sets the common base values of a sensor group, then calls
	 * the corresponding derived function to read in plugin specific values.
235
	 *
236
	 * @param sGroup	The sensor group for which to set the values
237
238
239
240
	 * @param config	A boost property (sub-)tree containing the sensor values
	 *
	 * @return	True on success, false otherwise
	 */
241
242
	bool readSensorGroup(SGroup& sGroup, boost::property_tree::iptree& config) {
		sGroup.setCacheInterval(_cacheInterval);
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
		//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.";
			}
		}

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

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

280
281
		sensorGroup(sGroup, config);
		return true;
282
283
	}

284
285
286
287
288
289
290
291
292
293
	/**
	 * 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
	 */
294
	bool readSensorEntity(SEntity& sEntity, boost::property_tree::iptree& config) {
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
		//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;
310
311
	}

312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
	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;
				}
			}
		}
329
330
		global(config);
		return true;
331
332
	}

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

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

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

361
362
363
364
365
366
367
368
369
370
371
	/**
	 * 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";
	}
372

373
374
375
376
377
378
	/**
	 * 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) {}
379

380
381
	std::string		_entityName;
	std::string		_groupName;
382
	std::string		_baseName;
383
384
385
386

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

393
#endif /* SRC_CONFIGURATORTEMPLATE_H_ */