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

wirenboard / wb-mqtt-serial / 673

15 Jul 2025 07:07AM UTC coverage: 73.028% (-0.8%) from 73.863%
673

push

github

web-flow
Add device/Load and device/Set RPC

6463 of 9217 branches covered (70.12%)

51 of 322 new or added lines in 13 files covered. (15.84%)

7 existing lines in 2 files now uncovered.

12368 of 16936 relevant lines covered (73.03%)

373.64 hits per line

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

14.53
/src/rpc/rpc_device_handler.cpp
1
#include "rpc_device_handler.h"
2
#include "rpc_device_load_config_task.h"
3
#include "rpc_device_load_task.h"
4
#include "rpc_device_probe_task.h"
5
#include "rpc_device_set_task.h"
6
#include "rpc_helpers.h"
7

8
#define LOG(logger) ::logger.Log() << "[RPC] "
9

UNCOV
10
void TRPCDeviceParametersCache::RegisterCallbacks(PHandlerConfig handlerConfig)
×
11
{
12
    for (const auto& portConfig: handlerConfig->PortConfigs) {
×
13
        for (const auto& device: portConfig->Devices) {
×
14
            std::string id = GetId(*portConfig->Port, device->Device->DeviceConfig()->SlaveId);
×
15
            device->Device->AddOnConnectionStateChangedCallback([this, id](PSerialDevice device) {
×
16
                if (device->GetConnectionState() == TDeviceConnectionState::DISCONNECTED) {
×
17
                    Remove(id);
×
18
                }
19
            });
×
20
        }
21
    }
22
}
23

24
std::string TRPCDeviceParametersCache::GetId(const TPort& port, const std::string& slaveId) const
×
25
{
26
    return port.GetDescription(false) + ":" + slaveId;
×
27
}
28

29
void TRPCDeviceParametersCache::Add(const std::string& id, const Json::Value& value)
×
30
{
31
    std::unique_lock lock(Mutex);
×
32
    DeviceParameters[id] = value;
×
33
}
34

35
void TRPCDeviceParametersCache::Remove(const std::string& id)
×
36
{
37
    std::unique_lock lock(Mutex);
×
38
    DeviceParameters.erase(id);
×
39
}
40

41
bool TRPCDeviceParametersCache::Contains(const std::string& id) const
×
42
{
43
    std::unique_lock lock(Mutex);
×
44
    return DeviceParameters.find(id) != DeviceParameters.end();
×
45
}
46

47
const Json::Value& TRPCDeviceParametersCache::Get(const std::string& id, const Json::Value& defaultValue) const
×
48
{
49
    std::unique_lock lock(Mutex);
×
50
    auto it = DeviceParameters.find(id);
×
51
    return it != DeviceParameters.end() ? it->second : defaultValue;
×
52
};
53

54
TRPCDeviceHelper::TRPCDeviceHelper(const Json::Value& request,
×
55
                                   const TSerialDeviceFactory& deviceFactory,
56
                                   PTemplateMap templates,
57
                                   TSerialClientTaskRunner& serialClientTaskRunner)
×
58
{
59
    auto params = serialClientTaskRunner.GetSerialClientParams(request);
×
60
    if (params.Device == nullptr) {
×
61
        DeviceTemplate = templates->GetTemplate(request["device_type"].asString());
×
62
        auto config = std::make_shared<TDeviceConfig>("RPC Device",
63
                                                      request["slave_id"].asString(),
×
64
                                                      DeviceTemplate->GetProtocol());
×
65
        if (DeviceTemplate->GetProtocol() == "modbus") {
×
66
            config->MaxRegHole = Modbus::MAX_HOLE_CONTINUOUS_16_BIT_REGISTERS;
×
67
            config->MaxBitHole = Modbus::MAX_HOLE_CONTINUOUS_1_BIT_REGISTERS;
×
68
            config->MaxReadRegisters = Modbus::MAX_READ_REGISTERS;
×
69
        }
70
        ProtocolParams = deviceFactory.GetProtocolParams(DeviceTemplate->GetProtocol());
×
71
        Device = ProtocolParams.factory->CreateDevice(DeviceTemplate->GetTemplate(), config, ProtocolParams.protocol);
×
72
    } else {
73
        Device = params.Device;
×
74
        DeviceTemplate = templates->GetTemplate(Device->DeviceConfig()->DeviceType);
×
75
        ProtocolParams = deviceFactory.GetProtocolParams(DeviceTemplate->GetProtocol());
×
76
        DeviceFromConfig = true;
×
77
    }
78
    if (DeviceTemplate->WithSubdevices()) {
×
79
        throw TRPCException("Device \"" + DeviceTemplate->Type + "\" is not supported by this RPC",
×
80
                            TRPCResultCode::RPC_WRONG_PARAM_VALUE);
×
81
    }
82
}
83

