• 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

99.6
/test/poll_test.cpp
1

2
#include "fake_serial_port.h"
3
#include "log.h"
4
#include "modbus_expectations_base.h"
5
#include "serial_driver.h"
6

7
using namespace std::chrono_literals;
8
using namespace std::chrono;
9

10
namespace
11
{
12
    const uint8_t MAX_EVENT_RESPONSE_SIZE = 0xF8;
13

14
    const auto DISCONNECTED_POLL_DELAY_STEP = 500ms;
15
    const auto DISCONNECTED_POLL_DELAY_LIMIT = 10s;
16

17
    class TTimeMock
18
    {
19
        steady_clock::time_point Time;
20

21
    public:
22
        void Reset()
34✔
23
        {
24
            Time = steady_clock::time_point();
34✔
25
        }
34✔
26

27
        steady_clock::time_point GetTime() const
5,470✔
28
        {
29
            return Time;
5,470✔
30
        }
31

32
        void AddTime(microseconds intervalToAdd)
1,626✔
33
        {
34
            Time += intervalToAdd;
1,626✔
35
        }
1,626✔
36
    };
37

38
    class TFakeSerialPortWithTime: public TFakeSerialPort
39
    {
40
        std::deque<microseconds> FrameReadTimes;
41
        TTimeMock& TimeMock;
42

43
    public:
44
        TFakeSerialPortWithTime(WBMQTT::Testing::TLoggedFixture& fixture, TTimeMock& timeMock)
34✔
45
            : TFakeSerialPort(fixture, "<TFakeSerialPortWithTime>"),
34✔
46
              TimeMock(timeMock)
34✔
47
        {}
34✔
48

49
        using TFakeSerialPort::Expect;
50

51
        void Expect(const std::vector<int>& request,
512✔
52
                    const std::vector<int>& response,
53
                    const char* func,
54
                    microseconds frameReadTime)
55
        {
56
            TFakeSerialPort::Expect(request, response, func);
512✔
57
            FrameReadTimes.push_back(frameReadTime);
512✔
58
        }
512✔
59

60
        TReadFrameResult ReadFrame(uint8_t* buf,
512✔
61
                                   size_t count,
62
                                   const microseconds& responseTimeout = -1ms,
63
                                   const microseconds& frameTimeout = -1ms,
64
                                   TFrameCompletePred frame_complete = 0) override
65
        {
66
            if (!FrameReadTimes.empty()) {
512✔
67
                TimeMock.AddTime(FrameReadTimes.front());
512✔
68
                FrameReadTimes.pop_front();
512✔
69
            }
70
            return TFakeSerialPort::ReadFrame(buf, count, responseTimeout, frameTimeout, frame_complete);
512✔
71
        }
72

73
        void SleepSinceLastInteraction(const std::chrono::microseconds& us) override
794✔
74
        {
75
            TFakeSerialPort::SleepSinceLastInteraction(us);
794✔
76
            TimeMock.AddTime(us);
794✔
77
        }
794✔
78
    };
79
}
80

