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

wirenboard / wb-mqtt-mbgate / 1

31 Jul 2025 10:23AM UTC coverage: 70.692%. First build
1

push

github

u236
fix varchar register negative size bug

701 of 928 branches covered (75.54%)

0 of 2 new or added lines in 1 file covered. (0.0%)

1165 of 1648 relevant lines covered (70.69%)

24.02 hits per line

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

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

3
#include <fstream>
4
#include <iostream>
5
#include <memory>
6
#include <string>
7
#include <time.h>
8
#include <tuple>
9
#include <unistd.h>
10

11
#include <wblib/json_utils.h>
12
#include <wblib/mqtt.h>
13

14
#include "log.h"
15
#include "mbgate_exception.h"
16
#include "modbus_lmb_backend.h"
17
#include "mqtt_converters.h"
18
#include "observer.h"
19

20
using namespace std;
21
using namespace WBMQTT;
22
using namespace WBMQTT::JSON;
23

24
#define LOG(logger) ::logger.Log() << "[config] "
25

26
namespace
27
{
28
    string expandTopic(const string& t)
×
29
    {
30
        auto lst = StringSplit(t, '/');
×
31
        return string("/devices/") + lst[0] + "/controls/" + lst[1];
×
32
    }
33
};
34

35
IConfigParser::~IConfigParser()
×
36
{}
37

38
TJSONConfigParser::TJSONConfigParser(const string& config_file, const string& schema_file): Root(Parse(config_file))
×
39
{
40
    try {
41
        auto schema(Parse(schema_file));
×
42
        Validate(Root, schema);
×
43
    } catch (const std::runtime_error& e) {
×
44
        throw TConfigException(e.what());
×
45
    }
46
}
47

48
bool TJSONConfigParser::Debug()
×
49
{
50
    bool debug = false;
×
51
    Get(Root, "debug", debug);
×
52
    return debug;
×
53
}
54

55
tuple<PModbusServer, PMqttClient> TJSONConfigParser::Build()
×
56
{
57
    PModbusBackend modbusBackend = nullptr;
×
58

59
    auto modbus_data = Root["modbus"];
×
60

61
    if (modbus_data.isMember("path")) {
×
62
        TModbusRTUBackendArgs args{};
×
63

64
        args.Device = modbus_data["path"].asCString();
×
65

66
        args.BaudRate = modbus_data.get("baud_rate", args.BaudRate).asInt();
×
67
        args.DataBits = modbus_data.get("data_bits", args.DataBits).asInt();
×
68
        args.StopBits = modbus_data.get("stop_bits", args.StopBits).asInt();
×
69
        args.Parity = modbus_data.get("parity", std::string(1, args.Parity)).asString()[0];
×
70

71
        LOG(Debug) << "Modbus configuration: device " << args.Device << ", baud rate " << args.BaudRate << ", parity "
×
72
                   << args.Parity << ", data bits " << args.DataBits << ", stop bits " << args.StopBits;
×
73

74
        modbusBackend = make_shared<TModbusRTUBackend>(args);
×
75
    } else {
76
        string modbus_host = modbus_data["host"].asString();
×
77
        int modbus_port = modbus_data["port"].asInt();
×
78

79
        LOG(Debug) << "Modbus configuration: host " << modbus_host << ", port " << modbus_port;
×
80
        modbusBackend = make_shared<TModbusTCPBackend>(modbus_host.c_str(), modbus_port);
×
81
    }
82

83
    PModbusServer modbus = make_shared<TModbusServer>(modbusBackend);
×
84

85
    // create MQTT client
86
    string mqtt_host = Root["mqtt"]["host"].asString();
×
87
    int mqtt_port = Root["mqtt"]["port"].asInt();
×
88
    int mqtt_keepalive = 60;
×
89
    Get(Root["mqtt"], "keepalive", mqtt_keepalive);
×
90

91
    LOG(Debug) << "MQTT configuration: host " << mqtt_host << ", port " << mqtt_port << ", keepalive "
×
92
               << mqtt_keepalive;
×
93

94
    TMosquittoMqttConfig mqtt_config;
×
95
    mqtt_config.Host = mqtt_host;
×
96
    mqtt_config.Port = mqtt_port;
×
97
    mqtt_config.Keepalive = mqtt_keepalive;
×
98
    mqtt_config.Id = string("mqtt-mbgate-") + to_string(time(NULL));
×
99

100
    PMqttClient mqtt = NewMosquittoMqttClient(mqtt_config);
×
101

102
    bool any_enabled = false;
×
103

104
    // create observers and link'em with MQTT and Modbus
105
    any_enabled |= _BuildStore(COIL, Root["registers"]["coils"], modbus, mqtt);
×
106
    any_enabled |= _BuildStore(DISCRETE_INPUT, Root["registers"]["discretes"], modbus, mqtt);
×
107
    any_enabled |= _BuildStore(HOLDING_REGISTER, Root["registers"]["holdings"], modbus, mqtt);
×
108
    any_enabled |= _BuildStore(INPUT_REGISTER, Root["registers"]["inputs"], modbus, mqtt);
×
109

110
    if (!any_enabled) {
×
111
        throw TEmptyConfigException();
×
112
    }
113

114
    return make_tuple(modbus, mqtt);
×
115
}
116

