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

wirenboard / wb-mqtt-serial / 679

04 Aug 2025 08:26AM UTC coverage: 73.003% (+0.09%) from 72.915%
679

push

github

web-flow
Add publish sporadic registers data on every read feature

6479 of 9242 branches covered (70.1%)

23 of 24 new or added lines in 3 files covered. (95.83%)

2 existing lines in 1 file now uncovered.

12401 of 16987 relevant lines covered (73.0%)

373.42 hits per line

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

0.0
/src/main.cpp
1
#include "log.h"
2
#include "serial_device.h"
3
#include "serial_driver.h"
4

5
#include <wblib/rpc.h>
6
#include <wblib/signal_handling.h>
7
#include <wblib/wbmqtt.h>
8

9
#include <filesystem>
10
#include <fstream>
11
#include <getopt.h>
12
#include <unistd.h>
13

14
#include "confed_config_generator.h"
15
#include "confed_json_generator.h"
16
#include "confed_schema_generator.h"
17
#include "config_schema_generator.h"
18

19
#include "device_template_generator.h"
20
#include "files_watcher.h"
21
#include "rpc/rpc_config.h"
22
#include "rpc/rpc_config_handler.h"
23
#include "rpc/rpc_device_handler.h"
24
#include "rpc/rpc_port_handler.h"
25
#include "serial_port.h"
26

27
#define STR(x) #x
28
#define XSTR(x) STR(x)
29

30
using namespace std;
31

32
#define LOG(logger) ::logger.Log() << "[serial] "
33

34
const auto driverName = "wb-modbus";
35

36
const auto APP_NAME = "wb-mqtt-serial";
37

38
const auto LIBWBMQTT_DB_FULL_FILE_PATH = "/var/lib/wb-mqtt-serial/libwbmqtt.db";
39
const auto CONFIG_FULL_FILE_PATH = "/etc/wb-mqtt-serial.conf";
40
const auto TEMPLATES_DIR = "/usr/share/wb-mqtt-serial/templates";
41
const auto USER_TEMPLATES_DIR = "/etc/wb-mqtt-serial.conf.d/templates";
42
const auto PORTS_JSON_SCHEMA_FULL_FILE_PATH = "/usr/share/wb-mqtt-serial/wb-mqtt-serial-ports.schema.json";
43
const auto TEMPLATES_JSON_SCHEMA_FULL_FILE_PATH =
44
    "/usr/share/wb-mqtt-serial/wb-mqtt-serial-device-template.schema.json";
45
const auto RPC_PORT_LOAD_REQUEST_SCHEMA_FULL_FILE_PATH =
46
    "/usr/share/wb-mqtt-serial/wb-mqtt-serial-rpc-port-load-request.schema.json";
47
const auto RPC_PORT_SETUP_REQUEST_SCHEMA_FULL_FILE_PATH =
48
    "/usr/share/wb-mqtt-serial/wb-mqtt-serial-rpc-port-setup-request.schema.json";
49
const auto RPC_PORT_SCAN_REQUEST_SCHEMA_FULL_FILE_PATH =
50
    "/usr/share/wb-mqtt-serial/wb-mqtt-serial-rpc-port-scan-request.schema.json";
51
const auto RPC_DEVICE_LOAD_CONFIG_REQUEST_SCHEMA_FULL_FILE_PATH =
52
    "/usr/share/wb-mqtt-serial/wb-mqtt-serial-rpc-device-load-config-request.schema.json";
53
const auto RPC_DEVICE_LOAD_REQUEST_SCHEMA_FULL_FILE_PATH =
54
    "/usr/share/wb-mqtt-serial/wb-mqtt-serial-rpc-device-load-request.schema.json";
55
const auto RPC_DEVICE_SET_REQUEST_SCHEMA_FULL_FILE_PATH =
56
    "/usr/share/wb-mqtt-serial/wb-mqtt-serial-rpc-device-set-request.schema.json";
