• 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

84.28
/src/serial_device.cpp
1
#include "serial_device.h"
2

3
#include "log.h"
4
#include <iostream>
5
#include <string.h>
6
#include <unistd.h>
7

8
#define LOG(logger) logger.Log() << "[serial device] "
9

10
IProtocol::IProtocol(const std::string& name, const TRegisterTypes& reg_types)
6,976✔
11
    : Name(name),
12
      RegTypes(std::make_shared<TRegisterTypeMap>(reg_types))
6,976✔
13
{}
6,976✔
14

15
const std::string& IProtocol::GetName() const
9,000✔
16
{
17
    return Name;
9,000✔
18
}
19

20
PRegisterTypeMap IProtocol::GetRegTypes() const
398✔
21
{
22
    return RegTypes;
398✔
23
}
24

25
bool IProtocol::IsModbus() const
×
26
{
27
    return false;
×
28
}
29

30
bool IProtocol::SupportsBroadcast() const
68✔
31
{
32
    return false;
68✔
33
}
34

35
TDeviceSetupItem::TDeviceSetupItem(PDeviceSetupItemConfig config, PSerialDevice device)
210✔
36
    : Name(config->GetName()),
210✔
37
      ParameterId(config->GetParameterId()),
210✔
38
      RawValue(config->GetRawValue()),
39
      HumanReadableValue(config->GetValue()),
210✔
40
      RegisterConfig(config->GetRegisterConfig()),
41
      Device(device)
840✔
42
{}
210✔
43

44
std::string TDeviceSetupItem::ToString()
120✔
45
{
46
    std::stringstream stream;
240✔
47
    auto reg = "<" + (Device ? Device->ToString() : "unknown device") + ":" + RegisterConfig->ToString() + ">";
360✔
48
    stream << "Init setup register \"" << Name << "\": " << reg << "<-- " << HumanReadableValue;
120✔
49
    if (RawValue.GetType() == TRegisterValue::ValueType::String) {
120✔
50
        stream << " (\"" << RawValue << "\")";
×
51
    } else {
52
        stream << " (0x" << std::hex << RawValue << ")";
120✔
53
    }
54
    return stream.str();
240✔
55
}
56

57
bool TDeviceSetupItemComparePredicate::operator()(const PDeviceSetupItem& a, const PDeviceSetupItem& b) const
418✔
58
{
59
    if (a->RegisterConfig->Type != b->RegisterConfig->Type) {
418✔
60
        return a->RegisterConfig->Type < b->RegisterConfig->Type;
18✔
61
    }
62
    auto compare = a->RegisterConfig->GetWriteAddress().Compare(b->RegisterConfig->GetWriteAddress());
400✔
63
    if (compare == 0) {
400✔
64
        return a->RegisterConfig->GetDataOffset() < b->RegisterConfig->GetDataOffset();
12✔
65
    }
66
    return compare < 0;
388✔
67
}
68

69
TUint32SlaveIdProtocol::TUint32SlaveIdProtocol(const std::string& name,
4,604✔
70
                                               const TRegisterTypes& reg_types,
71
                                               bool allowBroadcast)
4,604✔
72
    : IProtocol(name, reg_types),
73
      AllowBroadcast(allowBroadcast)
4,604✔
74
{}
4,604✔
75

76
bool TUint32SlaveIdProtocol::IsSameSlaveId(const std::string& id1, const std::string& id2) const
382✔
77
{
78
    return (TUInt32SlaveId(id1, AllowBroadcast) == TUInt32SlaveId(id2, AllowBroadcast));
382✔
79
}
80

81
bool TUint32SlaveIdProtocol::SupportsBroadcast() const
8✔
82
{
83
    return AllowBroadcast;
8✔
84
}
85

86
TSerialDevice::TSerialDevice(PDeviceConfig config, PProtocol protocol)
592✔
87
    : _DeviceConfig(config),
88
      _Protocol(protocol),
89
      LastSuccessfulCycle(),
90
      ConnectionState(TDeviceConnectionState::UNKNOWN),
91
      RemainingFailCycles(0),
92
      SupportsHoles(true),
93
      SporadicOnly(true)
592✔
94
{}
592✔
95

96
std::string TSerialDevice::ToString() const
1,840✔
97
{
98
    return Protocol()->GetName() + ":" + DeviceConfig()->SlaveId;
3,680✔
99
}
100

101
PRegisterRange TSerialDevice::CreateRegisterRange() const
220✔
102
{
103
    return PRegisterRange(new TSameAddressRegisterRange());
220✔
104
}
105