81
class TPollTest: public TLoggedFixture, public TModbusExpectationsBase
82
{
83
public:
84
    void SetUp() override
34✔
85
    {
86
        TLoggedFixture::SetUp();
34✔
87
        TimeMock.Reset();
34✔
88
        Port = std::make_shared<TFakeSerialPortWithTime>(*this, TimeMock);
34✔
89
        FeaturePort = std::make_shared<TFeaturePort>(Port, false);
34✔
90
        TModbusDevice::Register(DeviceFactory);
34✔
91
        FeaturePort->Open();
34✔
92
    }
34✔
93

94
    void TearDown() override
34✔
95
    {
96
        FeaturePort->Close();
34✔
97
        TLoggedFixture::TearDown();
34✔
98
    }
34✔
99

100
    void Cycle(TSerialClientRegisterAndEventsReader& serialClient, TSerialClientDeviceAccessHandler& lastAccessedDevice)
568✔
101
    {
102
        Emit() << ceil<microseconds>(TimeMock.GetTime().time_since_epoch()).count() << ": Cycle";
568✔
103
        serialClient.OpenPortCycle(
568✔
104
            *FeaturePort,
568✔
105
            [this](PRegister reg) {
894✔
106
                std::string errorMsg;
298✔
107
                if (reg->GetErrorState().any()) {
298✔
108
                    errorMsg = " with errors: " + reg->GetErrorState().to_string();
56✔
109
                }
110
                Emit() << ceil<microseconds>(TimeMock.GetTime().time_since_epoch()).count() << ": " << reg->ToString()
596✔
111
                       << errorMsg;
298✔
112
            },
298✔
113
            lastAccessedDevice);
1,136✔
114
        auto deadline = serialClient.GetDeadline(TimeMock.GetTime());
568✔
115
        if (deadline > TimeMock.GetTime()) {
568✔
116
            TimeMock.AddTime(ceil<microseconds>(deadline - TimeMock.GetTime()));
288✔
117
        }
118
        Emit() << ceil<microseconds>(TimeMock.GetTime().time_since_epoch()).count() << ": Cycle end\n";
568✔
119
    }
568✔
120

121
    void EnqueueEnableContinuousRead(uint8_t slaveId, microseconds readTime)
8✔
122
    {
123
        SetModbusRTUSlaveId(slaveId);
8✔
124
        Port->Expect(WrapPDU({
16✔
125
                         0x06, // function code
126
                         0x00, // starting address Hi
127
                         0x72, // starting address Lo
128
                         0x00, // value Hi
129
                         0x01  // value Lo
130
                     }),
16✔
131
                     WrapPDU({
8✔
132
                         0x06, // function code
133
                         0x00, // starting address Hi
134
                         0x72, // starting address Lo
135
                         0x00, // value Hi
136
                         0x01  // value Lo
137
                     }),
16✔
138
                     __func__,
139
                     readTime);
140
    }
8✔
141

142
    void EnqueueReadHolding(uint8_t slaveId, uint16_t addr, uint16_t count, microseconds readTime)
242✔
143
    {
144
        SetModbusRTUSlaveId(slaveId);
242✔
145
        std::vector<int> response = {0x03, count * 2};
242✔
146
        for (auto i = 0; i < count; ++i) {
484✔
147
            response.push_back((i >> 8) & 0xFF);
242✔
148
            response.push_back(i & 0xFF);
242✔
149
        }
150
        Port->Expect(WrapPDU({
484✔
151
                         0x03,                // function code
152
                         (addr >> 8) & 0xFF,  // starting address Hi
242✔
153
                         addr & 0xFF,         // starting address Lo
242✔
154
                         (count >> 8) & 0xFF, // quantity Hi
242✔
155
                         count & 0xFF         // quantity Lo
242✔
156
                     }),
484✔
157
                     WrapPDU(response),
484✔
158
                     __func__,
159
                     readTime);
160
    }
242✔
161

162
    void EnqueueReadHoldingError(uint8_t slaveId, uint16_t addr, uint16_t count, microseconds readTime)
52✔
163
    {
164
        SetModbusRTUSlaveId(slaveId);
52✔
165
        Port->Expect(WrapPDU({
104✔
166
                         0x03,                // function code
167
                         (addr >> 8) & 0xFF,  // starting address Hi
52✔
168
                         addr & 0xFF,         // starting address Lo
52✔
169
                         (count >> 8) & 0xFF, // quantity Hi
52✔
170
                         count & 0xFF         // quantity Lo
52✔
171
                     }),
104✔
172
                     std::vector<int>(),
104✔
173
                     __func__,
174
                     readTime);
175
    }
52✔
176

177
    void EnqueueReadEvents(microseconds readTime, uint8_t slaveId, bool error, uint8_t responseSize)
180✔
178
    {
179
        SetModbusRTUSlaveId(0xFD);
180✔
180
        Port->Expect(WrapPDU({
360✔
181
                         0x46,         // function code
182
                         0x10,         // subcommand
183
                         slaveId,      // staring slaveId
180✔
184
                         responseSize, // max response size
180✔
185
                         slaveId,      //
180✔
186
                         0x00          //
187
                     }),
360✔
188
                     WrapPDU({
180✔
189
                         0x46,               // function code
190
                         error ? 0x14 : 0x12 // subcommand, no events
180✔
191
                     }),
540✔
192
                     __func__,
193
                     readTime);
194
    }
180✔
195

196
    void EnqueueReadEvents(microseconds readTime)
154✔
197
    {
198
        EnqueueReadEvents(readTime, 0, false, MAX_EVENT_RESPONSE_SIZE);
154✔
199
    }
154✔
200

201
    void EnqueueResetEvent(microseconds readTime, uint8_t slaveId)
2✔
202
    {
203
        SetModbusRTUSlaveId(0xFD);
2✔
204
        Port->Expect(WrapPDU({
4✔
205
                         0x46,                    // function code
206
                         0x10,                    // subcommand
207
                         0x00,                    // staring slaveId
208
                         MAX_EVENT_RESPONSE_SIZE, // response size
209
                         0x00,                    //
210
                         0x00                     //
211
                     }),
4✔
212
                     WrapPDU(
2✔
213
                         {
214
                             0x46, // function code
215
                             0x11, // subcommand, events
216
                             0x00, // flag
217
                             0x01, // event count
218
                             0x04, // data size
219
                             0x00, // additional data size
220
                             0x0f, // event type: reset
221
                             0x00, // event id
222
                             0x00, // event id
223
                         },
224
                         slaveId),
4✔
225
                     __func__,
226
                     readTime);
227
    }
2✔
228

229
    void EnqueueEnableEvents(uint8_t slaveId,
28✔
230
                             uint16_t addr,
231
                             microseconds readTime,
232
                             uint8_t res = 0x01,
233
                             bool error = false)
234
    {
235
        SetModbusRTUSlaveId(slaveId);
28✔
236
        Port->Expect(WrapPDU({
56✔
237
                         0x46,               // function code
238
                         0x18,               // subcommand
239
                         0x0A,               // data size
240
                         0x03,               // holding
241
                         (addr >> 8) & 0xFF, // address Hi
28✔
242
                         addr & 0xFF,        // address Lo
28✔
243
                         0x01,               // count
244
                         0x01,               // enable
245
                         0x0F,               // disable reset event
246
                         0x00,               //
247
                         0x00,               //
248
                         0x01,               //
249
                         0x00                //
250
                     }),
56✔
251
                     WrapPDU({
28✔
252
                         0x46,                // function code
253
                         error ? 0x14 : 0x18, // subcommand
28✔
254
                         0x02,                // data size
255
                         res,                 //
28✔
256
                         0x00                 //
257
                     }),
84✔
258
                     __func__,
259
                     readTime);
260
    }
28✔
261

262
    PExpector Expector() const override
×
263
    {
264
        return Port;
×
265
    }
266

267
    TModbusDeviceConfig MakeDeviceConfig(const std::string& name, const std::string& addr)
34✔
268
    {
269
        TModbusDeviceConfig config;
34✔
270
        config.CommonConfig = std::make_shared<TDeviceConfig>(name, addr, "modbus");
34✔
271
        config.CommonConfig->FrameTimeout = 0ms;
34✔
272
        return config;
34✔
273
    }
274

275
    void AddRegister(TSerialDevice& device,
46✔
276
                     uint16_t addr,
277
                     milliseconds readPeriod = 0ms,
278
                     TRegisterConfig::TSporadicMode sporadicMode = TRegisterConfig::TSporadicMode::DISABLED)
279
    {
280
        auto registerConfig = TRegisterConfig::Create(Modbus::REG_HOLDING, addr);
92✔
281
        if (readPeriod != 0ms) {
46✔
282
            registerConfig->ReadPeriod = readPeriod;
6✔
283
        }
284
        if (sporadicMode == TRegisterConfig::TSporadicMode::DISABLED) {
46✔
285
            device.SetSporadicOnly(false);
26✔
286
        }
287
        registerConfig->SporadicMode = sporadicMode;
46✔
288
        device.AddRegister(registerConfig);
46✔
289
    }
46✔
290

291
    std::shared_ptr<TModbusDevice> MakeDevice(const TModbusDeviceConfig& config)
34✔
292
    {
293
        return std::make_shared<TModbusDevice>(std::make_unique<Modbus::TModbusRTUTraits>(),
68✔
294
                                               config,
295
                                               DeviceFactory.GetProtocol("modbus"));
102✔
296
    }
297

298
    std::shared_ptr<TFakeSerialPortWithTime> Port;
299
    std::shared_ptr<TFeaturePort> FeaturePort;
300
    TTimeMock TimeMock;
301
    TSerialDeviceFactory DeviceFactory;
302
};
303

