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

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

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

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

Micha Mueller's avatar
Micha Mueller committed
187
		//back to the very beginning
188
		return readConfig(_cfgPath);
189
	}
190

191
192
193
194
195
196
197
	/**
	 * 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.
	 */
198
	void setGlobalSettings(const pluginSettings_t& pluginSettings) final {
199
200
		_mqttPrefix = pluginSettings.mqttPrefix;
		_cacheInterval = pluginSettings.cacheInterval;
201
202

		derivedSetGlobalSettings(pluginSettings);
203
204
	}

Micha Mueller's avatar
Micha Mueller committed
205
206
207
208
209
	/**
	 * Get all sensor groups
	 *
	 * @return	Vector containing pointers to all sensor groups of this plugin
	 */
210
	std::vector<SensorGroupInterface*>& getSensorGroups() {
Micha Mueller's avatar
Micha Mueller committed
211
212
213
		return _sensorGroups;
	}

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

	/**
	 * Non-virtual interface method for class-internal use only.
239
240
	 * Reads and sets the common base values of a sensor group, then calls
	 * the corresponding derived function to read in plugin specific values.
241
	 *
242
	 * @param sGroup	The sensor group for which to set the values
243
244
245
246
	 * @param config	A boost property (sub-)tree containing the sensor values
	 *
	 * @return	True on success, false otherwise
	 */
247
248
	bool readSensorGroup(SGroup& sGroup, boost::property_tree::iptree& config) {
		sGroup.setCacheInterval(_cacheInterval);
249
250
251
252
253
254
255
256
		//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()) {
257
258
				sGroup = *(it->second);
				sGroup.setGroupName(config.data());
259
260
261
262
263
			} else {
				LOG(warning) << "Template " << _groupName << "\"" << def.get().data() << "\" not found! Using standard values.";
			}
		}

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

283
284
285
		//TODO keep debug logging for config?
//		LOG(debug) << "  Interval : " << sGroup.getInterval();
//		LOG(debug) << "  minValues: " << sGroup.getMinValues();
286

287
288
		sensorGroup(sGroup, config);
		return true;
289
290
	}

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

319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
	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;
				}
			}
		}
336
337
		global(config);
		return true;
338
339
	}

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

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

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

368
369
370
371
372
373
374
375
376
377
378
	/**
	 * 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";
	}
379

380
381
382
383
384
385
	/**
	 * 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) {}
386

387
388
	std::string		_entityName;
	std::string		_groupName;
389
	std::string		_baseName;
390
391
392
393

	std::string 	_cfgPath;
	std::string		_mqttPrefix;
	unsigned int	_cacheInterval;
394
	std::vector<SensorGroupInterface*> _sensorGroups;
395
	std::vector<SEntity*> _sensorEntitys;
396
397
	sGroupMap_t		_templateSensorGroups;
	sEntityMap_t	_templateSensorEntitys;
398
399
};

400
#endif /* SRC_CONFIGURATORTEMPLATE_H_ */