mqttchecker.h 5.98 KB
Newer Older
Alessio Netti's avatar
Alessio Netti committed
1
2
3
4
5
6
7
8
9
10
//
// Created by Netti, Alessio on 07.03.19.
//

#ifndef PROJECT_MQTTCHECKER_H
#define PROJECT_MQTTCHECKER_H

#include <set>
#include "logging.h"

11
12
#define MQTT_SEP '/'

Alessio Netti's avatar
Alessio Netti committed
13
14
15
16
17
18
19
20
21
/**
 * Class that manages constraint for MQTT topic formatting
 *
 * This class is implemented as a singleton. It maintains an internal set containing the currently used MQTT topics.
 * Each used sensor topic should be checked with the "check" method.
 */
class MQTTChecker {

public:
22
    
Alessio Netti's avatar
Alessio Netti committed
23
24
25
26
27
28
29
30
31
32
33
34
35
    /**
    * @brief            Returns an instance to a MQTTChecker object
    *
    *                   The MQTTChecker class is implemented as a singleton: therefore, all entities calling the
    *                   getInstance method will share the same instance of the MQTTChecker class.
    *
    * @returns          Reference to a MQTTChecker object
    */
    static MQTTChecker& getInstance() {
        static MQTTChecker m;
        return m;
    }

36
37
38
39
40
41
42
43
    /**
     * @brief           Sanitizes and formats a MQTT topic or suffix
     * 
     * @param topic     The topic or suffix to be processed 
     * @param cpuID     The cpu ID associated to this topic (if any)
     * @return          The processed MQTT topic or suffix
     */
    static std::string formatTopic(const std::string& topic, int cpuID=-1) {
Alessio Netti's avatar
Alessio Netti committed
44
45
46
47
48
49
50
51
        if(topic.empty()) return topic;
        std::string newTopic = topic;
        // Stripping off all forward slashes
        while(!newTopic.empty() && newTopic.front()==MQTT_SEP) newTopic.erase(0, 1);
        while(!newTopic.empty() && newTopic.back()==MQTT_SEP)  newTopic.erase(newTopic.size()-1, 1);
        // Adding the one front forward slash that we need
        newTopic.insert(0, 1, MQTT_SEP);
        return cpuID<0 ? newTopic : "/cpu" + std::to_string(cpuID) + newTopic;
52
53
    }

Alessio Netti's avatar
Alessio Netti committed
54
55
56
    /**
    * @brief            Resets the internal topics set
    */
Alessio Netti's avatar
Alessio Netti committed
57
58
59
60
61
62
63
64
65
66
67
68
    void reset()           { _topics.clear(); _groups.clear(); }

    /**
    * @brief            Removes a topic from the internal set
    *
    *                   This method should be used to remove obsolete topics from the set of active topics. This
    *                   usually happens when plugins are reloaded in dcdbpusher, and all old sensors are destroyed.
    *
    * @param topic      The topic to be removed
    */
    void removeTopic(const std::string& topic) {
        std::string str(topic);
69
        str.erase(std::remove(str.begin(), str.end(), MQTT_SEP), str.end());
Alessio Netti's avatar
Alessio Netti committed
70
71
        _topics.erase(str);
    }
Alessio Netti's avatar
Alessio Netti committed
72
73
74
75
76
77
78
79
80
81