304
TEST_F(TPollTest, SingleDeviceSingleRegister)
16✔
305
{
306
    // One register without fixed read period and without events support
307
    // Check what poller reads it as soon as possible
308

309
    Port->SetBaudRate(115200);
4✔
310
    auto config = MakeDeviceConfig("device1", "1");
16✔
311
    auto device = MakeDevice(config);
8✔
312
    AddRegister(*device, 1);
4✔
313

314
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
118✔
315
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
316

317
    for (size_t i = 0; i < 10; ++i) {
44✔
318
        EnqueueReadHolding(1, 1, 1, 30ms);
40✔
319
        Cycle(serialClient, lastAccessedDevice);
40✔
320
    }
321
}
4✔
322

323
TEST_F(TPollTest, SingleDeviceSingleRegisterWithReadPeriod)
16✔
324
{
325
    // One register with fixed read period
326
    // Check what read period is preserved
327

328
    Port->SetBaudRate(115200);
4✔
329
    auto config = MakeDeviceConfig("device1", "1");
16✔
330
    auto device = MakeDevice(config);
8✔
331
    AddRegister(*device, 1, 100ms);
4✔
332

333
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
118✔
334
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
335

336
    for (size_t i = 0; i < 10; ++i) {
44✔
337
        EnqueueReadHolding(1, 1, 1, 30ms);
40✔
338
        Cycle(serialClient, lastAccessedDevice);
40✔
339
    }
340
}
4✔
341

342
TEST_F(TPollTest, SingleDeviceSeveralRegisters)
16✔
343
{
344
    // One register with fixed read period and one without it
345
    // Check what read period is preserved and poller wait for the first register,
346
    // if there are not enough time to read the second register
347

348
    Port->SetBaudRate(115200);
4✔
349
    auto config = MakeDeviceConfig("device1", "1");
16✔
350
    config.CommonConfig->RequestDelay = 10ms;
4✔
351
    auto device = MakeDevice(config);
8✔
352
    AddRegister(*device, 1);
4✔
353
    AddRegister(*device, 2, 50ms);
4✔
354

355
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
276✔
356
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
357

358
    for (size_t i = 0; i < 10; ++i) {
44✔
359
        EnqueueReadHolding(1, 2, 1, 10ms);
40✔
360
        Cycle(serialClient, lastAccessedDevice);
40✔
361
        EnqueueReadHolding(1, 1, 1, 10ms);
40✔
362
        Cycle(serialClient, lastAccessedDevice);
40✔
363

364
        // Not enough time before reading register with read period
365
        Cycle(serialClient, lastAccessedDevice);
40✔
366
    }
367
}
4✔
368