57
const auto RPC_DEVICE_PROBE_REQUEST_SCHEMA_FULL_FILE_PATH =
58
    "/usr/share/wb-mqtt-serial/wb-mqtt-serial-rpc-device-probe-request.schema.json";
59
const auto CONFED_COMMON_JSON_SCHEMA_FULL_FILE_PATH =
60
    "/usr/share/wb-mqtt-serial/wb-mqtt-serial-confed-common.schema.json";
61
const auto DEVICE_GROUP_NAMES_JSON_FULL_FILE_PATH = "/usr/share/wb-mqtt-serial/groups.json";
62
const auto PROTOCOL_SCHEMAS_DIR = "/usr/share/wb-mqtt-serial/protocols";
63

64
const auto SERIAL_DRIVER_STOP_TIMEOUT_S = chrono::seconds(60);
65

66
namespace
67
{
68
    void PrintStartupInfo()
×
69
    {
70
        string commit(XSTR(WBMQTT_COMMIT));
×
71
        cout << APP_NAME << " " << XSTR(WBMQTT_VERSION);
×
72
        if (!commit.empty()) {
×
73
            cout << " git " << commit;
×
74
        }
75
        cout << endl;
×
76
    }
77

78
    void PrintUsage()
×
79
    {
80
        cout << "Usage:" << endl
×
81
             << " " << APP_NAME << " [options]" << endl
×
82
             << "Options:" << endl
×
83
             << "  -d       level     enable debugging output:" << endl
×
84
             << "                       1 - serial only;" << endl
×
85
             << "                       2 - mqtt only;" << endl
×
86
             << "                       3 - both;" << endl
×
87
             << "                       negative values - silent mode (-1, -2, -3))" << endl
×
88
             << "  -c       config    config file" << endl
×
89
             << "  -p       port      MQTT broker port (default: 1883)" << endl
×
90
             << "  -h, -H   IP        MQTT broker IP (default: localhost)" << endl
×
91
             << "  -u       user      MQTT user (optional)" << endl
×
92
             << "  -P       password  MQTT user password (optional)" << endl
×
93
             << "  -T       prefix    MQTT topic prefix (optional)" << endl
×
94
             << "  -J                 Make /etc/wb-mqtt-serial.conf from wb-mqtt-confed output" << endl
×
95
             << "  -G       options   Generate device template. Type \"-G help\" for options description" << endl
×
96
             << "  -v                 Print the version" << endl;
×
97
    }
98

99
    /**
100
     * @brief Create Json::StreamWriter with defined parameters
101
     *
102
     * @param indentation - a string that is added before lines as indentation, "" - no indentation
103
     * @param commentStyle - "All" (write comments) or "None" (do not write comments)
104
     */
105
    unique_ptr<Json::StreamWriter> MakeJsonWriter(const std::string& indentation, const std::string& commentStyle)
×
106
    {
107
        Json::StreamWriterBuilder builder;
×
108
        builder["commentStyle"] = commentStyle;
×
109
        builder["indentation"] = indentation;
×
110
        builder["precision"] = 15;
×
111
        return unique_ptr<Json::StreamWriter>(builder.newStreamWriter());
×
112
    }
113

114
    pair<shared_ptr<Json::Value>, PTemplateMap> LoadTemplates()
×
115
    {
116
        auto commonDeviceSchema =
117
            make_shared<Json::Value>(WBMQTT::JSON::Parse(CONFED_COMMON_JSON_SCHEMA_FULL_FILE_PATH));
×
118
        auto templates = make_shared<TTemplateMap>(
119
            LoadConfigTemplatesSchema(TEMPLATES_JSON_SCHEMA_FULL_FILE_PATH, *commonDeviceSchema));
×
120
        templates->AddTemplatesDir(TEMPLATES_DIR);
×
121
        templates->AddTemplatesDir(USER_TEMPLATES_DIR);
×
122
        return {commonDeviceSchema, templates};
×
123
    }
124

125
    void ConfedToConfig()
×
126
    {
127
        try {
128
            PTemplateMap templates;
×
129
            std::tie(std::ignore, templates) = LoadTemplates();
×
130
            MakeJsonWriter("  ", "None")->write(MakeConfigFromConfed(std::cin, *templates), &cout);
×
131
        } catch (const exception& e) {
×
132
            LOG(Error) << e.what();
×
133
            exit(EXIT_FAILURE);
×
134
        }
135
    }
136

137
    void SetDebugLevel(const char* optarg)
×
138
    {
139
        try {
140
            auto debugLevel = stoi(optarg);
×
141
            switch (debugLevel) {
×
142
                case 0:
×
143
                    return;
×
144
                case -1:
×
145
                    Info.SetEnabled(false);
×
146
                    return;
×
147

148
                case -2:
×
149
                    WBMQTT::Info.SetEnabled(false);
×
150
                    return;
×
151

152
                case -3:
×
153
                    WBMQTT::Info.SetEnabled(false);
×
154
                    Info.SetEnabled(false);
×
155
                    return;
×
156

157
                case 1:
×
158
                    Debug.SetEnabled(true);
×
159
                    return;
×
160

161
                case 2:
×
162
                    WBMQTT::Debug.SetEnabled(true);
×
163
                    return;
×
164

165
                case 3:
×
166
                    WBMQTT::Debug.SetEnabled(true);
×
167
                    Debug.SetEnabled(true);
×
168
                    return;
×
169
            }
170
        } catch (...) {
×
171
        }
172
        cout << "Invalid -d parameter value " << optarg << endl;
×
173
        PrintUsage();
×
174
        exit(2);
×
175
    }
176

177
    void ParseCommadLine(int argc, char* argv[], WBMQTT::TMosquittoMqttConfig& mqttConfig, string& customConfig)
×
178
    {
179
        int c;
180

181
        while ((c = getopt(argc, argv, "d:c:h:H:p:u:P:T:jJG:v")) != -1) {
×
182
            switch (c) {
×
183
                case 'd':
×
184
                    SetDebugLevel(optarg);
×
185
                    break;
×
186
                case 'c':
×
187
                    customConfig = optarg;
×
188
                    break;
×
189
                case 'p':
×
190
                    mqttConfig.Port = stoi(optarg);
×
191
                    break;
×
192
                case 'h':
×
193
                case 'H': // backward compatibility
194
                    mqttConfig.Host = optarg;
×
195
                    break;
×
196
                case 'T':
×
197
                    mqttConfig.Prefix = optarg;
×
198
                    break;
×
199
                case 'u':
×
200
                    mqttConfig.User = optarg;
×
201
                    break;
×
202
                case 'P':
×
203
                    mqttConfig.Password = optarg;
×
204
                    break;
×
205
                    exit(EXIT_SUCCESS);
206
                case 'J': // make config JSON from confed's JSON
×
207
                    ConfedToConfig();
×
208
                    exit(EXIT_SUCCESS);
×
209
                case 'G':
×
210
                    GenerateDeviceTemplate(APP_NAME, USER_TEMPLATES_DIR, optarg);
×
211
                    exit(EXIT_SUCCESS);
×
212
                case 'v':
×
213
                    PrintStartupInfo();
×
214
                    exit(EXIT_SUCCESS);
×
215
                case '?':
×
216
                default:
217
                    PrintStartupInfo();
×
218
                    PrintUsage();
×
219
                    exit(2);
×
220
            }
221
        }
222

223
        if (optind < argc) {
×
224
            for (int index = optind; index < argc; ++index) {
×
225
                cout << "Skipping unknown argument " << argv[index] << endl;
×
226
            }
227
        }
228
    }
229

230
    void HandleTemplateChangeEvent(TTemplateMap& templates,
×
231
                                   TDevicesConfedSchemasMap& confedSchemasMap,
232
                                   const std::string& fileName,
233
                                   TFilesWatcher::TEvent event)
234
    {
235
        if (event == TFilesWatcher::TEvent::CloseWrite) {
×
236
            LOG(Debug) << fileName << " changed. Reloading template";
×
237
            try {
238
                auto updatedTypes = templates.UpdateTemplate(fileName);
×
239
                for (const auto& deviceType: updatedTypes) {
×
240
                    confedSchemasMap.InvalidateCache(deviceType);
×
241
                }
242
            } catch (const exception& e) {
×
243
                LOG(Error) << "Failed to reload template: " << e.what();
×
244
            }
245
            return;
×
246
        }
247
        if (event == TFilesWatcher::TEvent::Delete) {
×
248
            LOG(Debug) << fileName << " deleted";
×
249
            confedSchemasMap.InvalidateCache(templates.DeleteTemplate(fileName));
×
250
        }
251
    }
252
}
253

