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

wirenboard / wb-mqtt-serial / 2

29 Dec 2025 12:28PM UTC coverage: 76.817% (+4.0%) from 72.836%
2

Pull #1045

github

54aa0c
pgasheev
up changelog
Pull Request #1045: Fix firmware version in WB-M1W2 template

6873 of 9161 branches covered (75.02%)

12966 of 16879 relevant lines covered (76.82%)

1651.61 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 "port/serial_port.h"
22
#include "rpc/rpc_config.h"
23
#include "rpc/rpc_config_handler.h"
24
#include "rpc/rpc_device_handler.h"
25
#include "rpc/rpc_port_handler.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 RPC_DEVICE_SET_POLL_REQUEST_SCHEMA_FULL_FILE_PATH =
60
    "/usr/share/wb-mqtt-serial/wb-mqtt-serial-rpc-device-set-poll-request.schema.json";
61
const auto CONFED_COMMON_JSON_SCHEMA_FULL_FILE_PATH =
62
    "/usr/share/wb-mqtt-serial/wb-mqtt-serial-confed-common.schema.json";
63
const auto DEVICE_GROUP_NAMES_JSON_FULL_FILE_PATH = "/usr/share/wb-mqtt-serial/groups.json";
64
const auto PROTOCOL_SCHEMAS_DIR = "/usr/share/wb-mqtt-serial/protocols";
65

66
const auto SERIAL_DRIVER_STOP_TIMEOUT_S = chrono::seconds(60);
67

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

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

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

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

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

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

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

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

159
                case 1:
×
160
                    Debug.SetEnabled(true);
×
161
                    return;
×
162

163
                case 2:
×
164
                    WBMQTT::Debug.SetEnabled(true);
×
165
                    return;
×
166

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

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

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

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

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

256
int main(int argc, char* argv[])
×
257
{
258
    signal(SIGPIPE, SIG_IGN);
×
259

260
    WBMQTT::TMosquittoMqttConfig mqttConfig;
×
261
    string configFilename(CONFIG_FULL_FILE_PATH);
×
262

263
    WBMQTT::SignalHandling::Handle({SIGINT, SIGTERM});
×
264
    WBMQTT::SignalHandling::OnSignals({SIGINT, SIGTERM}, [&] { WBMQTT::SignalHandling::Stop(); });
×
265
    WBMQTT::SetThreadName(APP_NAME);
×
266

267
    ParseCommadLine(argc, argv, mqttConfig, configFilename);
×
268

269
    TSerialDeviceFactory deviceFactory;
×
270
    RegisterProtocols(deviceFactory);
×
271

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

279
    TFilesWatcher watcher({TEMPLATES_DIR, USER_TEMPLATES_DIR}, [&](std::string fileName, TFilesWatcher::TEvent event) {
×
280
        HandleTemplateChangeEvent(*templates, confedSchemasMap, fileName, event);
×
281
    });
×
282

283
    try {
284
        if (mqttConfig.Id.empty())
×
285
            mqttConfig.Id = driverName;
×
286

287
        auto mqtt = WBMQTT::NewMosquittoMqttClient(mqttConfig);
×
288

289
        auto rpcServer(WBMQTT::NewMqttRpcServer(mqtt, APP_NAME));
×
290

291
        TRPCConfigHandler rpcConfigHandler(configFilename,
292
                                           portsSchema,
293
                                           templates,
294
                                           confedSchemasMap,
295
                                           protocolSchemasMap,
296
                                           WBMQTT::JSON::Parse(DEVICE_GROUP_NAMES_JSON_FULL_FILE_PATH),
×
297
                                           rpcServer);
×
298

299
        PRPCConfig rpcConfig = std::make_shared<TRPCConfig>();
×
300
        PHandlerConfig handlerConfig;
×
301

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

314
        PMQTTSerialDriver serialDriver;
×
315
        TRPCDeviceParametersCache parametersCache;
×
316

317
        if (handlerConfig) {
×
318
            if (handlerConfig->Debug) {
×
319
                Debug.SetEnabled(true);
×
320
            }
321

322
            auto backend = WBMQTT::NewDriverBackend(mqtt);
×
323
            auto driver = WBMQTT::NewDriver(WBMQTT::TDriverArgs{}
×
324
                                                .SetId(driverName)
×
325
                                                .SetBackend(backend)
×
326
                                                .SetUseStorage(true)
×
327
                                                .SetReownUnknownDevices(true)
×
328
                                                .SetStoragePath(LIBWBMQTT_DB_FULL_FILE_PATH));
×
329

330
            driver->StartLoop();
×
331
            WBMQTT::SignalHandling::OnSignals({SIGINT, SIGTERM}, [=] {
×
332
                driver->StopLoop();
×
333
                driver->Close();
×
334
            });
×
335

336
            driver->WaitForReady();
×
337
            serialDriver = make_shared<TMQTTSerialDriver>(driver, handlerConfig);
×
338
            parametersCache.RegisterCallbacks(handlerConfig);
×
339
        }
340

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

361
        if (serialDriver) {
×
362
            serialDriver->Start();
×
363
        } else {
364
            mqtt->Start();
×
365
        }
366
        rpcServer->Start();
×
367

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