369
TEST_F(TPollTest, SingleDeviceSingleRegisterWithEvents)
16✔
370
{
371
    // One register with events
372
    // 1. Events must be enabled
373
    // 2. Read once by normal request
374
    // 3. Events read requests are sent every 50ms (10 times)
375
    // 4. The register is read again by normal request according to "sporadic only" device polling
376

377
    Port->SetBaudRate(115200);
4✔
378
    auto config = MakeDeviceConfig("device1", "1");
16✔
379
    config.CommonConfig->RequestDelay = 10ms;
4✔
380
    auto device = MakeDevice(config);
8✔
381
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
4✔
382

383
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
118✔
384
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
385

386
    // Read registers
387
    EnqueueEnableEvents(1, 1, 10ms);
4✔
388
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
389
    Cycle(serialClient, lastAccessedDevice);
4✔
390

391
    // Only read events
392
    for (size_t i = 0; i < 10; ++i) {
44✔
393
        EnqueueReadEvents(4ms);
40✔
394
        Cycle(serialClient, lastAccessedDevice);
40✔
395
    }
396

397
    // Read registers
398
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
399
    Cycle(serialClient, lastAccessedDevice);
4✔
400
}
4✔
401

402
TEST_F(TPollTest, SingleDeviceSingleRegisterWithEventsAndPolling)
16✔
403
{
404
    // One register with events and one without read period
405
    // 1. Events must be enabled
406
    // 2. The register is read once by normal request and excluded from polling
407
    // 3. Events read requests are sent every 50ms
408
    // 4. Register without read period must be polled during free time
409

410
    Port->SetBaudRate(115200);
4✔
411
    auto config = MakeDeviceConfig("device1", "1");
16✔
412
    config.CommonConfig->RequestDelay = 10ms;
4✔
413
    auto device = MakeDevice(config);
8✔
414
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
4✔
415
    AddRegister(*device, 2);
4✔
416

417
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
142✔
418
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
419

420
    // Enable events and read first register
421
    EnqueueEnableEvents(1, 1, 10ms);
4✔
422
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
423
    Cycle(serialClient, lastAccessedDevice);
4✔
424
    EnqueueReadHolding(1, 2, 1, 10ms);
4✔
425
    Cycle(serialClient, lastAccessedDevice);
4✔
426

427
    for (size_t i = 0; i < 3; ++i) {
16✔
428
        EnqueueReadEvents(4ms);
12✔
429
        Cycle(serialClient, lastAccessedDevice);
12✔
430
        EnqueueReadHolding(1, 2, 1, 10ms);
12✔
431
        Cycle(serialClient, lastAccessedDevice);
12✔
432
        EnqueueReadHolding(1, 2, 1, 10ms);
12✔
433
        Cycle(serialClient, lastAccessedDevice);
12✔
434
        // Not enough time for polling
435
        Cycle(serialClient, lastAccessedDevice);
12✔
436
    }
437
}
4✔
438

439
TEST_F(TPollTest, SingleDeviceSingleRegisterWithEventsAndPollingWithReadPeriod)
16✔
440
{
441
    // One register with events, one with read period and one without read period
442
    // 1. Events must be enabled
443
    // 2. The register is read once by normal request and excluded from polling
444
    // 3. Events read requests are sent every 50ms
445
    // 4. Register with read period must be polled during free time as much close to read period as possible
446
    // 5. Register without read period must be polled during free time
447

448
    Port->SetBaudRate(115200);
4✔
449
    auto config = MakeDeviceConfig("device1", "1");
16✔
450
    config.CommonConfig->RequestDelay = 10ms;
4✔
451
    auto device = MakeDevice(config);
8✔
452
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
4✔
453
    AddRegister(*device, 2, 100ms);
4✔
454
    AddRegister(*device, 3);
4✔
455

456
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
170✔
457
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
458

459
    // Enable events and read registers
460
    EnqueueEnableEvents(1, 1, 10ms);
4✔
461
    EnqueueReadHolding(1, 2, 1, 10ms);
4✔
462
    Cycle(serialClient, lastAccessedDevice);
4✔
463
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
464
    Cycle(serialClient, lastAccessedDevice);
4✔
465

466
    // It is already time to read events
467
    EnqueueReadEvents(4ms);
4✔
468
    Cycle(serialClient, lastAccessedDevice);
4✔
469

470
    // Read the last register
471
    EnqueueReadHolding(1, 3, 1, 10ms);
4✔
472
    Cycle(serialClient, lastAccessedDevice);
4✔
473
    EnqueueReadHolding(1, 3, 1, 10ms);
4✔
474
    Cycle(serialClient, lastAccessedDevice);
4✔
475

476
    // Not enough time before reading register with read period
477
    Cycle(serialClient, lastAccessedDevice);
4✔
478

479
    // Not enough time before events reading
480
    Cycle(serialClient, lastAccessedDevice);
4✔
481

482
    // It is already time to read events
483
    EnqueueReadEvents(4ms);
4✔
484
    Cycle(serialClient, lastAccessedDevice);
4✔
485

486
    // Read register with read period
487
    EnqueueReadHolding(1, 2, 1, 10ms);
4✔
488
    Cycle(serialClient, lastAccessedDevice);
4✔
489

490
    // Read the last register
491
    EnqueueReadHolding(1, 3, 1, 10ms);
4✔
492
    Cycle(serialClient, lastAccessedDevice);
4✔
493

494
    // Not enough time before reading register with read period
495
    Cycle(serialClient, lastAccessedDevice);
4✔
496

497
    // It is already time to read events
498
    EnqueueReadEvents(4ms);
4✔
499
    Cycle(serialClient, lastAccessedDevice);
4✔
500

501
    // Read the last register
502
    EnqueueReadHolding(1, 3, 1, 10ms);
4✔
503
    Cycle(serialClient, lastAccessedDevice);
4✔
504
    EnqueueReadHolding(1, 3, 1, 10ms);
4✔
505
    Cycle(serialClient, lastAccessedDevice);
4✔
506

507
    // Not enough time before events reading
508
    Cycle(serialClient, lastAccessedDevice);
4✔
509

510
    // It is already time to read events
511
    EnqueueReadEvents(4ms);
4✔
512
    Cycle(serialClient, lastAccessedDevice);
4✔
513

514
    // Read register with read period
515
    EnqueueReadHolding(1, 2, 1, 10ms);
4✔
516
    Cycle(serialClient, lastAccessedDevice);
4✔
517
}
4✔
518