NEW
84
TRPCDeviceRequest::TRPCDeviceRequest(const TDeviceProtocolParams& protocolParams,
×
85
                                     PSerialDevice device,
86
                                     PDeviceTemplate deviceTemplate,
NEW
87
                                     bool deviceFromConfig)
×
88
    : ProtocolParams(protocolParams),
89
      Device(device),
90
      DeviceTemplate(deviceTemplate),
NEW
91
      DeviceFromConfig(deviceFromConfig)
×
92
{
NEW
93
    Json::Value responseTimeout = DeviceTemplate->GetTemplate()["response_timeout_ms"];
×
NEW
94
    if (responseTimeout.isInt()) {
×
NEW
95
        ResponseTimeout = std::chrono::milliseconds(responseTimeout.asInt());
×
96
    }
97

NEW
98
    Json::Value frameTimeout = DeviceTemplate->GetTemplate()["frame_timeout_ms"];
×
NEW
99
    if (frameTimeout.isInt()) {
×
NEW
100
        FrameTimeout = std::chrono::milliseconds(frameTimeout.asInt());
×
101
    }
102
}
103

NEW
104
void TRPCDeviceRequest::ParseSettings(const Json::Value& request,
×
105
                                      WBMQTT::TMqttRpcServer::TResultCallback onResult,
106
                                      WBMQTT::TMqttRpcServer::TErrorCallback onError)
107
{
NEW
108
    SerialPortSettings = ParseRPCSerialPortSettings(request);
×
NEW
109
    WBMQTT::JSON::Get(request, "response_timeout", ResponseTimeout);
×
NEW
110
    WBMQTT::JSON::Get(request, "frame_timeout", FrameTimeout);
×
NEW
111
    WBMQTT::JSON::Get(request, "total_timeout", TotalTimeout);
×
NEW
112
    OnResult = onResult;
×
NEW
113
    OnError = onError;
×
114
}
115

UNCOV
116
TRPCDeviceHandler::TRPCDeviceHandler(const std::string& requestDeviceLoadConfigSchemaFilePath,
×
117
                                     const std::string& requestDeviceLoadSchemaFilePath,
118
                                     const std::string& requestDeviceSetSchemaFilePath,
119
                                     const std::string& requestDeviceProbeSchemaFilePath,
120
                                     const TSerialDeviceFactory& deviceFactory,
121
                                     PTemplateMap templates,
122
                                     TSerialClientTaskRunner& serialClientTaskRunner,
123
                                     TRPCDeviceParametersCache& parametersCache,
124
                                     WBMQTT::PMqttRpcServer rpcServer)
×
125
    : DeviceFactory(deviceFactory),
126
      RequestDeviceLoadConfigSchema(LoadRPCRequestSchema(requestDeviceLoadConfigSchemaFilePath, "device/LoadConfig")),
×
NEW
127
      RequestDeviceLoadSchema(LoadRPCRequestSchema(requestDeviceLoadSchemaFilePath, "device/Load")),
×
NEW
128
      RequestDeviceSetSchema(LoadRPCRequestSchema(requestDeviceSetSchemaFilePath, "device/Set")),
×
UNCOV
129
      RequestDeviceProbeSchema(LoadRPCRequestSchema(requestDeviceProbeSchemaFilePath, "device/Probe")),
×
130
      Templates(templates),
131
      SerialClientTaskRunner(serialClientTaskRunner),
132
      ParametersCache(parametersCache)
×
133
{
134
    rpcServer->RegisterAsyncMethod("device",
×
135
                                   "LoadConfig",
136
                                   std::bind(&TRPCDeviceHandler::LoadConfig,
×
137
                                             this,
×
138
                                             std::placeholders::_1,
139
                                             std::placeholders::_2,
140
                                             std::placeholders::_3));
×
NEW
141
    rpcServer->RegisterAsyncMethod("device",
×
142
                                   "Load",
NEW
143
                                   std::bind(&TRPCDeviceHandler::Load, //
×
NEW
144
                                             this,
×
145
                                             std::placeholders::_1,
146
                                             std::placeholders::_2,
NEW
147
                                             std::placeholders::_3));
×
NEW
148
    rpcServer->RegisterAsyncMethod("device",
×
149
                                   "Set",
NEW
150
                                   std::bind(&TRPCDeviceHandler::Set, //
×
NEW
151
                                             this,
×
152
                                             std::placeholders::_1,
153
                                             std::placeholders::_2,
NEW
154
                                             std::placeholders::_3));
×
UNCOV
155
    rpcServer->RegisterAsyncMethod("device",
×
156
                                   "Probe",
157
                                   std::bind(&TRPCDeviceHandler::Probe,
×
158
                                             this,
×
159
                                             std::placeholders::_1,
160
                                             std::placeholders::_2,
161
                                             std::placeholders::_3));
×
162
}
163