106
void TSerialDevice::Prepare(TPort& port, TDevicePrepareMode prepareMode)
384✔
107
{
108
    bool deviceWasDisconnected = (ConnectionState != TDeviceConnectionState::CONNECTED);
384✔
109
    try {
110
        PrepareImpl(port);
384✔
111
        if (prepareMode == TDevicePrepareMode::WITH_SETUP_IF_WAS_DISCONNECTED && deviceWasDisconnected) {
378✔
112
            WriteSetupRegisters(port, GetSetupItems());
308✔
113
        }
114
    } catch (const TSerialDeviceException& ex) {
72✔
115
        SetTransferResult(false);
36✔
116
        throw;
36✔
117
    }
118
}
348✔
119

120
void TSerialDevice::PrepareImpl(TPort& port)
382✔
121
{
122
    port.SleepSinceLastInteraction(GetFrameTimeout(port));
382✔
123
}
382✔
124

125
void TSerialDevice::EndSession(TPort& port)
62✔
126
{}
62✔
127

128
void TSerialDevice::InvalidateReadCache()
580✔
129
{}
580✔
130

131
void TSerialDevice::WriteRegister(TPort& port, PRegister reg, const TRegisterValue& value)
276✔
132
{
133
    try {
134
        WriteRegisterImpl(port, *reg->GetConfig(), value);
302✔
135
        SetTransferResult(true);
250✔
136
    } catch (const TSerialDevicePermanentRegisterException& e) {
32✔
137
        SetTransferResult(true);
6✔
138
        throw;
6✔
139
    } catch (const TSerialDeviceException& ex) {
40✔
140
        SetTransferResult(false);
20✔
141
        throw;
20✔
142
    }
143
}
250✔
144

145
void TSerialDevice::WriteRegister(TPort& port, PRegister reg, uint64_t value)
34✔
146
{
147
    WriteRegister(port, reg, TRegisterValue{value});
40✔
148
}
28✔
149

150
TRegisterValue TSerialDevice::ReadRegisterImpl(TPort& port, const TRegisterConfig& reg)
×
151
{
152
    throw TSerialDeviceException("single register reading is not supported");
×
153
}
154

155
void TSerialDevice::WriteRegisterImpl(TPort& port, const TRegisterConfig& reg, const TRegisterValue& value)
×
156
{
157
    throw TSerialDeviceException(ToString() + ": register writing is not supported");
×
158
}
159

160
void TSerialDevice::ReadRegisterRange(TPort& port, PRegisterRange range, bool breakOnError)
548✔
161
{
162
    for (auto& reg: range->RegisterList()) {
1,558✔
163
        try {
164
            if (reg->GetAvailable() != TRegisterAvailability::UNAVAILABLE) {
1,010✔
165
                port.SleepSinceLastInteraction(DeviceConfig()->RequestDelay);
1,010✔
166
                auto value = ReadRegisterImpl(port, *reg->GetConfig());
2,020✔
167
                SetTransferResult(true);
916✔
168
                reg->SetValue(value);
916✔
169
            }
170
        } catch (const TSerialDeviceInternalErrorException& e) {
94✔
171
            reg->SetError(TRegister::TError::ReadError);
×
172
            LOG(Warn) << "TSerialDevice::ReadRegister(): " << e.what() << " [slave_id is "
×
173
                      << reg->Device()->ToString() + "]";
×
174
            SetTransferResult(true);
×
175
            if (breakOnError) {
×
176
                throw;
×
177
            }
178
        } catch (const TSerialDevicePermanentRegisterException& e) {
×
179
            reg->SetAvailable(TRegisterAvailability::UNAVAILABLE);
×
180
            reg->SetError(TRegister::TError::ReadError);
×
181
            LOG(Warn) << "TSerialDevice::ReadRegister(): " << e.what() << " [slave_id is "
×
182
                      << reg->Device()->ToString() + "] Register " << reg->ToString()
×
183
                      << " is now marked as unsupported";
×
184
            SetTransferResult(true);
×
185
            if (breakOnError) {
×
186
                throw;
×
187
            }
188
        } catch (const TSerialDeviceException& e) {
188✔
189
            reg->SetError(TRegister::TError::ReadError);
94✔
190
            auto& logger = (ConnectionState == TDeviceConnectionState::DISCONNECTED) ? Debug : Warn;
94✔
191
            LOG(logger) << "TSerialDevice::ReadRegister(): " << e.what() << " [slave_id is "
94✔
192
                        << reg->Device()->ToString() + "]";
94✔
193
            SetTransferResult(false);
94✔
194
            if (breakOnError) {
94✔
195
                throw;
×
196
            }
197
        }
198
    }
199
    InvalidateReadCache();
548✔
200
}
548✔
201