    /**
    * @brief            Performs a check on a certain MQTT topic
    *
    *                   The check is passed if the topic is not present already in the internal set (to prevent duplicate
    *                   topics) and if all other internal checks are cleared.
    *
    * @param topic      An arbitrary MQTT topic to check
    * @return           True if the topic is valid, False otherwise
    */
Alessio Netti's avatar
Alessio Netti committed
82
    bool checkTopic(const std::string& topic) {
83
        //We remove all '/' characters to detect duplicates
Alessio Netti's avatar
Alessio Netti committed
84
        std::string str(topic);
85
        str.erase(std::remove(str.begin(), str.end(), MQTT_SEP), str.end());
Alessio Netti's avatar
Alessio Netti committed
86
87
88
89
90
91
92
93
        auto returnIt = _topics.insert(str);
        if (!returnIt.second) {
            LOG(error) << "MQTT-Topic \"" << topic << "\" used twice!";
            return false;
        }
        return true;
    }

Alessio Netti's avatar
Alessio Netti committed
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
    /**
    * @brief            Removes a name from the internal set of sensor names
    *
    *                   This method should be used to remove obsolete sensor names, e.g. when reloading plugins.
    *                   This is useful only in the case that MQTT topics differ from the actual sensor names.
    *
    * @param name      The name (string) to be removed
    */
    void removeName(const std::string& name) {
        _names.erase(name);
    }

    /**
    * @brief            Performs a check on a certain name
    *
    *                   The check is passed if the name for the group, analyzer or entity is not used already.
    *
    * @param name      An arbitrary name (string) to check
    * @return           True if the name is valid, False otherwise
    */
    bool checkName(const std::string& name) {
115
116
117
118
119
        auto returnIt = _names.insert(name);
        if (!returnIt.second) {
            LOG(error) << "Name \"" << name << "\" used twice!";
            return false;
        }
Alessio Netti's avatar
Alessio Netti committed
120
121
122
        return true;
    }

Alessio Netti's avatar
Alessio Netti committed
123
124
125
126
127
128
    /**
    * @brief            Removes a name from the internal set of entities
    *
    *                   This method should be used to remove obsolete sensor groups, entities or analyzers once they
    *                   are destroyed, e.g. on plugin reload actions.
    *
Alessio Netti's avatar
Alessio Netti committed
129
    * @param name      The name (string) to be removed
Alessio Netti's avatar
Alessio Netti committed
130
    */
Alessio Netti's avatar
Alessio Netti committed
131
    void removeGroup(const std::string& name) {
Alessio Netti's avatar
Alessio Netti committed
132
133
134
135
        _groups.erase(name);
    }

    /**
Alessio Netti's avatar
Alessio Netti committed
136
    * @brief            Performs a check on a certain group name
Alessio Netti's avatar
Alessio Netti committed
137
138
139
    *
    *                   The check is passed if the name for the group, analyzer or entity is not used already.
    *
Alessio Netti's avatar
Alessio Netti committed
140
    * @param name      An arbitrary name (string) to check
Alessio Netti's avatar
Alessio Netti committed
141
142
    * @return           True if the name is valid, False otherwise
    */
Alessio Netti's avatar
Alessio Netti committed
143
    bool checkGroup(const std::string& name) {
144
145
146
147
148
        auto returnIt = _groups.insert(name);
        if (!returnIt.second) {
            LOG(error) << "Group name \"" << name << "\" used twice!";
            return false;
        }
Alessio Netti's avatar
Alessio Netti committed
149
150
151
        return true;
    }

Alessio Netti's avatar
Alessio Netti committed
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
private:

    /**
    * @brief            Private class constructor
    */
    MQTTChecker() {}

    /**
    * @brief            Copy constructor is not available
    */
    MQTTChecker(MQTTChecker const&)     = delete;

    /**
    * @brief            Assignment operator is not available
    */
    void operator=(MQTTChecker const&)  = delete;

    // Set used to keep track of topics
    std::set<std::string> _topics;
Alessio Netti's avatar
Alessio Netti committed
171
172
    // Set used to keep track of groups, analyzers and other entities
    std::set<std::string> _groups;
Alessio Netti's avatar
Alessio Netti committed
173
174
    // Set used to keep track of sensor names (if different from the respective topics)
    std::set<std::string> _names;
Alessio Netti's avatar
Alessio Netti committed
175

Alessio Netti's avatar
Alessio Netti committed
176
177
178
179
180
    // Logger object to notify MQTT check outcome
    logger_t lg;
};

#endif //PROJECT_MQTTCHECKER_H