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

wirenboard / wb-mqtt-serial / 6

13 Jan 2026 06:31AM UTC coverage: 76.817% (+4.0%) from 72.836%
6

Pull #1049

github

8b2ffc
Ilia1S
Remove fw from groups
Pull Request #1049: WB-MR templates: Add delays

6873 of 9161 branches covered (75.02%)

12966 of 16879 relevant lines covered (76.82%)

830.18 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()
17✔
23
        {
24
            Time = steady_clock::time_point();
17✔
25
        }
17✔
26

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

32
        void AddTime(microseconds intervalToAdd)
813✔
33
        {
34
            Time += intervalToAdd;
813✔
35
        }
813✔
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)
17✔
45
            : TFakeSerialPort(fixture, "<TFakeSerialPortWithTime>"),
17✔
46
              TimeMock(timeMock)
17✔
47
        {}
17✔
48

49
        using TFakeSerialPort::Expect;
50

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

60
        TReadFrameResult ReadFrame(uint8_t* buf,
256✔
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()) {
256✔
67
                TimeMock.AddTime(FrameReadTimes.front());
256✔
68
                FrameReadTimes.pop_front();
256✔
69
            }
70
            return TFakeSerialPort::ReadFrame(buf, count, responseTimeout, frameTimeout, frame_complete);
256✔
71
        }
72

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

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

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

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

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

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

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

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

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

201
    void EnqueueResetEvent(microseconds readTime, uint8_t slaveId)
1✔
202
    {
203
        SetModbusRTUSlaveId(0xFD);
1✔
204
        Port->Expect(WrapPDU({
2✔
205
                         0x46,                    // function code
206
                         0x10,                    // subcommand
207
                         0x00,                    // staring slaveId
208
                         MAX_EVENT_RESPONSE_SIZE, // response size
209
                         0x00,                    //
210
                         0x00                     //
211
                     }),
2✔
212
                     WrapPDU(
1✔
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),
2✔
225
                     __func__,
226
                     readTime);
227
    }
1✔
228

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

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

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

275
    void AddRegister(TSerialDevice& device,
23✔
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);
46✔
281
        if (readPeriod != 0ms) {
23✔
282
            registerConfig->ReadPeriod = readPeriod;
3✔
283
        }
284
        if (sporadicMode == TRegisterConfig::TSporadicMode::DISABLED) {
23✔
285
            device.SetSporadicOnly(false);
13✔
286
        }
287
        registerConfig->SporadicMode = sporadicMode;
23✔
288
        device.AddRegister(registerConfig);
23✔
289
    }
23✔
290

291
    std::shared_ptr<TModbusDevice> MakeDevice(const TModbusDeviceConfig& config)
17✔
292
    {
293
        return std::make_shared<TModbusDevice>(std::make_unique<Modbus::TModbusRTUTraits>(),
34✔
294
                                               config,
295
                                               DeviceFactory.GetProtocol("modbus"));
51✔
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)
8✔
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);
2✔
310
    auto config = MakeDeviceConfig("device1", "1");
8✔
311
    auto device = MakeDevice(config);
4✔
312
    AddRegister(*device, 1);
2✔
313

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

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

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

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

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

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

342
TEST_F(TPollTest, SingleDeviceSeveralRegisters)
8✔
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);
2✔
349
    auto config = MakeDeviceConfig("device1", "1");
8✔
350
    config.CommonConfig->RequestDelay = 10ms;
2✔
351
    auto device = MakeDevice(config);
4✔
352
    AddRegister(*device, 1);
2✔
353
    AddRegister(*device, 2, 50ms);
2✔
354

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

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

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

369
TEST_F(TPollTest, SingleDeviceSingleRegisterWithEvents)
8✔
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);
2✔
378
    auto config = MakeDeviceConfig("device1", "1");
8✔
379
    config.CommonConfig->RequestDelay = 10ms;
2✔
380
    auto device = MakeDevice(config);
4✔
381
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
2✔
382

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

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

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

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

402
TEST_F(TPollTest, SingleDeviceSingleRegisterWithEventsAndPolling)
8✔
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);
2✔
411
    auto config = MakeDeviceConfig("device1", "1");