117
bool TJSONConfigParser::_BuildStore(TStoreType type, const Json::Value& list, PModbusServer modbus, PMqttClient mqtt)
×
118
{
119
    LOG(Debug) << "Processing store " << type;
×
120

121
    bool enabled = false;
×
122

123
    for (const auto& reg_item: list) {
×
124
        if (!reg_item["enabled"].asBool()) {
×
125
            continue;
×
126
        }
127
        enabled = true;
×
128

129
        int address = reg_item["address"].asInt();
×
130
        int slave_id = reg_item["unitId"].asInt();
×
131
        string topic = reg_item["topic"].asString();
×
132
        int size;
133

134
        LOG(Debug) << "Element " << topic << " : " << address;
×
135

136
        PGatewayObserver obs;
×
137
        PMQTTConverter conv;
×
138

139
        if (type == COIL || type == DISCRETE_INPUT) {
×
140
            conv = make_shared<TMQTTDiscrConverter>();
×
141
            size = 1;
×
142
        } else {
143
            string format = reg_item["format"].asString();
×
144
            bool byteswap = reg_item["byteswap"].asBool();
×
145
            bool wordswap = reg_item["wordswap"].asBool();
×
146
            size = reg_item["size"].asInt();
×
147

148
            if (format == "varchar") {
×
149
                // old version config may contain varchar registers with negative size value (-1)
150
                // set this registers size to 1
NEW
151
                if (size < 0)
×
NEW
152
                    size = 1;
×
153
                conv = make_shared<TMQTTTextConverter>(size, byteswap, wordswap);
×
154
            } else if (format == "float") {
×
155
                double scale = reg_item["scale"].asFloat();
×
156
                conv = make_shared<TMQTTFloatConverter>(size, byteswap, wordswap, scale);
×
157
                size /= 2;
×
158
            } else {
159
                double scale = reg_item["scale"].asFloat();
×
160

161
                TMQTTIntConverter::IntegerType int_type;
162
                if (format == "signed") {
×
163
                    int_type = TMQTTIntConverter::SIGNED;
×
164
                } else if (format == "unsigned") {
×
165
                    int_type = TMQTTIntConverter::UNSIGNED;
×
166
                } else if (format == "bcd") {
×
167
                    int_type = TMQTTIntConverter::BCD;
×
168
                } else {
169
                    throw TConfigException("Unknown integer format: " + format);
×
170
                }
171

172
                conv = make_shared<TMQTTIntConverter>(int_type, scale, size, byteswap, wordswap);
×
173
                size /= 2;
×
174
            }
175
        }
176

177
        obs = make_shared<TGatewayObserver>(expandTopic(topic), conv, mqtt);
×
178

179
        LOG(Debug) << "Creating observer on " << address << ":" << size;
×
180

181
        try {
182
            modbus->Observe(obs, type, TModbusAddressRange(address, size), slave_id);
×
183
        } catch (const WrongSegmentException& e) {
×
184
            throw TConfigException(string("Address overlapping: ") + StoreTypeToString(type) + ": topic " + topic);
×
185
        }
186
    }
187
    return enabled;
×
188
}
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