164
void TRPCDeviceHandler::LoadConfig(const Json::Value& request,
×
165
                                   WBMQTT::TMqttRpcServer::TResultCallback onResult,
166
                                   WBMQTT::TMqttRpcServer::TErrorCallback onError)
167
{
168
    ValidateRPCRequest(request, RequestDeviceLoadConfigSchema);
×
169
    try {
170
        auto helper = TRPCDeviceHelper(request, DeviceFactory, Templates, SerialClientTaskRunner);
×
171
        auto rpcRequest = ParseRPCDeviceLoadConfigRequest(request,
172
                                                          helper.ProtocolParams,
173
                                                          helper.Device,
174
                                                          helper.DeviceTemplate,
175
                                                          helper.DeviceFromConfig,
176
                                                          ParametersCache,
177
                                                          onResult,
178
                                                          onError);
×
NEW
179
        SerialClientTaskRunner.RunTask(request, std::make_shared<TRPCDeviceLoadConfigSerialClientTask>(rpcRequest));
×
NEW
180
    } catch (const TRPCException& e) {
×
NEW
181
        ProcessException(e, onError);
×
182
    }
183
}
184

NEW
185
void TRPCDeviceHandler::Load(const Json::Value& request,
×
186
                             WBMQTT::TMqttRpcServer::TResultCallback onResult,
187
                             WBMQTT::TMqttRpcServer::TErrorCallback onError)
188
{
NEW
189
    ValidateRPCRequest(request, RequestDeviceLoadSchema);
×
190
    try {
NEW
191
        auto helper = TRPCDeviceHelper(request, DeviceFactory, Templates, SerialClientTaskRunner);
×
192
        auto rpcRequest = ParseRPCDeviceLoadRequest(request,
193
                                                    helper.ProtocolParams,
194
                                                    helper.Device,
195
                                                    helper.DeviceTemplate,
196
                                                    helper.DeviceFromConfig,
197
                                                    onResult,
NEW
198
                                                    onError);
×
NEW
199
        SerialClientTaskRunner.RunTask(request, std::make_shared<TRPCDeviceLoadSerialClientTask>(rpcRequest));
×
NEW
200
    } catch (const TRPCException& e) {
×
NEW
201
        ProcessException(e, onError);
×
202
    }
203
}
204

NEW
205
void TRPCDeviceHandler::Set(const Json::Value& request,
×
206
                            WBMQTT::TMqttRpcServer::TResultCallback onResult,
207
                            WBMQTT::TMqttRpcServer::TErrorCallback onError)
208
{
NEW
209
    ValidateRPCRequest(request, RequestDeviceSetSchema);
×
210
    try {
NEW
211
        auto helper = TRPCDeviceHelper(request, DeviceFactory, Templates, SerialClientTaskRunner);
×
212
        auto rpcRequest = ParseRPCDeviceSetRequest(request,
213
                                                   helper.ProtocolParams,
214
                                                   helper.Device,
215
                                                   helper.DeviceTemplate,
216
                                                   helper.DeviceFromConfig,
217
                                                   onResult,
NEW
218
                                                   onError);
×
NEW
219
        SerialClientTaskRunner.RunTask(request, std::make_shared<TRPCDeviceSetSerialClientTask>(rpcRequest));
×
220
    } catch (const TRPCException& e) {
×
221
        ProcessException(e, onError);
×
222
    }
223
}
224

225
void TRPCDeviceHandler::Probe(const Json::Value& request,
×
226
                              WBMQTT::TMqttRpcServer::TResultCallback onResult,
227
                              WBMQTT::TMqttRpcServer::TErrorCallback onError)
228
{
229
    ValidateRPCRequest(request, RequestDeviceProbeSchema);
×
230
    try {
231
        SerialClientTaskRunner.RunTask(request,
×
232
                                       std::make_shared<TRPCDeviceProbeSerialClientTask>(request, onResult, onError));
×
233
    } catch (const TRPCException& e) {
×
234
        ProcessException(e, onError);
×
235
    }
236
}
237

238
TRPCRegisterList CreateRegisterList(const TDeviceProtocolParams& protocolParams,
4✔
239
                                    const PSerialDevice& device,
240
                                    const Json::Value& templateItems,
241
                                    const Json::Value& knownItems,
242
                                    const std::string& fwVersion)