8✔
412
    config.CommonConfig->RequestDelay = 10ms;
2✔
413
    auto device = MakeDevice(config);
4✔
414
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
2✔
415
    AddRegister(*device, 2);
2✔
416

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

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

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

439
TEST_F(TPollTest, SingleDeviceSingleRegisterWithEventsAndPollingWithReadPeriod)
8✔
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);
2✔
449
    auto config = MakeDeviceConfig("device1", "1");
8✔
450
    config.CommonConfig->RequestDelay = 10ms;
2✔
451
    auto device = MakeDevice(config);
4✔
452
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
2✔
453
    AddRegister(*device, 2, 100ms);
2✔
454
    AddRegister(*device, 3);
2✔
455

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

519
TEST_F(TPollTest, SingleDeviceEventsAndBigReadTime)
8✔
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);
2✔
528
    auto config = MakeDeviceConfig("device1", "1");
8✔
529
    config.CommonConfig->RequestDelay = 100ms;
2✔
530
    auto device = MakeDevice(config);
4✔
531
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
2✔
532
    AddRegister(*device, 2);
2✔
533

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

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

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

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

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

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

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

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

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

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

575
TEST_F(TPollTest, SingleDeviceSingleRegisterWithBigReadTime)
8✔
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);
2✔
581
    auto config = MakeDeviceConfig("device1", "1");
8✔
582
    config.CommonConfig->RequestDelay = 100ms;
2✔
583
    auto device = MakeDevice(config);
4✔
584
    AddRegister(*device, 1);
2✔
585

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

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

595
TEST_F(TPollTest, SingleDeviceSingleRegisterWithEventsAndErrors)
8✔
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);
2✔
604
    auto config = MakeDeviceConfig("device1", "1");
8✔
605
    config.CommonConfig->RequestDelay = 10ms;
2✔
606
    auto device = MakeDevice(config);
4✔
607
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
2✔
608

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

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

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

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

632
TEST_F(TPollTest, SingleDeviceEnableEventsError)
8✔
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);
2✔
641
    auto config = MakeDeviceConfig("device1", "1");
8✔
642
    config.CommonConfig->RequestDelay = 10ms;
2✔
643
    auto device = MakeDevice(config);
4✔
644
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
2✔
645

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

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

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

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

664
TEST_F(TPollTest, SemiSporadicRegister)
8✔
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);
2✔
673
    auto config = MakeDeviceConfig("device1", "1");
8✔
674
    config.CommonConfig->RequestDelay = 10ms;
2✔
675
    auto device = MakeDevice(config);
4✔
676
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::EVENTS_AND_POLLING);
2✔
677
    AddRegister(*device, 2);
2✔
678

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

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

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

701
TEST_F(TPollTest, ReconnectWithOnlyEvents)
8✔
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);
2✔
714
    auto config = MakeDeviceConfig("device1", "1");
8✔
715
    config.CommonConfig->RequestDelay = 10ms;
2✔
716
    auto device = MakeDevice(config);
4✔
717
    AddRegister(*device, 1, 0ms, TRegisterConfig::TSporadicMode::ONLY_EVENTS);
2✔
718

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

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

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

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

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

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

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

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

757
TEST_F(TPollTest, OnlyEventsPollError)
8✔
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);
2✔
768

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

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

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

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

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

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

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

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

806
TEST_F(TPollTest, DisconnectedPollDelay)
8✔
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);
2✔
816

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

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

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

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

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

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

854
TEST_F(TPollTest, SuspendAndResume)
8✔
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);
2✔
866

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

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

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

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

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

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

889
    serialClient.ResumePoll(device);
2✔
890

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

898
TEST_F(TPollTest, SuspendAndResumeByTimeout)
8✔
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);
2✔
910

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

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

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

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

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

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

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

940
TEST_F(TPollTest, SuspendAndResumeWithEvents)
8✔
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);
2✔
954

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

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

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

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

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

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

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

980
    serialClient.ResumePoll(device);
2✔
981

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

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