202
void TSerialDevice::SetTransferResult(bool ok)
2,396✔
203
{
204
    // disable reconnect functionality option
205
    if (_DeviceConfig->DeviceTimeout.count() < 0 || _DeviceConfig->DeviceMaxFailCycles < 0) {
2,396✔
206
        return;
×
207
    }
208

209
    if (ok) {
2,396✔
210
        LastSuccessfulCycle = std::chrono::steady_clock::now();
2,114✔
211
        SetConnectionState(TDeviceConnectionState::CONNECTED);
2,114✔
212
        RemainingFailCycles = _DeviceConfig->DeviceMaxFailCycles;
2,114✔
213
    } else {
214

215
        if (RemainingFailCycles > 0) {
282✔
216
            --RemainingFailCycles;
76✔
217
        }
218

219
        if (RemainingFailCycles == 0 && (ConnectionState != TDeviceConnectionState::DISCONNECTED) &&
426✔
220
            (std::chrono::steady_clock::now() - LastSuccessfulCycle > _DeviceConfig->DeviceTimeout))
426✔
221
        {
222
            SetDisconnected();
56✔
223
        }
224
    }
225
}
226

227
TDeviceConnectionState TSerialDevice::GetConnectionState() const
7,834✔
228
{
229
    return ConnectionState;
7,834✔
230
}
231

232
void TSerialDevice::WriteSetupRegisters(TPort& port, const TDeviceSetupItems& setupItems, bool breakOnError)
138✔
233
{
234
    for (const auto& item: setupItems) {
210✔
235
        WriteRegisterImpl(port, *item->RegisterConfig, item->RawValue);
88✔
236
        LOG(Info) << item->ToString();
72✔
237
    }
238
    if (!setupItems.empty()) {
122✔
239
        SetTransferResult(true);
32✔
240
    }
241
}
122✔
242

243
PDeviceConfig TSerialDevice::DeviceConfig() const
15,348✔
244
{
245
    return _DeviceConfig;
15,348✔
246
}
247

248
PProtocol TSerialDevice::Protocol() const
3,372✔
249
{
250
    return _Protocol;
3,372✔
251
}
252

253
bool TSerialDevice::GetSupportsHoles() const
802✔
254
{
255
    return SupportsHoles;
802✔
256
}
257

258
void TSerialDevice::SetSupportsHoles(bool supportsHoles)
70✔
259
{
260
    SupportsHoles = supportsHoles;
70✔
261
}
70✔
262

263
bool TSerialDevice::IsSporadicOnly() const
3,986✔
264
{
265
    return SporadicOnly;
3,986✔
266
}
267

268
void TSerialDevice::SetSporadicOnly(bool sporadicOnly)
390✔
269
{
270
    SporadicOnly = sporadicOnly;
390✔
271
}
390✔
272

273
void TSerialDevice::SetDisconnected()
66✔
274
{
275
    SetSupportsHoles(true);
66✔
276
    SetConnectionState(TDeviceConnectionState::DISCONNECTED);
66✔
277
}
66✔
278

279
PRegister TSerialDevice::AddRegister(PRegisterConfig config)
3,526✔
280
{
281
    auto reg = std::make_shared<TRegister>(shared_from_this(), config);
3,526✔
282
    Registers.push_back(reg);
3,526✔
283
    return reg;
3,526✔
284
}
285

286
const std::list<PRegister>& TSerialDevice::GetRegisters() const
840✔
287
{
288
    return Registers;
840✔
289
}
290

291
std::chrono::steady_clock::time_point TSerialDevice::GetLastReadTime() const
×
292
{
293
    return LastReadTime;
×
294
}
295

296
void TSerialDevice::SetLastReadTime(std::chrono::steady_clock::time_point readTime)
1,516✔
297
{
298
    LastReadTime = readTime;
1,516✔
299
}
1,516✔
300

301
void TSerialDevice::AddOnConnectionStateChangedCallback(TSerialDevice::TDeviceCallback callback)
526✔
302
{
303
    ConnectionStateChangedCallbacks.push_back(callback);
526✔
304
}
526✔
305

