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

wirenboard / wb-mqtt-opcua / 76

02 Mar 2026 04:58AM UTC coverage: 57.527% (-0.4%) from 57.942%
76

push

github

web-flow
Add option to prevent groups update (#34)

168 of 285 branches covered (58.95%)

0 of 14 new or added lines in 2 files covered. (0.0%)

321 of 558 relevant lines covered (57.53%)

1.44 hits per line

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

74.45
/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];
2✔
27
    }
28

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

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

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

59
    OPCUA::TObjectNodesConfig LoadNodes(const Json::Value& config)
3✔
60
    {
61
        OPCUA::TObjectNodesConfig res;
3✔
62
        bool anyEnabled = false;
3✔
63
        for (const auto& group: config["groups"]) {
6✔
64
            bool enabled = false;
3✔
65
            Get(group, "enabled", enabled);
3✔
66
            if (enabled) {
3✔
67
                anyEnabled = true;
3✔
68
                res.insert({group["name"].asString(), LoadVariableNodes(group["controls"])});
3✔
69
            }
70
        }
71
        if (!anyEnabled) {
3✔
72
            throw TEmptyConfigException();
×
73
        }
74
        return res;
3✔
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());
4✔
89
        info += (c->IsReadonly() ? " (read only)" : " (setup is allowed)");
2✔
90
        std::string controlName(c->GetDevice()->GetId() + "/" + c->GetId());
4✔
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
        }
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"]) {
3✔
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)
3✔
123
    {
124
        if (configRoot.isMember("mqtt")) {
3✔
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
    }
3✔
136
}
137

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

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

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

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

207
    for (auto& group: oldConfig["groups"]) {
2✔
208
        for (auto& control: group["controls"]) {
2✔
209
            auto topic = control["topic"].asString();
2✔
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
        }
223
    }
224

225
    for (auto& mqttDevice: mqttDevices) {
3✔
226
        auto controls(MakeControlsConfig(mqttDevice.second));
4✔
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
    }
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