519
TEST_F(TPollTest, SingleDeviceEventsAndBigReadTime)
16✔
520
{
521
    // One register with events, one with read period and one without read period
522
    // 1. Events must be enabled
523
    // 2. The register is read once by normal request and excluded from polling
524
    // 3. Events read requests are sent every 50ms
525
    // 4. Register without read period must be polled after some time of event reads because of time balancing
526

527
    Port->SetBaudRate(115200);
4✔
528
    auto config = MakeDeviceConfig("device1", "1");
16✔
529
    config.CommonConfig->RequestDelay = 100ms;
4✔
530
    auto device = MakeDevice(config);
8✔
531
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
4✔
532
    AddRegister(*device, 2);
4✔
533

534
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
354✔
535
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
536

537
    // Enable events and read registers
538
    EnqueueEnableEvents(1, 1, 10ms);
4✔
539
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
540
    Cycle(serialClient, lastAccessedDevice);
4✔
541

542
    for (size_t i = 0; i < 13; ++i) {
56✔
543
        EnqueueReadEvents(4ms);
52✔
544
        Cycle(serialClient, lastAccessedDevice);
52✔
545

546
        // Calculated read time is too big
547
        Cycle(serialClient, lastAccessedDevice);
52✔
548
    }
549

550
    EnqueueReadEvents(4ms);
4✔
551
    Cycle(serialClient, lastAccessedDevice);
4✔
552

553
    // Read register
554
    EnqueueReadHolding(1, 2, 1, 10ms);
4✔
555
    Cycle(serialClient, lastAccessedDevice);
4✔
556

557
    for (size_t i = 0; i < 2; ++i) {
12✔
558
        for (size_t j = 0; j < 2; ++j) {
24✔
559
            EnqueueReadEvents(4ms);
16✔
560
            Cycle(serialClient, lastAccessedDevice);
16✔
561

562
            // Calculated read time is too big
563
            Cycle(serialClient, lastAccessedDevice);
16✔
564
        }
565

566
        EnqueueReadEvents(4ms);
8✔
567
        Cycle(serialClient, lastAccessedDevice);
8✔
568

569
        // Read register
570
        EnqueueReadHolding(1, 2, 1, 10ms);
8✔
571
        Cycle(serialClient, lastAccessedDevice);
8✔
572
    }
573
}
4✔
574

575
TEST_F(TPollTest, SingleDeviceSingleRegisterWithBigReadTime)
16✔
576
{
577
    // One register without fixed read period but with big read time
578
    // Check what poller reads it as soon as possible
579

580
    Port->SetBaudRate(115200);
4✔
581
    auto config = MakeDeviceConfig("device1", "1");
16✔
582
    config.CommonConfig->RequestDelay = 100ms;
4✔
583
    auto device = MakeDevice(config);
8✔
584
    AddRegister(*device, 1);
4✔
585

586
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
118✔
587
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
588

589
    for (size_t i = 0; i < 10; ++i) {
44✔
590
        EnqueueReadHolding(1, 1, 1, 30ms);
40✔
591
        Cycle(serialClient, lastAccessedDevice);
40✔
592
    }
593
}
4✔
594

595
TEST_F(TPollTest, SingleDeviceSingleRegisterWithEventsAndErrors)
16✔
596
{
597
    // One register with events
598
    // 1. Events must be enabled
599
    // 2. Read once by normal request
600
    // 3. Events read requests are sent every 50ms
601
    // 4. Some read requests have errors
602

603
    Port->SetBaudRate(115200);
4✔
604
    auto config = MakeDeviceConfig("device1", "1");
16✔
605
    config.CommonConfig->RequestDelay = 10ms;
4✔
606
    auto device = MakeDevice(config);
8✔
607
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
4✔
608

609
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
100✔
610
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
611

612
    // Read registers
613
    EnqueueEnableEvents(1, 1, 10ms);
4✔
614
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
615
    Cycle(serialClient, lastAccessedDevice);
4✔
616

617
    // Only read events
618
    for (size_t i = 0; i < 3; ++i) {
16✔
619
        EnqueueReadEvents(10ms);
12✔
620
        Cycle(serialClient, lastAccessedDevice);
12✔
621
    }
622

623
    for (size_t i = 0; i < 3; ++i) {
16✔
624
        EnqueueReadEvents(30ms, 0, true, MAX_EVENT_RESPONSE_SIZE);
12✔
625
        EnqueueReadEvents(30ms, 0, true, MAX_EVENT_RESPONSE_SIZE);
12✔
626
        EnqueueReadEvents(30ms, 0, true, MAX_EVENT_RESPONSE_SIZE);
12✔
627
        EnqueueReadEvents(25ms, 0, true, 0x40);
12✔
628
        Cycle(serialClient, lastAccessedDevice);
12✔
629
    }
630
}
4✔
631

