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

wirenboard / wb-mqtt-serial / 666

08 Jul 2025 11:10AM UTC coverage: 73.854% (+0.1%) from 73.706%
666

push

github

web-flow
Port handling refactoring (#960)

Pass port by reference when needed instead of storing it in every device

6444 of 9057 branches covered (71.15%)

526 of 700 new or added lines in 56 files covered. (75.14%)

6 existing lines in 5 files now uncovered.

12341 of 16710 relevant lines covered (73.85%)

305.53 hits per line

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

0.0
/src/rpc/rpc_port_scan_serial_client_task.cpp
1
#include "rpc_port_scan_serial_client_task.h"
2
#include "modbus_base.h"
3
#include "modbus_common.h"
4
#include "rpc_helpers.h"
5
#include "serial_port.h"
6
#include "wb_registers.h"
7
#include <regex>
8

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

11
template<> bool inline WBMQTT::JSON::Is<ModbusExt::TModbusExtCommand>(const Json::Value& value)
×
12
{
13
    return value.isUInt();
×
14
}
15

16
template<> inline ModbusExt::TModbusExtCommand WBMQTT::JSON::As<ModbusExt::TModbusExtCommand>(const Json::Value& value)
×
17
{
18
    return ModbusExt::TModbusExtCommand(value.asUInt());
×
19
}
20

21
namespace
22
{
23
    const std::string READ_FW_VERSION_ERROR_ID = "com.wb.device_manager.device.read_fw_version_error";
24
    const std::string READ_FW_SIGNATURE_ERROR_ID = "com.wb.device_manager.device.read_fw_signature_error";
25
    const std::string READ_DEVICE_SIGNATURE_ERROR_ID = "com.wb.device_manager.device.read_device_signature_error";
26
    const std::string READ_SERIAL_PARAMS_ERROR_ID = "com.wb.device_manager.device.read_serial_params_error";
27
    const std::string READ_SN_ERROR_ID = "com.wb.device_manager.device.read_sn_error";
28

29
    uint32_t GetSnFromRegister(const std::string& deviceModel, uint32_t sn)
×
30
    {
31
        if (std::regex_match(deviceModel, std::regex("^MAP[0-9]{1,2}.*"))) {
×
32
            return sn & 0x00FFFFFF;
×
33
        }
34
        return sn;
×
35
    }
36

37
    std::string GetParityChar(uint64_t parity)
×
38
    {
39
        const std::unordered_map<uint64_t, std::string> parityMap = {{0, "N"}, {1, "O"}, {2, "E"}};
×
40
        auto it = parityMap.find(parity);
×
41
        return (it != parityMap.end() ? it->second : "U");
×
42
    }
43

44
    void AppendError(Json::Value& errorsJson, const std::string& id, const std::string& message)
×
45
    {
46
        Json::Value errorJson;
×
47
        errorJson["id"] = id;
×
48
        errorJson["message"] = message;
×
49
        errorsJson.append(errorJson);
×
50
    }
51

52
    std::string GetDeviceSignature(RpcPortScan::TRegisterReader& reader)
×
53
    {
54
        try {
55
            return reader.Read<std::string>(WbRegisters::DEVICE_MODEL_EX_REGISTER_NAME);
×
56
        } catch (const std::exception& e) {
×
57
            return reader.Read<std::string>(WbRegisters::DEVICE_MODEL_REGISTER_NAME);
×
58
        }
59
    }
60

61
    Json::Value GetDeviceDetails(RpcPortScan::TRegisterReader& reader,
×
62
                                 uint8_t slaveId,
63
                                 uint32_t snFromRegister,
64
                                 const std::list<PSerialDevice>& polledDevices)
65
    {
66
        Json::Value errorsJson(Json::arrayValue);
×
67
        Json::Value deviceJson;
×
68

69
        std::string stringSn = std::to_string(snFromRegister);
×
70
        try {
71
            deviceJson["device_signature"] = GetDeviceSignature(reader);
×
72
            stringSn = std::to_string(GetSnFromRegister(deviceJson["device_signature"].asString(), snFromRegister));
×
73
        } catch (const std::exception& e) {
×
74
            AppendError(errorsJson, READ_DEVICE_SIGNATURE_ERROR_ID, e.what());
×
75
        }
76
        deviceJson["sn"] = stringSn;
×
77

78
        try {
79
            deviceJson["fw_signature"] = reader.Read<std::string>(WbRegisters::FW_SIGNATURE_REGISTER_NAME);
×
80
        } catch (const std::exception& e) {
×
81
            AppendError(errorsJson, READ_FW_SIGNATURE_ERROR_ID, e.what());
×
82
        }
83

84
        Json::Value cfgJson;
×
85
        cfgJson["slave_id"] = slaveId;
×
86
        cfgJson["data_bits"] = 8;
×
87
        std::string serialParamsReadError;
×
88
        try {
89
            cfgJson["baud_rate"] = reader.Read<uint64_t>(WbRegisters::BAUD_RATE_REGISTER_NAME) * 100;
×
90
        } catch (const std::exception& e) {
×
91
            serialParamsReadError = e.what();
×
92
        }
93
        try {
94
            cfgJson["stop_bits"] = reader.Read<uint64_t>(WbRegisters::STOP_BITS_REGISTER_NAME);
×
95
        } catch (const std::exception& e) {
×
96
            serialParamsReadError = e.what();
×
97
        }
98
        try {
99
            cfgJson["parity"] = GetParityChar(reader.Read<uint64_t>(WbRegisters::PARITY_REGISTER_NAME));
×
100
        } catch (const std::exception& e) {
×
101
            serialParamsReadError = e.what();
×
102
        }
103
        if (!serialParamsReadError.empty()) {
×
104
            AppendError(errorsJson, READ_SERIAL_PARAMS_ERROR_ID, serialParamsReadError);
×
105
        }
106
        deviceJson["cfg"] = cfgJson;
×
107

108
        try {
109
            Json::Value fwJson;
×
110
            fwJson["version"] = reader.Read<std::string>(WbRegisters::FW_VERSION_REGISTER_NAME);
×
111
            deviceJson["fw"] = fwJson;
×
112
        } catch (const std::exception& e) {
×
113
            AppendError(errorsJson, READ_FW_VERSION_ERROR_ID, e.what());
×
114
        }
115

116
        auto stringSlaveId = std::to_string(slaveId);
×
117
        for (const auto& polledDevice: polledDevices) {
×
118
            if (polledDevice->DeviceConfig()->SlaveId == stringSlaveId && polledDevice->Protocol()->IsModbus()) {
×
119
                auto snRegister = polledDevice->GetSnRegister();
×
120
                if (snRegister) {
×
121
                    if (snRegister->GetErrorState().test(TRegister::ReadError) ||
×
122
                        snRegister->GetValue().GetType() == TRegisterValue::ValueType::Undefined)
×
123
                    {
124
                        auto registerRange = polledDevice->CreateRegisterRange();
×
NEW
125
                        registerRange->Add(reader.GetPort(), snRegister, std::chrono::milliseconds::max());
×
NEW
126
                        polledDevice->ReadRegisterRange(reader.GetPort(), registerRange);
×
127
                    }
128
                    if (snRegister->GetValue().GetType() == TRegisterValue::ValueType::Integer &&
×
129
                        snFromRegister == snRegister->GetValue().Get<uint64_t>())
×
130
                    {
131
                        deviceJson["configured_device_type"] = polledDevice->DeviceConfig()->DeviceType;
×
132
                        break;
×
133
                    }
134
                }
135
            }
136
        }
137

138
        if (!errorsJson.empty()) {
×
139
            deviceJson["errors"] = errorsJson;
×
140
        }
141

142
        return deviceJson;
×
143
    }
144

145
    Json::Value GetScannedDeviceDetails(TPort& port,
×
146
                                        const ModbusExt::TScannedDevice& scannedDevice,
147
                                        ModbusExt::TModbusExtCommand modbusExtCommand,
148
                                        const std::list<PSerialDevice>& polledDevices)
149
    {
150
        ModbusExt::TModbusTraits modbusTraits;
×
151
        modbusTraits.SetModbusExtCommand(modbusExtCommand);
×
152
        modbusTraits.SetSn(scannedDevice.Sn);
×
153
        RpcPortScan::TRegisterReader reader(port, modbusTraits, scannedDevice.SlaveId);
×
154
        return ::GetDeviceDetails(reader, scannedDevice.SlaveId, scannedDevice.Sn, polledDevices);
×
155
    }
156
}
157

158
Json::Value RpcPortScan::GetDeviceDetails(RpcPortScan::TRegisterReader& reader,
×
159
                                          uint8_t slaveId,
160
                                          const std::list<PSerialDevice>& polledDevices)
161
{
162
    uint64_t sn;
163
    try {
164
        sn = reader.Read<uint64_t>(WbRegisters::SN_REGISTER_NAME);
×
165
    } catch (const TResponseTimeoutException& e) {
×
166
        return Json::Value(Json::objectValue);
×
167
    } catch (const Modbus::TErrorBase& e) {
×
168
        Json::Value errorsJson(Json::arrayValue);
×
169
        AppendError(errorsJson, READ_SN_ERROR_ID, e.what());
×
170
        Json::Value deviceJson;
×
171
        deviceJson["errors"] = errorsJson;
×
172
        return deviceJson;
×
173
    }
174
    return ::GetDeviceDetails(reader, slaveId, sn, polledDevices);
×
175
}
176

177
RpcPortScan::TRegisterReader::TRegisterReader(TPort& port, Modbus::IModbusTraits& modbusTraits, uint8_t slaveId)
×
178
    : ModbusTraits(modbusTraits),
179
      FrameTimeout(
180
          std::chrono::ceil<std::chrono::milliseconds>(port.GetSendTimeBytes(Modbus::STANDARD_FRAME_TIMEOUT_BYTES))),
×
181
      Port(port),
182
      SlaveId(slaveId)
×
183
{}
184

185
PRPCPortScanRequest ParseRPCPortScanRequest(const Json::Value& request)
×
186
{
187
    PRPCPortScanRequest res = std::make_shared<TRPCPortScanRequest>();
×
188
    res->SerialPortSettings = ParseRPCSerialPortSettings(request);
×
189
    WBMQTT::JSON::Get(request, "total_timeout", res->TotalTimeout);
×
190
    WBMQTT::JSON::Get(request, "command", res->ModbusExtCommand);
×
191
    return res;
×
192
}
193

194
void ExecRPCPortScanRequest(TPort& port, PRPCPortScanRequest rpcRequest, const std::list<PSerialDevice>& polledDevices)
×
195
{
196
    if (!rpcRequest->OnResult) {
×
197
        return;
×
198
    }
199

200
    port.SkipNoise();
×
201

202
    Json::Value replyJSON;
×
203
    std::vector<ModbusExt::TScannedDevice> scannedDevices;
×
204
    try {
205
        ModbusExt::Scan(port, rpcRequest->ModbusExtCommand, scannedDevices);
×
206
    } catch (const std::exception& e) {
×
207
        replyJSON["error"] = e.what();
×
208
    }
209

210
    replyJSON["devices"] = Json::Value(Json::arrayValue);
×
211
    for (const auto& device: scannedDevices) {
×
212
        replyJSON["devices"].append(GetScannedDeviceDetails(port, device, rpcRequest->ModbusExtCommand, polledDevices));
×
213
    }
214

215
    rpcRequest->OnResult(replyJSON);
×
216
}
217

218
TRPCPortScanSerialClientTask::TRPCPortScanSerialClientTask(const Json::Value& request,
×
219
                                                           WBMQTT::TMqttRpcServer::TResultCallback onResult,
220
                                                           WBMQTT::TMqttRpcServer::TErrorCallback onError)
×
221
    : Request(ParseRPCPortScanRequest(request))
×
222
{
223
    Request->OnResult = onResult;
×
224
    Request->OnError = onError;
×
225
    ExpireTime = std::chrono::steady_clock::now() + Request->TotalTimeout;
×
226
}
227

228
ISerialClientTask::TRunResult TRPCPortScanSerialClientTask::Run(PPort port,
×
229
                                                                TSerialClientDeviceAccessHandler& lastAccessedDevice,
230
                                                                const std::list<PSerialDevice>& polledDevices)
231
{
232
    if (std::chrono::steady_clock::now() > ExpireTime) {
×
233
        if (Request->OnError) {
×
234
            Request->OnError(WBMQTT::E_RPC_REQUEST_TIMEOUT, "RPC request timeout");
×
235
        }
236
        return ISerialClientTask::TRunResult::OK;
×
237
    }
238

239
    try {
240
        if (!port->IsOpen()) {
×
241
            port->Open();
×
242
        }
NEW
243
        lastAccessedDevice.PrepareToAccess(*port, nullptr);
×
244
        TSerialPortSettingsGuard settingsGuard(port, Request->SerialPortSettings);
×
245
        ExecRPCPortScanRequest(*port, Request, polledDevices);
×
246
    } catch (const std::exception& error) {
×
247
        if (Request->OnError) {
×
248
            Request->OnError(WBMQTT::E_RPC_SERVER_ERROR, std::string("Port IO error: ") + error.what());
×
249
        }
250
    }
251
    return ISerialClientTask::TRunResult::OK;
×
252
}
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