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

wirenboard / wb-mqtt-opcua / 1

20 Sep 2026 09:11AM UTC coverage: 66.203% (-0.04%) from 66.246%
1

push

github

sikmir
Fix undefined behaviors

274 of 386 branches covered (70.98%)

9 of 12 new or added lines in 2 files covered. (75.0%)

476 of 719 relevant lines covered (66.2%)

534.52 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)
2✔
25
    {
26
        return StringSplit(topic, '/')[0];
2✔
27
    }
28

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

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

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

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

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

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

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

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

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

122
    void LoadMqttConfig(WBMQTT::TMosquittoMqttConfig& cfg, const Json::Value& configRoot)
12✔
123
    {
124
        if (configRoot.isMember("mqtt")) {
12✔
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
    }
12✔
136
}
137

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

144
        if (config.isMember("opcua")) {
12✔
145
            Get(config["opcua"], "host", cfg.OpcUa.BindIp);
×
146
            Get(config["opcua"], "port", cfg.OpcUa.BindPort);
×
147
        }
148
        LoadMqttConfig(cfg.Mqtt, config);
12✔
149
        cfg.OpcUa.ObjectNodes = LoadNodes(config);
12✔
150
        Get(config, "debug", cfg.Debug);
12✔
151
    } catch (const TEmptyConfigException&) {
30✔
152
        throw;
×
153
    } catch (const std::exception& e) {
10✔
154
        throw TConfigException(e.what());
30✔
155
    }
10✔
156
}
12✔
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)
2✔
188
{
189
    driver->WaitForReady();
2✔
190
    driver->SetFilter(GetAllDevicesFilter());
2✔
191
    driver->WaitForReady();
2✔
192

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

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

225
    for (auto& mqttDevice: mqttDevices) {
6✔
226
        auto controls(MakeControlsConfig(mqttDevice.second));
4✔
227
        if (controls.size()) {
4✔
228
            auto& configGroup = GetGroup(oldConfig, mqttDevice.first);
4✔
229
            for (auto& v: controls) {
8✔
230
                configGroup["controls"].append(v);
4✔
231
            }
232
        }
233
    }
4✔
234
}
2✔
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