632
TEST_F(TPollTest, SingleDeviceEnableEventsError)
16✔
633
{
634
    // One register with events
635
    // 1. Events must be enabled
636
    // 2. Read once by normal request
637
    // 3. Events read requests are sent every 50ms
638
    // 4. Some read requests have errors
639

640
    Port->SetBaudRate(115200);
4✔
641
    auto config = MakeDeviceConfig("device1", "1");
16✔
642
    config.CommonConfig->RequestDelay = 10ms;
4✔
643
    auto device = MakeDevice(config);
8✔
644
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
4✔
645

646
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
138✔
647
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
648

649
    // Read registers
650
    EnqueueEnableEvents(1, 1, 10ms, 0x01, true);
4✔
651
    Cycle(serialClient, lastAccessedDevice);
4✔
652

653
    // Events are disabled, poll register
654
    EnqueueEnableEvents(1, 1, 10ms, 0x00);
4✔
655
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
656
    Cycle(serialClient, lastAccessedDevice);
4✔
657

658
    for (size_t i = 0; i < 10; ++i) {
44✔
659
        EnqueueReadHolding(1, 1, 1, 10ms);
40✔
660
        Cycle(serialClient, lastAccessedDevice);
40✔
661
    }
662
}
4✔
663

664
TEST_F(TPollTest, SemiSporadicRegister)
16✔
665
{
666
    // One register with events and polling and one without read period
667
    // 1. Events must be enabled
668
    // 2. The register is read once by normal request and NOT excluded from polling
669
    // 3. Events read requests are sent every 50ms
670
    // 4. Both registers must be polled during free time
671

672
    Port->SetBaudRate(115200);
4✔
673
    auto config = MakeDeviceConfig("device1", "1");
16✔
674
    config.CommonConfig->RequestDelay = 10ms;
4✔
675
    auto device = MakeDevice(config);
8✔
676
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::EVENTS_AND_POLLING);
4✔
677
    AddRegister(*device, 2);
4✔
678

679
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
142✔
680
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
681

682
    // Enable events and read first register
683
    EnqueueEnableEvents(1, 1, 10ms);
4✔
684
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
685
    Cycle(serialClient, lastAccessedDevice);
4✔
686
    EnqueueReadHolding(1, 2, 1, 10ms);
4✔
687
    Cycle(serialClient, lastAccessedDevice);
4✔
688

689
    for (size_t i = 0; i < 3; ++i) {
16✔
690
        EnqueueReadEvents(4ms);
12✔
691
        Cycle(serialClient, lastAccessedDevice);
12✔
692
        EnqueueReadHolding(1, 1, 1, 10ms);
12✔
693
        Cycle(serialClient, lastAccessedDevice);
12✔
694
        EnqueueReadHolding(1, 2, 1, 10ms);
12✔
695
        Cycle(serialClient, lastAccessedDevice);
12✔
696
        // Not enough time for polling
697
        Cycle(serialClient, lastAccessedDevice);
12✔
698
    }
699
}
4✔
700

701
TEST_F(TPollTest, ReconnectWithOnlyEvents)
16✔
702
{
703
    // One register with events
704
    // 1. Events must be enabled
705
    // 2. Read once by normal request
706
    // 3. Events read requests are sent every 50ms (8 times to not trigger every 500ms polling)
707
    // 4. Simulate restart event
708
    // 5. Reenable events
709
    // 6. Read once by normal request
710
    // 7. Events read requests are sent every 50ms (10 times)
711
    // 8. The register is read again by normal request according to "sporadic only" device polling
712

713
    Port->SetBaudRate(115200);
4✔
714
    auto config = MakeDeviceConfig("device1", "1");
16✔
715
    config.CommonConfig->RequestDelay = 10ms;
4✔
716
    auto device = MakeDevice(config);
8✔
717
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
4✔
718

719
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
206✔
720
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
721

722
    // Read registers
723
    EnqueueEnableEvents(1, 1, 10ms);
4✔
724
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
725
    Cycle(serialClient, lastAccessedDevice);
4✔
726

727
    // Only read events
728
    for (size_t i = 0; i < 8; ++i) {
36✔
729
        EnqueueReadEvents(4ms);
32✔
730
        Cycle(serialClient, lastAccessedDevice);
32✔
731
    }
732

733
    // Device reset event
734
    EnqueueResetEvent(4ms, 1);
4✔
735
    EnqueueReadEvents(4ms, 1, false, MAX_EVENT_RESPONSE_SIZE);
4✔
736
    Cycle(serialClient, lastAccessedDevice);
4✔
737

738
    // Additional cycle for reading event but without actual read
739
    Cycle(serialClient, lastAccessedDevice);
4✔
740

741
    // Read registers
742
    EnqueueEnableEvents(1, 1, 10ms);
4✔
743
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
744
    Cycle(serialClient, lastAccessedDevice);
4✔
745

746
    // Only read events
747
    for (size_t i = 0; i < 10; ++i) {
44✔
748
        EnqueueReadEvents(4ms);
40✔
749
        Cycle(serialClient, lastAccessedDevice);
40✔
750
    }
751

752
    // Read registers
753
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
754
    Cycle(serialClient, lastAccessedDevice);
4✔
755
}
4✔
756