243
{
244
    TRPCRegisterList registerList;
4✔
245
    for (auto it = templateItems.begin(); it != templateItems.end(); ++it) {
30✔
246
        const auto& item = *it;
26✔
247
        auto id = templateItems.isObject() ? it.key().asString() : item["id"].asString();
26✔
248
        bool duplicate = false;
26✔
249
        for (const auto& item: registerList) {
67✔
250
            if (item.first == id) {
42✔
251
                duplicate = true;
1✔
252
                break;
1✔
253
            }
254
        }
255
        if (duplicate || item["address"].isNull() || item["readonly"].asBool() || !knownItems[id].isNull()) {
26✔
256
            continue;
5✔
257
        }
258
        if (!fwVersion.empty()) {
21✔
259
            std::string fw = item["fw"].asString();
12✔
260
            if (!fw.empty() && util::CompareVersionStrings(fw, fwVersion) > 0) {
12✔
261
                continue;
4✔
262
            }
263
        }
264
        auto config = LoadRegisterConfig(item,
265
                                         *protocolParams.protocol->GetRegTypes(),
17✔
266
                                         std::string(),
34✔
267
                                         *protocolParams.factory,
17✔
268
                                         protocolParams.factory->GetRegisterAddressFactory().GetBaseRegisterAddress(),
17✔
269
                                         0);
34✔
270
        auto reg = std::make_shared<TRegister>(device, config.RegisterConfig);
17✔
271
        reg->SetAvailable(TRegisterAvailability::AVAILABLE);
17✔
272
        registerList.push_back(std::make_pair(id, reg));
17✔
273
    }
274
    return registerList;
4✔
275
}
276

NEW
277
void ReadRegisterList(TPort& port,
×
278
                      PSerialDevice device,
279
                      TRPCRegisterList& registerList,
280
                      Json::Value& result,
281
                      int maxRetries)
282
{
NEW
283
    if (registerList.size() == 0) {
×
NEW
284
        return;
×
285
    }
286
    TRegisterComparePredicate compare;
NEW
287
    std::sort(registerList.begin(),
×
288
              registerList.end(),
NEW
289
              [compare](std::pair<std::string, PRegister>& a, std::pair<std::string, PRegister>& b) {
×
NEW
290
                  return compare(b.second, a.second);
×
291
              });
292

NEW
293
    std::string error;
×
NEW
294
    for (int i = 0; i <= maxRetries; i++) {
×
295
        try {
NEW
296
            device->Prepare(port, TDevicePrepareMode::WITHOUT_SETUP);
×
NEW
297
            break;
×
NEW
298
        } catch (const TSerialDeviceException& e) {
×
NEW
299
            if (i == maxRetries) {
×
NEW
300
                error = std::string("Failed to prepare session: ") + e.what();
×
NEW
301
                LOG(Warn) << port.GetDescription() << " " << device->ToString() << ": " << error;
×
NEW
302
                throw TRPCException(error, TRPCResultCode::RPC_WRONG_PARAM_VALUE);
×
303
            }
304
        }
305
    }
306

NEW
307
    size_t index = 0;
×
NEW
308
    while (index < registerList.size() && error.empty()) {
×
NEW
309
        auto first = registerList[index].second;
×
NEW
310
        auto range = device->CreateRegisterRange();
×
NEW
311
        while (index < registerList.size() &&
×
NEW
312
               range->Add(port, registerList[index].second, std::chrono::milliseconds::max()))
×
313
        {
NEW
314
            ++index;
×
315
        }
NEW
316
        for (int i = 0; i <= maxRetries; ++i) {
×
317
            try {
NEW
318
                device->ReadRegisterRange(port, range, true);
×
NEW
319
                break;
×
NEW
320
            } catch (const TSerialDeviceException& e) {
×
NEW
321
                if (i == maxRetries) {
×
NEW
322
                    error = "Failed to read " + std::to_string(range->RegisterList().size()) +
×
NEW
323
                            " registers starting from <" + first->GetConfig()->ToString() + ">: " + e.what();
×
324
                }
325
            }
326
        }
327
    }
328

329
    try {
NEW
330
        device->EndSession(port);
×
NEW
331
    } catch (const TSerialDeviceException& e) {
×
NEW
332
        LOG(Warn) << port.GetDescription() << " " << device->ToString() << " unable to end session: " << e.what();
×
333
    }
334

NEW
335
    if (!error.empty()) {
×
NEW
336
        LOG(Warn) << port.GetDescription() << " " << device->ToString() << ": " << error;
×
NEW
337
        throw TRPCException(error, TRPCResultCode::RPC_WRONG_PARAM_VALUE);
×
338
    }
339

NEW
340
    for (size_t i = 0; i < registerList.size(); ++i) {
×
NEW
341
        auto& reg = registerList[i];
×
NEW
342
        result[reg.first] = RawValueToJSON(*reg.second->GetConfig(), reg.second->GetValue());
×
343
    }
344
}
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