254
int main(int argc, char* argv[])
×
255
{
256
    WBMQTT::TMosquittoMqttConfig mqttConfig;
×
257
    string configFilename(CONFIG_FULL_FILE_PATH);
×
258

259
    WBMQTT::SignalHandling::Handle({SIGINT, SIGTERM});
×
260
    WBMQTT::SignalHandling::OnSignals({SIGINT, SIGTERM}, [&] { WBMQTT::SignalHandling::Stop(); });
×
261
    WBMQTT::SetThreadName(APP_NAME);
×
262

263
    ParseCommadLine(argc, argv, mqttConfig, configFilename);
×
264

265
    TSerialDeviceFactory deviceFactory;
×
266
    RegisterProtocols(deviceFactory);
×
267

268
    shared_ptr<Json::Value> commonDeviceSchema;
×
269
    PTemplateMap templates;
×
270
    std::tie(commonDeviceSchema, templates) = LoadTemplates();
×
271
    TDevicesConfedSchemasMap confedSchemasMap(*templates, deviceFactory, *commonDeviceSchema);
×
272
    TProtocolConfedSchemasMap protocolSchemasMap(PROTOCOL_SCHEMAS_DIR, *commonDeviceSchema);
×
273
    auto portsSchema = WBMQTT::JSON::Parse(PORTS_JSON_SCHEMA_FULL_FILE_PATH);
×
274

275
    TFilesWatcher watcher({TEMPLATES_DIR, USER_TEMPLATES_DIR}, [&](std::string fileName, TFilesWatcher::TEvent event) {
×
276
        HandleTemplateChangeEvent(*templates, confedSchemasMap, fileName, event);
×
277
    });
×
278

279
    try {
280
        if (mqttConfig.Id.empty())
×
281
            mqttConfig.Id = driverName;
×
282

283
        auto mqtt = WBMQTT::NewMosquittoMqttClient(mqttConfig);
×
284

285
        auto rpcServer(WBMQTT::NewMqttRpcServer(mqtt, APP_NAME));
×
286

287
        TRPCConfigHandler rpcConfigHandler(configFilename,
288
                                           portsSchema,
289
                                           templates,
290
                                           confedSchemasMap,
291
                                           protocolSchemasMap,
292
                                           WBMQTT::JSON::Parse(DEVICE_GROUP_NAMES_JSON_FULL_FILE_PATH),
×
293
                                           rpcServer);
×
294

295
        PRPCConfig rpcConfig = std::make_shared<TRPCConfig>();
×
296
        PHandlerConfig handlerConfig;
×
297

298
        try {
299
            handlerConfig = LoadConfig(configFilename,
×
300
                                       deviceFactory,
301
                                       *commonDeviceSchema,
×
302
                                       *templates,
×
303
                                       rpcConfig,
304
                                       portsSchema,
305
                                       protocolSchemasMap);
×
306
        } catch (const exception& e) {
×
307
            LOG(Error) << e.what();
×
308
        }
309

310
        PMQTTSerialDriver serialDriver;
×
311
        TRPCDeviceParametersCache parametersCache;
×
312

313
        if (handlerConfig) {
×
314
            if (handlerConfig->Debug) {
×
315
                Debug.SetEnabled(true);
×
316
            }
317

318
            auto backend = WBMQTT::NewDriverBackend(mqtt);
×
UNCOV
319
            auto driver = WBMQTT::NewDriver(WBMQTT::TDriverArgs{}
×
UNCOV
320
                                                .SetId(driverName)
×
321
                                                .SetBackend(backend)
×
322
                                                .SetUseStorage(true)
×
323
                                                .SetReownUnknownDevices(true)
×
NEW
324
                                                .SetStoragePath(LIBWBMQTT_DB_FULL_FILE_PATH));
×
325

326
            driver->StartLoop();
×
327
            WBMQTT::SignalHandling::OnSignals({SIGINT, SIGTERM}, [=] {
×
328
                driver->StopLoop();
×
329
                driver->Close();
×
330
            });
×
331

332
            driver->WaitForReady();
×
333
            serialDriver = make_shared<TMQTTSerialDriver>(driver, handlerConfig);
×
334
            parametersCache.RegisterCallbacks(handlerConfig);
×
335
        }
336

337
        TSerialClientTaskRunner serialClientTaskRunner(serialDriver);
×
338
        auto rpcPortHandler = std::make_shared<TRPCPortHandler>(RPC_PORT_LOAD_REQUEST_SCHEMA_FULL_FILE_PATH,
339
                                                                RPC_PORT_SETUP_REQUEST_SCHEMA_FULL_FILE_PATH,
340
                                                                RPC_PORT_SCAN_REQUEST_SCHEMA_FULL_FILE_PATH,
341
                                                                rpcConfig,
342
                                                                serialClientTaskRunner,
343
                                                                parametersCache,
344
                                                                rpcServer);
×
345
        auto rpcDeviceHandler =
346
            std::make_shared<TRPCDeviceHandler>(RPC_DEVICE_LOAD_CONFIG_REQUEST_SCHEMA_FULL_FILE_PATH,
347
                                                RPC_DEVICE_LOAD_REQUEST_SCHEMA_FULL_FILE_PATH,
348
                                                RPC_DEVICE_SET_REQUEST_SCHEMA_FULL_FILE_PATH,
349
                                                RPC_DEVICE_PROBE_REQUEST_SCHEMA_FULL_FILE_PATH,
350
                                                deviceFactory,
351
                                                templates,
352
                                                serialClientTaskRunner,
353
                                                parametersCache,
354
                                                rpcServer);
×
355

356
        if (serialDriver) {
×
357
            serialDriver->Start();
×
358
        } else {
359
            mqtt->Start();
×
360
        }
361
        rpcServer->Start();
×
362

363
        WBMQTT::SignalHandling::OnSignals({SIGINT, SIGTERM}, [=] {
×
364
            rpcServer->Stop();
×
365
            if (serialDriver) {
×
366
                serialDriver->Stop();
×
367
            } else {
368
                mqtt->Stop();
×
369
            }
370
        });
×
371
        WBMQTT::SignalHandling::SetOnTimeout(SERIAL_DRIVER_STOP_TIMEOUT_S, [&] {
×
372
            LOG(Error) << "Driver takes too long to stop. Exiting.";
×
373
            exit(EXIT_FAILURE);
×
374
        });
×
375
        WBMQTT::SignalHandling::Start();
×
376
        WBMQTT::SignalHandling::Wait();
×
377
    } catch (const exception& e) {
×
378
        LOG(Error) << "FATAL: " << e.what();
×
379
        return 1;
×
380
    }
381
    return 0;
×
382
}
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