757
TEST_F(TPollTest, OnlyEventsPollError)
16✔
758
{
759
    // One register with events
760
    // 1. Events must be enabled
761
    // 2. Read once by normal request
762
    // 3. Events read requests are sent every 50ms (10 times)
763
    // 4. Simulate read response timeout and check for device is now disconnected
764
    // 5. Reenable events
765
    // 6. Read once by normal request and check for device is reconnected
766

767
    Port->SetBaudRate(115200);
4✔
768

769
    auto config = MakeDeviceConfig("device1", "1");
16✔
770
    config.CommonConfig->RequestDelay = 10ms;
4✔
771
    config.CommonConfig->DeviceTimeout = std::chrono::milliseconds(0);
4✔
772
    config.CommonConfig->DeviceMaxFailCycles = 1;
4✔
773

774
    auto device = MakeDevice(config);
8✔
775
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
4✔
776

777
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
132✔
778
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
779

780
    // Enable events and read register
781
    EnqueueEnableEvents(1, 1, 10ms);
4✔
782
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
783
    Cycle(serialClient, lastAccessedDevice);
4✔
784

785
    // Only read events
786
    for (size_t i = 0; i < 10; ++i) {
44✔
787
        EnqueueReadEvents(4ms);
40✔
788
        Cycle(serialClient, lastAccessedDevice);
40✔
789
    }
790

791
    // Read register with no response
792
    EnqueueReadHoldingError(1, 1, 1, 10ms);
4✔
793
    Cycle(serialClient, lastAccessedDevice);
4✔
794
    EXPECT_EQ(device->GetConnectionState(), TDeviceConnectionState::DISCONNECTED);
4✔
795

796
    // Additional cycle for reading event but without actual read
797
    Cycle(serialClient, lastAccessedDevice);
4✔
798

799
    // Reenable events and read register
800
    EnqueueEnableEvents(1, 1, 10ms);
4✔
801
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
802
    Cycle(serialClient, lastAccessedDevice);
4✔
803
    EXPECT_EQ(device->GetConnectionState(), TDeviceConnectionState::CONNECTED);
4✔
804
}
4✔
805

806
TEST_F(TPollTest, DisconnectedPollDelay)
16✔
807
{
808
    // One register without events
809
    // 1. Read once
810
    // 2. Simulate read response timeout and check for device is now disconnected'
811
    // 3. Check for poll interval is increased
812
    // 4. Repeat steps 2 and 3 another 24 times
813
    // 5. Read once and check for device is reconnected
814

815
    Port->SetBaudRate(115200);
4✔
816

817
    auto config = MakeDeviceConfig("device1", "1");
16✔
818
    config.CommonConfig->RequestDelay = 10ms;
4✔
819
    config.CommonConfig->DeviceTimeout = std::chrono::milliseconds(0);
4✔
820
    config.CommonConfig->DeviceMaxFailCycles = 1;
4✔
821

822
    auto device = MakeDevice(config);
8✔
823
    AddRegister(*device, 1);
4✔
824

825
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
288✔
826
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
827

828
    // Read register
829
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
830
    Cycle(serialClient, lastAccessedDevice);
4✔
831

832
    auto time = TimeMock.GetTime() + 20ms; // 20ms added by read cycle
4✔
833
    auto delay = milliseconds(0);
4✔
834
    for (int i = 0; i < 25; ++i) {
104✔
835
        // Read register with no response
836
        EnqueueReadHoldingError(1, 1, 1, 10ms);
100✔
837
        Cycle(serialClient, lastAccessedDevice);
100✔
838
        EXPECT_EQ(device->GetConnectionState(), TDeviceConnectionState::DISCONNECTED);
100✔
839
        // Check for poll interval is increased
840
        EXPECT_EQ(ceil<milliseconds>(TimeMock.GetTime().time_since_epoch()).count(),
100✔
841
                  ceil<milliseconds>(time.time_since_epoch()).count());
842
        if (delay < DISCONNECTED_POLL_DELAY_LIMIT) {
100✔
843
            delay += DISCONNECTED_POLL_DELAY_STEP;
80✔
844
        }
845
        time += delay;
100✔
846
    }
847

848
    // Read register
849
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
850
    Cycle(serialClient, lastAccessedDevice);
4✔
851
    EXPECT_EQ(device->GetConnectionState(), TDeviceConnectionState::CONNECTED);
4✔
852
}
4✔
853

