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

#ifndef PROJECT_MQTTCHECKER_H
#define PROJECT_MQTTCHECKER_H

#include <set>
Alessio Netti's avatar
Alessio Netti committed
9
#include <algorithm>
Alessio Netti's avatar
Alessio Netti committed
10
11
#include "logging.h"

12
13
#define MQTT_SEP '/'

Alessio Netti's avatar
Alessio Netti committed
14
15
16
17
18
19
20
21
22
/**
 * 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:
23
    
Alessio Netti's avatar
Alessio Netti committed
24
25
26
27
28
29
30
31
32
33
34
35
36
    /**
    * @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;
    }

Alessio Netti's avatar
Alessio Netti committed
37
38
39
40
41
42
43
44
45
46
47
48
49
50
    /**
     * @brief           Replaces all characters in a MQTT topics matching a token and sanitizes the result
     * 
     * @param topic     The topic or suffix to be processed 
     * @param tok       The token character to be replaced with forward slashes
     * @return          The processed MQTT topic
     */
    static std::string convertTopic(const std::string& topic, const char tok='.') {
        if(topic.empty()) return topic;
        std::string newTopic = topic;
        std::replace(newTopic.begin(), newTopic.end(), tok, MQTT_SEP);
        return newTopic;
    }
    
51
52
53
54
55
56
57
58
    /**
     * @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
59
60
61
62
63
64
65
66
        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;
67
68
    }

Alessio Netti's avatar
Alessio Netti committed
69
70
71
    /**
    * @brief            Resets the internal topics set
    */
Alessio Netti's avatar
Alessio Netti committed
72
73
74
75
76
77
78
79
80
81
82
83
    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);
84
        str.erase(std::remove(str.begin(), str.end(), MQTT_SEP), str.end());
Alessio Netti's avatar
Alessio Netti committed
85
86
        _topics.erase(str);
    }
Alessio Netti's avatar
Alessio Netti committed
87
88
89
90
91
92
93
94
95
96

    /**
    * @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
97
    bool checkTopic(const std::string& topic) {
98
        //We remove all '/' characters to detect duplicates
Alessio Netti's avatar
Alessio Netti committed
99
        std::string str(topic);
100
        str.erase(std::remove(str.begin(), str.end(), MQTT_SEP), str.end());
Alessio Netti's avatar
Alessio Netti committed
101
102
103
104
105
106
107
108
        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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
    /**
    * @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) {
130
131
132
133
134
        auto returnIt = _names.insert(name);
        if (!returnIt.second) {
            LOG(error) << "Name \"" << name << "\" used twice!";
            return false;
        }
Alessio Netti's avatar
Alessio Netti committed
135
136
137
        return true;
    }

Alessio Netti's avatar
Alessio Netti committed
138
139
140
141
142
143
    /**
    * @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
144
    * @param name      The name (string) to be removed
Alessio Netti's avatar
Alessio Netti committed
145
    */
Alessio Netti's avatar
Alessio Netti committed
146
    void removeGroup(const std::string& name) {
Alessio Netti's avatar
Alessio Netti committed
147
148
149
150
        _groups.erase(name);
    }

    /**
Alessio Netti's avatar
Alessio Netti committed
151
    * @brief            Performs a check on a certain group name
Alessio Netti's avatar
Alessio Netti committed
152
153
154
    *
    *                   The check is passed if the name for the group, analyzer or entity is not used already.
    *
Alessio Netti's avatar
Alessio Netti committed
155
    * @param name      An arbitrary name (string) to check
Alessio Netti's avatar
Alessio Netti committed
156
157
    * @return           True if the name is valid, False otherwise
    */
Alessio Netti's avatar
Alessio Netti committed
158
    bool checkGroup(const std::string& name) {
159
160
161
162
163
        auto returnIt = _groups.insert(name);
        if (!returnIt.second) {
            LOG(error) << "Group name \"" << name << "\" used twice!";
            return false;
        }
Alessio Netti's avatar
Alessio Netti committed
164
165
166
        return true;
    }

Alessio Netti's avatar
Alessio Netti committed
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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
186
187
    // Set used to keep track of groups, analyzers and other entities
    std::set<std::string> _groups;
Alessio Netti's avatar
Alessio Netti committed
188
189
    // 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
190

Alessio Netti's avatar
Alessio Netti committed
191
192
193
194
195
    // Logger object to notify MQTT check outcome
    logger_t lg;
};

#endif //PROJECT_MQTTCHECKER_H