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

wirenboard / wb-mqtt-opcua / 84

31 Jul 2026 09:25AM UTC coverage: 61.751% (+2.1%) from 59.625%
84

push

github

web-flow
Use exported vars instead of MAKEFLAGS for coverage options (#41)

232 of 345 branches covered (67.25%)

402 of 651 relevant lines covered (61.75%)

2.56 hits per line

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

72.55
/src/config_parser.cpp
1
#include "config_parser.h"
2

3
#include <fstream>
4
#include <iostream>
5

6
#include <sys/stat.h>
7

8
#include <wblib/json_utils.h>
9
#include <wblib/wbmqtt.h>
10

11
#include "log.h"
12
#include "opcua_exception.h"
13

14
using namespace std;
15
using namespace WBMQTT;
16
using namespace WBMQTT::JSON;
17

18
#define LOG(logger) ::logger.Log() << "[config] "
19

20
namespace
21
{
22
    const auto GENERATOR_ID = "wb-mqtt-opcua-config_generator";
23

24
    std::string GetDeviceName(const std::string& topic)
1✔
25
    {
26
        return StringSplit(topic, '/')[0];
1✔
27
    }
28

29
    std::string GetControlName(const std::string& topic)
1✔
30
    {
31
        return StringSplit(topic, '/')[1];
1✔
32
    }
33

34
    bool IsValidTopic(const std::string& topic)
7✔
35
    {
36
        auto l = StringSplit(topic, '/');
7✔
37
        return (l.size() == 2);
14✔
38
    }
7✔
39

40
    OPCUA::TVariableNodesConfig LoadVariableNodes(const Json::Value& controls)
5✔
41
    {
42
        OPCUA::TVariableNodesConfig res;
5✔
43
        for (const auto& control: controls) {
11✔
44
            bool enabled = false;
6✔
45
            Get(control, "enabled", enabled);
6✔
46
            if (enabled) {
6✔
47
                OPCUA::TVariableNodeConfig n;
6✔
48
                n.DeviceControlPair = control["topic"].asString();
6✔
49
                if (IsValidTopic(n.DeviceControlPair)) {
6✔
50
                    res.push_back(n);
5✔
51
                } else {
52
                    LOG(Warn) << "Invalid topic: " << n.DeviceControlPair;
1✔
53
                }
54
            }
6✔
55
        }
56
        return res;
5✔
57
    }
×
58

59
    OPCUA::TObjectNodesConfig LoadNodes(const Json::Value& config)
5✔
60
    {
61
        OPCUA::TObjectNodesConfig res;
5✔
62
        bool anyEnabled = false;
5✔
63
        for (const auto& group: config["groups"]) {
10✔
64
            bool enabled = false;
5✔
65
            Get(group, "enabled", enabled);
5✔
66
            if (enabled) {
5✔
67
                anyEnabled = true;
5✔
68
                res.insert({group["name"].asString(), LoadVariableNodes(group["controls"])});
5✔
69
            }
70
        }
71
        if (!anyEnabled) {
5✔
72
            throw TEmptyConfigException();
×
73
        }
74
        return res;
5✔
75
    }
×
76

77
    Json::Value MakeControlConfig(const std::string& topic, const std::string& info)
2✔
78
    {
79
        Json::Value cnt(Json::objectValue);
2✔
80
        cnt["topic"] = topic;
2✔
81
        cnt["info"] = info;
2✔
82
        cnt["enabled"] = false;
2✔
83
        return cnt;
2✔
84
    }
×
85

86
    void AppendControl(Json::Value& root, PControl c)
2✔
87
    {
88
        std::string info(c->GetType());
2✔
89
        info += (c->IsReadonly() ? " (read only)" : " (setup is allowed)");
2✔
90
        std::string controlName(c->GetDevice()->GetId() + "/" + c->GetId());
2✔
91
        root.append(MakeControlConfig(controlName, info));
2✔
92
    }
2✔
93

94
    Json::Value MakeControlsConfig(std::map<std::string, PControl>& controls)
2✔
95
    {
96
        Json::Value res(Json::arrayValue);
2✔
97
        for (auto control: controls) {
4✔
98
            AppendControl(res, control.second);
2✔
99
        }
2✔
100
        return res;
2✔
101
    }
×
102

103
    Json::Value MakeGroupConfig(const std::string& name)
1✔
104
    {
105
        Json::Value dev(Json::objectValue);
1✔
106
        dev["name"] = name;
1✔
107
        dev["enabled"] = false;
1✔
108
        dev["controls"] = Json::Value(Json::arrayValue);
1✔
109
        return dev;
1✔
110
    }
×
111

112
    Json::Value& GetGroup(Json::Value& config, const std::string& name)
2✔
113
    {
114
        for (auto& group: config["groups"]) {
4✔
115
            if (group["name"].asString() == name) {
2✔
116
                return group;
1✔
117
            }
118
        }
119
        return config["groups"].append(MakeGroupConfig(name));
1✔
120
    }
121

122
    void LoadMqttConfig(WBMQTT::TMosquittoMqttConfig& cfg, const Json::Value& configRoot)
5✔
123
    {
124
        if (configRoot.isMember("mqtt")) {
5✔
125
            Get(configRoot["mqtt"], "host", cfg.Host);
×
126
            Get(configRoot["mqtt"], "port", cfg.Port);
×
127
            Get(configRoot["mqtt"], "keepalive", cfg.Keepalive);
×
128
            bool auth = false;
×
129
            Get(configRoot["mqtt"], "auth", auth);
×
130
            if (auth) {
×
131
                Get(configRoot["mqtt"], "username", cfg.User);
×
132
                Get(configRoot["mqtt"], "password", cfg.Password);
×
133
            }
134
        }
135
    }
5✔
136
}
137

138
void LoadConfig(TConfig& cfg, const std::string& configFileName, const std::string& configSchemaFileName)
10✔
139
{
140
    try {
141
        auto config = JSON::Parse(configFileName);
10✔
142
        JSON::Validate(config, JSON::Parse(configSchemaFileName));
12✔
143

144
        if (config.isMember("opcua")) {
5✔
145
            Get(config["opcua"], "host", cfg.OpcUa.BindIp);
×
146
            Get(config["opcua"], "port", cfg.OpcUa.BindPort);
×
147
        }
148
        LoadMqttConfig(cfg.Mqtt, config);
5✔
149
        cfg.OpcUa.ObjectNodes = LoadNodes(config);
5✔
150
        Get(config, "debug", cfg.Debug);
5✔
151
    } catch (const TEmptyConfigException&) {
14✔
152
        throw;
×
153
    } catch (const std::exception& e) {
5✔
154
        throw TConfigException(e.what());
15✔
155
    }
5✔
156
}
5✔
157

158
void UpdateConfig(const string& configFileName, const string& configSchemaFileName)
×
159
{
160
    auto config = JSON::Parse(configFileName);
×
161
    JSON::Validate(config, JSON::Parse(configSchemaFileName));
×
162

163
    bool update_groups = false;
×
164
    Get(config, "update_groups", update_groups);
×
165
    if (update_groups) {
×
166
        WBMQTT::TMosquittoMqttConfig mqttConfig;
×
167
        mqttConfig.Id = GENERATOR_ID;
×
168
        LoadMqttConfig(mqttConfig, config);
×
169
        auto mqtt = NewMosquittoMqttClient(mqttConfig);
×
170
        auto backend = NewDriverBackend(mqtt);
×
171
        auto driver = NewDriver(TDriverArgs{}.SetId(GENERATOR_ID).SetBackend(backend));
×
172
        driver->StartLoop();
×
173
        UpdateConfig(driver, config);
×
174
        driver->StopLoop();
×
175
    }
×
176

177
    config["update_groups"] = false;
×
178

179
    Json::StreamWriterBuilder builder;
×
180
    builder["indentation"] = "    ";
×
181
    std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
×
182
    std::ofstream file(configFileName);
×
183
    writer->write(config, &file);
×
184
    file << std::endl;
×
185
}
×
186

187
void UpdateConfig(PDeviceDriver driver, Json::Value& oldConfig)
1✔
188
{
189
    driver->WaitForReady();
1✔
190
    driver->SetFilter(GetAllDevicesFilter());
1✔
191
    driver->WaitForReady();
1✔
192

193
    std::map<std::string, std::map<std::string, PControl>> mqttDevices;
1✔
194
    auto tx = driver->BeginTx();
1✔
195
    for (auto& device: tx->GetDevicesList()) {
3✔
196
        if (!WBMQTT::StringStartsWith(device->GetId(), "system__")) {
6✔
197
            std::map<std::string, PControl> controls;
2✔
198
            for (auto& control: device->ControlsList()) {
5✔
199
                controls.insert({control->GetId(), control});
3✔
200
            }
2✔
201
            if (controls.size()) {
2✔
202
                mqttDevices.insert({device->GetId(), controls});
2✔
203
            }
204
        }
2✔
205
    }
1✔
206

207
    for (auto& group: oldConfig["groups"]) {
2✔
208
        for (auto& control: group["controls"]) {
2✔
209
            auto topic = control["topic"].asString();
1✔
210
            if (IsValidTopic(topic)) {
1✔
211
                auto mqttDevice = mqttDevices.find(GetDeviceName(topic));
1✔
212
                if (mqttDevice != mqttDevices.end()) {
1✔
213
                    auto mqttControl = mqttDevice->second.find(GetControlName(topic));
1✔
214
                    if (mqttControl != mqttDevice->second.end()) {
1✔
215
                        mqttDevice->second.erase(mqttControl);
1✔
216
                    }
217
                }
218
                if (!mqttDevice->second.size()) {
1✔
219
                    mqttDevices.erase(mqttDevice);
×
220
                }
221
            }
222
        }
1✔
223
    }
224

225
    for (auto& mqttDevice: mqttDevices) {
3✔
226
        auto controls(MakeControlsConfig(mqttDevice.second));
2✔
227
        if (controls.size()) {
2✔
228
            auto& configGroup = GetGroup(oldConfig, mqttDevice.first);
2✔
229
            for (auto& v: controls) {
4✔
230
                configGroup["controls"].append(v);
2✔
231
            }
232
        }
233
    }
2✔
234
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc