• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wirenboard / wb-mqtt-adc / 51

22 Aug 2025 08:50AM UTC coverage: 57.332% (-0.4%) from 57.722%
51

push

github

web-flow
Cleanup deb packaging (#49)

250 of 430 branches covered (58.14%)

434 of 757 relevant lines covered (57.33%)

4.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.42
/src/config.cpp
1
#include "config.h"
2
#include <algorithm>
3
#include <chrono>
4
#include <fstream>
5
#include <numeric>
6
#include <wblib/json_utils.h>
7
#include <wblib/utils.h>
8

9
#include "file_utils.h"
10

11
using namespace Json;
12
using namespace WBMQTT::JSON;
13
using namespace std;
14

15
namespace
16
{
17
    const string ProtectedProperties[] = {"match_iio",
18
                                          "channel_number",
19
                                          "mqtt_type",
20
                                          "voltage_multiplier",
21
                                          "max_voltage"};
22

23
    void LoadChannel(const Value& item, vector<TADCChannelSettings>& channels)
7✔
24
    {
25
        TADCChannelSettings channel;
14✔
26

27
        Get(item, "id", channel.Id);
7✔
28
        Get(item, "averaging_window", channel.ReaderCfg.AveragingWindow);
7✔
29
        if (Get(item, "max_voltage", channel.ReaderCfg.MaxScaledVoltage))
7✔
30
            channel.ReaderCfg.MaxScaledVoltage *= 1000;
1✔
31
        Get(item, "voltage_multiplier", channel.ReaderCfg.VoltageMultiplier);
7✔
32
        Get(item, "decimal_places", channel.ReaderCfg.DecimalPlaces);
7✔
33
        Get(item, "scale", channel.ReaderCfg.DesiredScale);
7✔
34
        Get(item, "poll_interval", channel.ReaderCfg.PollInterval);
7✔
35
        Get(item, "delay_between_measurements", channel.ReaderCfg.DelayBetweenMeasurements);
7✔
36
        Get(item, "match_iio", channel.MatchIIO);
7✔
37

38
        Value v = item["channel_number"];
14✔
39
        if (v.isInt()) {
7✔
40
            channel.ReaderCfg.ChannelNumber = "voltage" + to_string(v.asInt());
×
41
        } else {
42
            channel.ReaderCfg.ChannelNumber = v.asString();
7✔
43
        }
44
        channels.push_back(channel);
7✔
45
    }
7✔
46

47
    /*! This function recalculates delay between measurements if it is too large for
48
        selected poll interval. If so, measurements will be evenly distributed in interval
49
    */
50
    void MaybeFixDelayBetweenMeasurements(TConfig& cfg, WBMQTT::TLogger* log)
5✔
51
    {
52
        for (auto& ch: cfg.Channels) {
11✔
53
            auto calcMeasureDelay = ch.ReaderCfg.DelayBetweenMeasurements * ch.ReaderCfg.AveragingWindow;
6✔
54
            if (calcMeasureDelay > ch.ReaderCfg.PollInterval) {
6✔
55
                if (log) {
1✔
56
                    log->Log() << ch.Id << ": averaging delay doesn't fit in poll_interval, "
2✔
57
                               << "adjusting delay_between_measurements";
1✔
58
                }
59

60
                ch.ReaderCfg.DelayBetweenMeasurements = ch.ReaderCfg.PollInterval / ch.ReaderCfg.AveragingWindow;
1✔
61
            }
62
        }
63
    }
5✔
64

65
    Value Load(const string& filePath, const Value& schema)
25✔
66
    {
67
        auto json = Parse(filePath);
25✔
68
        Validate(json, schema);
25✔
69
        return json;
18✔
70
    }
71

72
    TConfig LoadFromJSON(const Value& configJson)
6✔
73
    {
74
        TConfig config;
6✔
75

76
        Get(configJson, "device_name", config.DeviceName);
6✔
77
        Get(configJson, "debug", config.EnableDebugMessages);
6✔
78
        Get(configJson, "max_unchanged_interval", config.MaxUnchangedInterval);
6✔
79

80
        const auto& ch = configJson["iio_channels"];
6✔
81
        for_each(ch.begin(), ch.end(), [&](const Value& v) { LoadChannel(v, config.Channels); });
13✔
82

83
        return config;
6✔
84
    }
85

86
    Value RemoveDeviceNameRequirement(const Value& schema)
18✔
87
    {
88
        Value newArray = arrayValue;
36✔
89
        for (auto& v: schema["required"]) {
54✔
90
            if (v.asString() != "device_name") {
36✔
91
                newArray.append(v);
18✔
92
            }
93
        }
94
        Value res(schema);
18✔
95
        res["required"] = newArray;
18✔
96
        return res;
36✔
97
    }
98
} // namespace
99

100
TConfig LoadConfig(const string& mainConfigFile,
16✔
101
                   const string& optionalConfigFile,
102
                   const string& systemConfigDir,
103
                   const string& schemaFile,
104
                   WBMQTT::TLogger* infoLogger,
105
                   WBMQTT::TLogger* warnLogger)
106
{
107
    auto schema = Parse(schemaFile);
29✔
108

109
    if (!optionalConfigFile.empty()) {
13✔
110
        return LoadFromJSON(Load(optionalConfigFile, schema));
3✔
111
    }
112

113
    auto noDeviceNameSchema = RemoveDeviceNameRequirement(schema);
22✔
114

115
    TMergeParams mergeParams;
22✔
116
    mergeParams.LogPrefix = "[config] ";
11✔
117
    mergeParams.InfoLogger = infoLogger;
11✔
118
    mergeParams.WarnLogger = warnLogger;
11✔
119
    mergeParams.MergeArraysOn["/iio_channels"] = "id";
11✔
120

121
    Json::Value resultingConfig;
22✔
122
    resultingConfig["iio_channels"] = Json::Value(Json::arrayValue);
11✔
123

124
    try {
125
        IterateDirByPattern(systemConfigDir, ".conf", [&](const string& f) {
5✔
126
            Merge(resultingConfig, Load(f, noDeviceNameSchema), mergeParams);
5✔
127
            return false;
5✔
128
        });
28✔
129
    } catch (const TNoDirError&) {
6✔
130
    }
131

132
    for (const auto& pr: ProtectedProperties) {
66✔
133
        mergeParams.ProtectedParameters.insert("/iio_channels/" + pr);
55✔
134
    }
135
    Merge(resultingConfig, Load(mainConfigFile, schema), mergeParams);
11✔
136
    auto cfg = LoadFromJSON(resultingConfig);
10✔
137
    MaybeFixDelayBetweenMeasurements(cfg, infoLogger);
5✔
138
    return cfg;
5✔
139
}
140

141
void MakeJsonForConfed(const string& configFile, const string& systemConfigsDir, const string& schemaFile)
×
142
{
143
    auto schema = Parse(schemaFile);
×
144
    auto noDeviceNameSchema = RemoveDeviceNameRequirement(schema);
×
145
    auto config = Load(configFile, schema);
×
146
    unordered_map<string, Value> configuredChannels;
×
147
    for (const auto& ch: config["iio_channels"]) {
×
148
        configuredChannels.emplace(ch["id"].asString(), ch);
×
149
    }
150
    Value newChannels(arrayValue);
×
151
    try {
152
        IterateDirByPattern(systemConfigsDir, ".conf", [&](const string& f) {
×
153
            auto cfg = Load(f, noDeviceNameSchema);
×
154
            for (const auto& ch: cfg["iio_channels"]) {
×
155
                auto name = ch["id"].asString();
×
156
                auto it = configuredChannels.find(name);
×
157
                if (it != configuredChannels.end()) {
×
158
                    newChannels.append(it->second);
×
159
                    configuredChannels.erase(name);
×
160
                } else {
161
                    Value v;
×
162
                    v["id"] = ch["id"];
×
163
                    v["decimal_places"] = ch["decimal_places"];
×
164
                    newChannels.append(v);
×
165
                }
166
            }
167
            return false;
×
168
        });
×
169
    } catch (const TNoDirError&) {
×
170
    }
171
    for (const auto& ch: config["iio_channels"]) {
×
172
        auto it = configuredChannels.find(ch["id"].asString());
×
173
        if (it != configuredChannels.end()) {
×
174
            newChannels.append(ch);
×
175
        }
176
    }
177
    config["iio_channels"].swap(newChannels);
×
178
    MakeWriter("", "None")->write(config, &cout);
×
179
}
180

181
void MakeConfigFromConfed(const string& systemConfigsDir, const string& schemaFile)
×
182
{
183
    auto noDeviceNameSchema = RemoveDeviceNameRequirement(Parse(schemaFile));
×
184
    unordered_set<string> systemChannels;
×
185
    try {
186
        IterateDirByPattern(systemConfigsDir, ".conf", [&](const string& f) {
×
187
            auto cfg = Load(f, noDeviceNameSchema);
×
188
            for (const auto& ch: cfg["iio_channels"]) {
×
189
                systemChannels.insert(ch["id"].asString());
×
190
            }
191
            return false;
×
192
        });
×
193
    } catch (const TNoDirError&) {
×
194
    }
195

196
    Value config;
×
197
    CharReaderBuilder readerBuilder;
×
198
    String errs;
×
199

200
    if (!parseFromStream(readerBuilder, cin, &config, &errs)) {
×
201
        throw runtime_error("Failed to parse JSON:" + errs);
×
202
    }
203

204
    Value newChannels(arrayValue);
×
205
    for (auto& ch: config["iio_channels"]) {
×
206
        auto it = systemChannels.find(ch["id"].asString());
×
207
        if (it != systemChannels.end()) {
×
208
            for (const auto& pr: ProtectedProperties) {
×
209
                ch.removeMember(pr);
×
210
            }
211
            if (ch.size() > 1) {
×
212
                newChannels.append(ch);
×
213
            }
214
        } else {
215
            newChannels.append(ch);
×
216
        }
217
    }
218
    config["iio_channels"].swap(newChannels);
×
219
    MakeWriter("  ", "None")->write(config, &cout);
×
220
}
221

222
void MakeSchemaForConfed(const string& systemConfigsDir, const string& schemaFile, const string& schemaForConfedFile)
7✔
223
{
224
    auto schema = Parse(schemaFile);
14✔
225
    auto noDeviceNameSchema = RemoveDeviceNameRequirement(schema);
14✔
226
    unordered_set<string> systemChannels;
14✔
227
    try {
228
        IterateDirByPattern(systemConfigsDir, ".conf", [&](const string& f) {
7✔
229
            auto cfg = Load(f, noDeviceNameSchema);
7✔
230
            for (const auto& ch: cfg["iio_channels"]) {
14✔
231
                systemChannels.insert(ch["id"].asString());
7✔
232
            }
233
            return false;
14✔
234
        });
14✔
235
    } catch (const TNoDirError&) {
×
236
    }
237
    auto& notNode = schema["definitions"]["custom_channel"]["allOf"][1]["not"]["properties"]["id"]["enum"];
7✔
238
    auto& sysIdsNode = schema["definitions"]["system_channel"]["allOf"][1]["properties"]["id"]["enum"];
7✔
239
    for (const auto& id: systemChannels) {
14✔
240
        notNode.append(id);
7✔
241
        sysIdsNode.append(id);
7✔
242
    }
243
    ofstream f(schemaForConfedFile);
14✔
244
    MakeWriter("    ", "None")->write(schema, &f);
7✔
245
}
7✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc