GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/config/ConfigParser.cpp Lines: 115 117 98.3 %
Date: 2022-10-28 19:38:43 Branches: 146 253 57.7 %

Line Branch Exec Source
1
#include <iostream>
2
#include <sstream>
3
4
#include "blink-lib.hpp"
5
6
#include "config/ConfigParser.hpp"
7
#include "config/ProcessMonitorConfig.hpp"
8
#include "config/RollupConfig.hpp"
9
10
namespace blink1_control::config {
11
    static const std::string CONDITIONS_STRING = "conditions";
12
    static const std::string PATTERNS_STRING = "patterns";
13
14
    static const std::unordered_map<std::string, ConditionConfig::Type> types{
15
        {"ProcessMonitor", ConditionConfig::Type::ProcessMonitor},
16
        {"Rollup", ConditionConfig::Type::Rollup}
17
    };
18
19
9
    std::optional<Config> ConfigParser::parseConfig(std::istream& instream) {
20
18
        Json json;
21
22

9
        if (instream.good()) {
23
            try {
24
8
                instream >> json;
25
26
7
                Config config;
27
7
                bool success = true;
28
7
                success &= parseConditions(json, config);
29
7
                success &= parsePatterns(json, config);
30
7
                success &= parseTopLevelVars(json, config);
31
32
7
                if (success) {
33
2
                    return std::make_optional(config);
34
                }
35
2
            } catch (nlohmann::detail::exception& err) {
36

1
                std::cout << "Error parsing json: " << err.what() << std::endl;
37
            }
38
        } else {
39

1
            std::cout << "Error reading from stream" << std::endl;
40
        }
41
42
7
        return std::nullopt;
43
    }
44
45
7
    bool ConfigParser::parseTopLevelVars(const Json& json, Config& config) {
46

7
        if (json.find("socketPath") != json.end()) {
47
1
            config.socketPath = json.at("socketPath");
48
        }
49
50
7
        return true;
51
    }
52
53
14
    static bool parseArray(const Json& json, Config& config, const std::string& arrayName, const std::function<bool(const Json&, Config&)>& parseFunction) {
54
14
        bool success = true;
55
56

14
        if (json.find(arrayName) != json.end()) {
57

22
            Json conditionsArray = json.at(arrayName);
58

21
            for (auto& conditionJson : conditionsArray) {
59

10
                success &= parseFunction(conditionJson, config);
60
            }
61
        }
62
63
14
        return success;
64
    }
65
66
7
    bool ConfigParser::parseConditions(const Json& json, Config& config) {
67

13
        return parseArray(json, config, CONDITIONS_STRING, [](const Json& ljson, Config& lconfig){return parseCondition(ljson, lconfig);});
68
    }
69
70
6
    bool ConfigParser::parseCondition(const Json& json, Config& config) {
71
6
        bool success = true;
72
73
        try {
74
6
            std::shared_ptr<ConditionConfig> condition;
75
76

6
            auto type = types.at(json.at("type"));
77
5
            switch (type) {
78
3
                case ConditionConfig::Type::ProcessMonitor:
79
3
                    condition = parseProcessMonitor(json);
80
2
                    break;
81
2
                case ConditionConfig::Type::Rollup:
82
2
                    condition = parseRollup(json);
83
1
                    break;
84
            }
85
86

3
            condition->name = json.at("name");
87
3
            condition->type = type;
88
89


4
            for (const Json& patternName : json.at("patterns")) {
90
1
                if (patternName.is_string()) {
91

1
                    condition->patterns.push_back(patternName);
92
                } else {
93
                    std::cout << "Pattern name is not a string: " << patternName << std::endl;
94
                    success = false;
95
                }
96
            }
97
98
3
            if (success) {
99
3
                config.conditionConfigs.insert_or_assign(condition->name, condition);
100
            }
101
3
        } catch (std::exception& err) {
102

3
            std::cout << "Error parsing condition: " << err.what() << std::endl;
103
3
            success = false;
104
        }
105
106
6
        return success;
107
    }
108
109
3
    std::shared_ptr<ProcessMonitorConfig> ConfigParser::parseProcessMonitor(const Json& json) {
110
3
        auto pm_config = std::make_shared<ProcessMonitorConfig>();
111

3
        pm_config->cmd = json.at("cmd");
112
2
        return pm_config;
113
    }
114
115
2
    std::shared_ptr<RollupConfig> ConfigParser::parseRollup(const Json& json) {
116
2
        auto rollupConfig = std::make_shared<RollupConfig>();
117



4
        for (Json childConfig : json.at("children")) {
118
4
            RollupChild childCondition;
119

2
            childCondition.name = childConfig.at("name");
120

2
            childCondition.critical = childConfig.at("critical");
121
2
            rollupConfig->children.push_back(childCondition);
122
        }
123
1
        return rollupConfig;
124
    }
125
126
7
    bool ConfigParser::parsePatterns(const Json& json, Config& config) {
127

11
        return parseArray(json, config, PATTERNS_STRING, [](const Json& ljson, Config& lconfig){return parsePattern(ljson, lconfig);});
128
    }
129
130
5
    void ConfigParser::readPattern(const Json& json, std::vector<std::unique_ptr<PatternCommand>>& commands) {
131


10
        for (const Json& line : json) {
132
7
            bool hasLed = line.contains("led");
133
7
            bool hasColor = line.contains("color");
134
135

7
            if (hasLed && hasColor) {
136
5
                blink1_lib::PatternLineN patternLine;
137

6
                patternLine.rgbn = parseRgb(line.at("color"));
138

4
                patternLine.rgbn.n = line.at("led");
139

4
                patternLine.fadeMillis = line.at("time");
140
141

4
                commands.push_back(std::make_unique<FadeCommand>(patternLine));
142

2
            } else if (hasLed || hasColor) {
143
1
                throw std::runtime_error("Pattern line must contain both 'led' and 'color' or neither of them");
144
            } else {
145


1
                commands.push_back(std::make_unique<WaitCommand>(std::chrono::milliseconds(line.at("time"))));
146
            }
147
        }
148
3
    }
149
150
4
    bool ConfigParser::parsePattern(const Json& json, Config& config) {
151
4
        bool success = true;
152
153
        try {
154
8
            std::shared_ptr<PatternConfig> pattern = std::make_shared<PatternConfig>();
155

4
            pattern->name = json.at("name");
156

3
            pattern->repeat = json.at("repeat");
157
158

3
            readPattern(json.at("lines"), pattern->pattern);
159

1
            if (json.contains("before")) {
160

1
                readPattern(json.at("before"), pattern->before);
161
            }
162

1
            if (json.contains("after")) {
163

1
                readPattern(json.at("after"), pattern->after);
164
            }
165
166
1
            config.patternConfigs.emplace(pattern->name, pattern);
167
3
        } catch (std::exception& err) {
168

3
            std::cout << "Error parsing pattern: " << err.what() << std::endl;
169
3
            success = false;
170
        }
171
172
4
        return success;
173
    }
174
175
12
    static std::uint8_t parseHexString(const std::string& string) {
176
12
        unsigned int x = 0;
177
12
        std::stringstream ss;
178

12
        ss << std::hex << string;
179
12
        ss >> x;
180
181
24
        return static_cast<std::uint8_t>(x);
182
    }
183
184
5
    blink1_lib::RGBN ConfigParser::parseRgb(const std::string& rgbString) {
185
5
        constexpr int RGB_STR_LEN = 7;
186
5
        constexpr int COLOR_LEN = 2;
187
5
        constexpr int RED_START = 1;
188
5
        constexpr int GREEN_START = 3;
189
5
        constexpr int BLUE_START = 5;
190
191
5
        blink1_lib::RGBN out;
192

5
        if (rgbString.rfind('#', 0) == 0 && rgbString.length() == RGB_STR_LEN) {
193
8
            std::string redString = rgbString.substr(RED_START, COLOR_LEN);
194
8
            std::string greenString = rgbString.substr(GREEN_START, COLOR_LEN);
195
4
            std::string blueString = rgbString.substr(BLUE_START, COLOR_LEN);
196
197
4
            out.r = parseHexString(redString);
198
4
            out.g = parseHexString(greenString);
199
4
            out.b = parseHexString(blueString);
200
        } else {
201

1
            throw std::runtime_error("Cannot parse RGB string: " + rgbString);
202
        }
203
204
4
        return out;
205
    }
206
}