306
void TSerialDevice::SetConnectionState(TDeviceConnectionState state)
2,180✔
307
{
308
    if (ConnectionState == state) {
2,180✔
309
        return;
1,852✔
310
    }
311
    ConnectionState = state;
328✔
312
    if (state == TDeviceConnectionState::CONNECTED) {
328✔
313
        // clear serial number on connection as it can be other device than before disconnection
314
        if (SnRegister) {
262✔
315
            SnRegister->SetValue(TRegisterValue());
4✔
316
        }
317
        LOG(Info) << "device " << ToString() << " is connected";
262✔
318
    } else {
319
        LOG(Warn) << "device " << ToString() << " is disconnected";
66✔
320
    }
321
    for (auto& callback: ConnectionStateChangedCallbacks) {
1,012✔
322
        callback(shared_from_this());
684✔
323
    }
324
}
325

326
PRegister TSerialDevice::GetSnRegister() const
×
327
{
328
    return SnRegister;
×
329
}
330

331
void TSerialDevice::SetSnRegister(PRegisterConfig regConfig)
4✔
332
{
333
    auto regIt = std::find_if(Registers.begin(), Registers.end(), [&regConfig](const PRegister& reg) {
48✔
334
        return reg->GetConfig() == regConfig;
24✔
335
    });
4✔
336
    if (regIt == Registers.end()) {
4✔
337
        SnRegister = std::make_shared<TRegister>(shared_from_this(), regConfig);
×
338
    } else {
339
        SnRegister = *regIt;
4✔
340
    }
341
}
4✔
342

343
void TSerialDevice::AddSetupItem(PDeviceSetupItemConfig item)
214✔
344
{
345
    auto key = item->GetRegisterConfig()->ToString();
428✔
346
    auto addrIt = SetupItemsByAddress.find(key);
214✔
347
    if (addrIt != SetupItemsByAddress.end()) {
214✔
348
        std::stringstream ss;
4✔
349
        ss << "Setup command \"" << item->GetName() << "\" (" << key << ") from \"" << DeviceConfig()->DeviceType
12✔
350
           << "\" has a duplicate command \"" << addrIt->second->Name << "\" ";
12✔
351
        if (item->GetRawValue() == addrIt->second->RawValue) {
4✔
352
            ss << "with the same register value.";
×
353
        } else {
354
            ss << "with different register value. IT WILL BREAK TEMPLATE OPERATION";
4✔
355
        }
356
        LOG(Warn) << ss.str();
4✔
357
    } else {
358
        auto setupItem = std::make_shared<TDeviceSetupItem>(item, shared_from_this());
210✔
359
        SetupItemsByAddress.insert({key, setupItem});
210✔
360
        SetupItems.insert(setupItem);
210✔
361
    }
362
}
214✔
363

364
const TDeviceSetupItems& TSerialDevice::GetSetupItems() const
362✔
365
{
366
    return SetupItems;
362✔
367
}
368

369
std::chrono::milliseconds TSerialDevice::GetFrameTimeout(TPort& port) const
658✔
370
{
371
    return _DeviceConfig->FrameTimeout;
658✔
372
}
373

374
std::chrono::milliseconds TSerialDevice::GetResponseTimeout(TPort& port) const
1,592✔
375
{
376
    return _DeviceConfig->ResponseTimeout;
1,592✔
377
}
378

379
TUInt32SlaveId::TUInt32SlaveId(const std::string& slaveId, bool allowBroadcast): HasBroadcastSlaveId(false)
1,474✔
380
{
381
    if (allowBroadcast) {
1,474✔
382
        if (slaveId.empty()) {
36✔
383
            SlaveId = 0;
×
384
            HasBroadcastSlaveId = true;
×
385
            return;
×
386
        }
387
    }
388
    try {
389
        SlaveId = std::stoul(slaveId, /* pos = */ 0, /* base = */ 0);
1,474✔
390
    } catch (const std::logic_error& e) {
×
391
        throw TSerialDeviceException("slave ID \"" + slaveId + "\" is not convertible to integer");
×
392
    }
393
}
1,474✔
394

395
bool TUInt32SlaveId::operator==(const TUInt32SlaveId& id) const
448✔
396
{
397
    if (HasBroadcastSlaveId || id.HasBroadcastSlaveId) {
448✔
398
        return true;
×
399
    }
400
    return SlaveId == id.SlaveId;
448✔
401
}
402

403
uint64_t CopyDoubleToUint64(double value)
34✔
404
{
405
    uint64_t res = 0;
34✔
406
    static_assert((sizeof(res) >= sizeof(value)), "Can't fit double into uint64_t");
407
    memcpy(&res, &value, sizeof(value));
34✔
408
    return res;
34✔
409
}
410

411
TDeviceConfig::TDeviceConfig(const std::string& name, const std::string& slave_id, const std::string& protocol)
592✔
412
    : Name(name),
413
      SlaveId(slave_id),
414
      Protocol(protocol)
592✔
415
{}
592✔
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