854
TEST_F(TPollTest, SuspendAndResume)
16✔
855
{
856
    // One register without events
857
    // 1. Continuous read must be enabled
858
    // 2. Poll register (3 times)
859
    // 3. Suspend poll
860
    // 4. Wait a "minute" (3 times)
861
    // 5. Resume poll "manually"
862
    // 6. Reenable continuous read
863
    // 7. Poll register (3 times)
864

865
    Port->SetBaudRate(115200);
4✔
866

867
    auto config = MakeDeviceConfig("device1", "1");
16✔
868
    config.EnableWbContinuousRead = true;
4✔
869

870
    auto device = MakeDevice(config);
8✔
871
    AddRegister(*device, 1);
4✔
872

873
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
166✔
874
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
875

876
    EnqueueEnableContinuousRead(1, 10ms);
4✔
877
    for (size_t i = 0; i < 3; ++i) {
16✔
878
        EnqueueReadHolding(1, 1, 1, 10ms);
12✔
879
        Cycle(serialClient, lastAccessedDevice);
12✔
880
    }
881

882
    serialClient.SuspendPoll(device, TimeMock.GetTime());
4✔
883

884
    for (size_t i = 0; i < 3; ++i) {
16✔
885
        Cycle(serialClient, lastAccessedDevice);
12✔
886
        TimeMock.AddTime(1min);
12✔
887
    }
888

889
    serialClient.ResumePoll(device);
4✔
890

891
    EnqueueEnableContinuousRead(1, 10ms);
4✔
892
    for (size_t i = 0; i < 10; ++i) {
44✔
893
        EnqueueReadHolding(1, 1, 1, 10ms);
40✔
894
        Cycle(serialClient, lastAccessedDevice);
40✔
895
    }
896
}
4✔
897

898
TEST_F(TPollTest, SuspendAndResumeByTimeout)
16✔
899
{
900
    // One register without events
901
    // 1. Continuous read must be enabled
902
    // 2. Poll register (3 times)
903
    // 3. Suspend poll
904
    // 4. Wait a "minute" (10 times)
905
    // 5. Poll must be resumed by timeout
906
    // 6. Reenable continuous read
907
    // 7. Poll register (3 times)
908

909
    Port->SetBaudRate(115200);
4✔
910

911
    auto config = MakeDeviceConfig("device1", "1");
16✔
912
    config.EnableWbContinuousRead = true;
4✔
913

914
    auto device = MakeDevice(config);
8✔
915
    AddRegister(*device, 1);
4✔
916

917
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
138✔
918
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
919

920
    EnqueueEnableContinuousRead(1, 10ms);
4✔
921
    for (size_t i = 0; i < 3; ++i) {
16✔
922
        EnqueueReadHolding(1, 1, 1, 10ms);
12✔
923
        Cycle(serialClient, lastAccessedDevice);
12✔
924
    }
925

926
    serialClient.SuspendPoll(device, TimeMock.GetTime());
4✔
927

928
    for (size_t i = 0; i < 10; ++i) {
44✔
929
        Cycle(serialClient, lastAccessedDevice);
40✔
930
        TimeMock.AddTime(1min);
40✔
931
    }
932

933
    EnqueueEnableContinuousRead(1, 10ms);
4✔
934
    for (size_t i = 0; i < 3; ++i) {
16✔
935
        EnqueueReadHolding(1, 1, 1, 10ms);
12✔
936
        Cycle(serialClient, lastAccessedDevice);
12✔
937
    }
938
}
4✔
939

940
TEST_F(TPollTest, SuspendAndResumeWithEvents)
16✔
941
{
942
    // One register with events
943
    // 1. Events must be enabled
944
    // 2. Read once by normal request
945
    // 3. Events read requests are sent every 50ms (3 times)
946
    // 4. Suspend poll
947
    // 4. Wait a "minute" (3 times)
948
    // 6. Resume poll "manually"
949
    // 7. Reenable events
950
    // 8. Read once by normal request
951
    // 9. Events read requests are sent every 50ms (10 times)
952

953
    Port->SetBaudRate(115200);
4✔
954

955
    auto config = MakeDeviceConfig("device1", "1");
16✔
956
    config.CommonConfig->RequestDelay = 10ms;
4✔
957

958
    auto device = MakeDevice(config);
8✔
959
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
4✔
960

961
    TSerialClientRegisterAndEventsReader serialClient({device}, 50ms, [this]() { return TimeMock.GetTime(); });
102✔
962
    TSerialClientDeviceAccessHandler lastAccessedDevice(serialClient.GetEventsReader());
8✔
963

964
    EnqueueEnableEvents(1, 1, 10ms);
4✔
965
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
966
    Cycle(serialClient, lastAccessedDevice);
4✔
967

968
    for (size_t i = 0; i < 3; ++i) {
16✔
969
        EnqueueReadEvents(4ms);
12✔
970
        Cycle(serialClient, lastAccessedDevice);
12✔
971
    }
972

973
    serialClient.SuspendPoll(device, TimeMock.GetTime());
4✔
974

975
    for (size_t i = 0; i < 3; ++i) {
16✔
976
        Cycle(serialClient, lastAccessedDevice);
12✔
977
        TimeMock.AddTime(1min);
12✔
978
    }
979

980
    serialClient.ResumePoll(device);
4✔
981

982
    EnqueueEnableEvents(1, 1, 10ms);
4✔
983
    EnqueueReadHolding(1, 1, 1, 10ms);
4✔
984
    Cycle(serialClient, lastAccessedDevice);
4✔
985

986
    for (size_t i = 0; i < 3; ++i) {
16✔
987
        EnqueueReadEvents(4ms);
12✔
988
        Cycle(serialClient, lastAccessedDevice);
12✔
989
    }
990
}
4✔
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