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

wirenboard / wb-mqtt-serial / 690

26 Aug 2025 01:28PM UTC coverage: 73.031%. Remained the same
690

push

github

web-flow
Add protocol parameter to device/Probe RPC (#988)

  * Add protocol to Modbus TCP port information in ports/Load RPC
  * Add protocol parameter to device/Probe RPC
  * Set TCP_NODELAY for TCP ports
  * Handle TCP port closing by remote

6605 of 9406 branches covered (70.22%)

28 of 59 new or added lines in 11 files covered. (47.46%)

136 existing lines in 8 files now uncovered.

12554 of 17190 relevant lines covered (73.03%)

372.2 hits per line

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

95.83
/test/modbus_ext_common_test.cpp
1
#include "crc16.h"
2
#include "modbus_ext_common.h"
3
#include "serial_exc.h"
4
#include "gtest/gtest.h"
5

6
#include <cmath>
7

8
namespace
9
{
10
    class TPortMock: public TPort
11
    {
12
    public:
13
        std::vector<uint8_t> Request;
14
        std::vector<uint8_t> Response;
15

16
        TPortMock()
11✔
17
        {}
11✔
18

19
        void Open() override
×
20
        {}
21
        void Close() override
×
22
        {}
23
        bool IsOpen() const override
×
24
        {
25
            return false;
×
26
        }
27
        void CheckPortOpen() const override
×
28
        {}
29

30
        void WriteBytes(const uint8_t* buf, int count) override
15✔
31
        {
32
            Request.clear();
15✔
33
            Request.insert(Request.end(), buf, buf + count);
15✔
34
        }
15✔
35

36
        uint8_t ReadByte(const std::chrono::microseconds& timeout) override
×
37
        {
38
            return 0;
×
39
        }
40

41
        TReadFrameResult ReadFrame(uint8_t* buf,
15✔
42
                                   size_t count,
43
                                   const std::chrono::microseconds& responseTimeout,
44
                                   const std::chrono::microseconds& frameTimeout,
45
                                   TFrameCompletePred frame_complete = 0) override
46
        {
47
            if (Response.size() > count) {
15✔
48
                throw std::runtime_error("Buffer is too small");
×
49
            }
50
            TReadFrameResult res;
15✔
51
            res.Count = Response.size();
15✔
52
            memcpy(buf, Response.data(), Response.size());
15✔
53
            return res;
15✔
54
        }
55

56
        void SkipNoise() override
×
57
        {}
58

59
        void SleepSinceLastInteraction(const std::chrono::microseconds& us) override
8✔
60
        {}
8✔
61

62
        std::string GetDescription(bool verbose) const override
×
63
        {
64
            return std::string();
×
65
        }
66

67
        std::chrono::microseconds GetSendTimeBytes(double bytesNumber) const override
16✔
68
        {
69
            // 8-N-2
70
            auto bits = std::ceil(11 * bytesNumber);
16✔
71
            return GetSendTimeBits(bits);
16✔
72
        }
73

74
        std::chrono::microseconds GetSendTimeBits(size_t bitsNumber) const override
28✔
75
        {
76
            // 115200 bps
77
            auto us = std::ceil((1000000.0 * bitsNumber) / 115200.0);
28✔
78
            return std::chrono::microseconds(static_cast<std::chrono::microseconds::rep>(us));
28✔
79
        }
80
    };
81

82
    class TTestEventsVisitor: public ModbusExt::IEventsVisitor
83
    {
84
    public:
85
        std::unordered_map<uint16_t, uint16_t> Events;
86
        bool Reboot = false;
87

88
        virtual void Event(uint8_t slaveId,
2✔
89
                           uint8_t eventType,
90
                           uint16_t eventId,
91
                           const uint8_t* data,
92
                           size_t dataSize) override
93
        {
94
            if (eventType == ModbusExt::TEventType::REBOOT) {
2✔
95
                Reboot = true;
1✔
96
                return;
1✔
97
            }
98
            Events[eventId] = data[0] | (data[1] << 8);
1✔
99
        }
100
    };
101

102
    void SetCrc(std::vector<uint8_t>& data)
4✔
103
    {
104
        if (data.size() < 2) {
4✔
UNCOV
105
            return;
×
106
        }
107
        auto crc = CRC16::CalculateCRC16(data.data(), data.size() - 2);
4✔
108
        data[data.size() - 1] = crc & 0xFF;
4✔
109
        data[data.size() - 2] = (crc >> 8) & 0xFF;
4✔
110
    }
111

112
} // namespace
113

114
TEST(TModbusExtTest, EventsEnablerOneReg)
8✔
115
{
116
    TPortMock port;
4✔
117
    port.Response = {0x0A, 0x46, 0x18, 0x01, 0x01, 0xE9, 0x1E};
2✔
118

119
    std::map<uint16_t, bool> response;
4✔
120
    ModbusExt::TEventsEnabler ev(10, port, [&response](uint8_t type, uint16_t reg, bool enabled) {
1✔
121
        response[reg] = enabled;
1✔
122
    });
5✔
123
    ev.AddRegister(101, ModbusExt::TEventType::COIL, ModbusExt::TEventPriority::HIGH);
2✔
124

125
    EXPECT_NO_THROW(ev.SendRequests());
2✔
126

127
    EXPECT_EQ(port.Request.size(), 11);
2✔
128
    EXPECT_EQ(port.Request[0], 0x0A); // slave id
2✔
129
    EXPECT_EQ(port.Request[1], 0x46); // command
2✔
130
    EXPECT_EQ(port.Request[2], 0x18); // subcommand
2✔
131
    EXPECT_EQ(port.Request[3], 0x05); // settings size
2✔
132

133
    EXPECT_EQ(port.Request[4], 0x01); // event type
2✔
134
    EXPECT_EQ(port.Request[5], 0x00); // address MSB
2✔
135
    EXPECT_EQ(port.Request[6], 0x65); // address LSB
2✔
136
    EXPECT_EQ(port.Request[7], 0x01); // count
2✔
137
    EXPECT_EQ(port.Request[8], 0x02); // priority
2✔
138

139
    EXPECT_EQ(port.Request[9], 0xC5);  // CRC16 LSB
2✔
140
    EXPECT_EQ(port.Request[10], 0x90); // CRC16 MSB
2✔
141

142
    EXPECT_EQ(response.size(), 1);
2✔
143
    EXPECT_TRUE(response[101]);
2✔
144
}
2✔
145

146
TEST(TModbusExtTest, EventsEnablerIllegalFunction)
8✔
147
{
148
    TPortMock port;
4✔
149
    port.Response = {0x0A, 0xC6, 0x01, 0xC3, 0xA2};
2✔
150

151
    ModbusExt::TEventsEnabler ev(10, port, [](uint8_t, uint16_t, bool) {});
4✔
152
    ev.AddRegister(101, ModbusExt::TEventType::COIL, ModbusExt::TEventPriority::HIGH);
2✔
153

154
    EXPECT_THROW(ev.SendRequests(), Modbus::TModbusExceptionError);
2✔
155
}
2✔
156

157
TEST(TModbusExtTest, EventsEnablerTwoRanges)
8✔
158
{
159
    TPortMock port;
4✔
160
    // 0x03 = 0b00000011
161
    // 0xFB = 0b11111011
162
    // 0x02 = 0b00000010
163
    port.Response = {0x0A, 0x46, 0x18, 0x03, 0x03, 0xFB, 0x02, 0xAD, 0x11};
2✔
164

165
    std::map<uint16_t, bool> response;
4✔
166
    ModbusExt::TEventsEnabler ev(10, port, [&response](uint8_t type, uint16_t reg, bool enabled) {
12✔
167
        response[reg] = enabled;
12✔
168
    });
16✔
169

170
    ev.AddRegister(101, ModbusExt::TEventType::COIL, ModbusExt::TEventPriority::HIGH);
2✔
171
    ev.AddRegister(102, ModbusExt::TEventType::COIL, ModbusExt::TEventPriority::HIGH);
2✔
172

173
    ev.AddRegister(103, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
174
    ev.AddRegister(104, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
175
    ev.AddRegister(105, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
176
    ev.AddRegister(106, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
177
    ev.AddRegister(107, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
178
    ev.AddRegister(108, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
179
    ev.AddRegister(109, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
180
    ev.AddRegister(110, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
181
    ev.AddRegister(111, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
182
    ev.AddRegister(112, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
183

184
    EXPECT_NO_THROW(ev.SendRequests());
2✔
185

186
    EXPECT_EQ(port.Request.size(), 26);
2✔
187
    EXPECT_EQ(port.Request[0], 0x0A); // slave id
2✔
188
    EXPECT_EQ(port.Request[1], 0x46); // command
2✔
189
    EXPECT_EQ(port.Request[2], 0x18); // subcommand
2✔
190
    EXPECT_EQ(port.Request[3], 0x14); // settings size
2✔
191

192
    EXPECT_EQ(port.Request[4], 0x01); // event type
2✔
193
    EXPECT_EQ(port.Request[5], 0x00); // address MSB
2✔
194
    EXPECT_EQ(port.Request[6], 0x65); // address LSB
2✔
195
    EXPECT_EQ(port.Request[7], 0x02); // count
2✔
196
    EXPECT_EQ(port.Request[8], 0x02); // priority 101
2✔
197
    EXPECT_EQ(port.Request[9], 0x02); // priority 102
2✔
198

199
    EXPECT_EQ(port.Request[10], 0x04); // event type
2✔
200
    EXPECT_EQ(port.Request[11], 0x00); // address MSB
2✔
201
    EXPECT_EQ(port.Request[12], 0x67); // address LSB
2✔
202
    EXPECT_EQ(port.Request[13], 0x0A); // count
2✔
203
    EXPECT_EQ(port.Request[14], 0x01); // priority 103
2✔
204
    EXPECT_EQ(port.Request[15], 0x01); // priority 104
2✔
205
    EXPECT_EQ(port.Request[16], 0x01); // priority 105
2✔
206
    EXPECT_EQ(port.Request[17], 0x01); // priority 106
2✔
207
    EXPECT_EQ(port.Request[18], 0x01); // priority 107
2✔
208
    EXPECT_EQ(port.Request[19], 0x01); // priority 108
2✔
209
    EXPECT_EQ(port.Request[20], 0x01); // priority 109
2✔
210
    EXPECT_EQ(port.Request[21], 0x02); // priority 110
2✔
211
    EXPECT_EQ(port.Request[22], 0x02); // priority 111
2✔
212
    EXPECT_EQ(port.Request[23], 0x02); // priority 112
2✔
213

214
    EXPECT_EQ(port.Request[24], 0x25); // CRC16 LSB
2✔
215
    EXPECT_EQ(port.Request[25], 0xF0); // CRC16 MSB
2✔
216

217
    EXPECT_EQ(response.size(), 12);
2✔
218
    EXPECT_TRUE(response[101]);
2✔
219
    EXPECT_TRUE(response[102]);
2✔
220
    EXPECT_TRUE(response[103]);
2✔
221
    EXPECT_TRUE(response[104]);
2✔
222
    EXPECT_FALSE(response[105]);
2✔
223
    EXPECT_TRUE(response[106]);
2✔
224
    EXPECT_TRUE(response[107]);
2✔
225
    EXPECT_TRUE(response[108]);
2✔
226
    EXPECT_TRUE(response[109]);
2✔
227
    EXPECT_TRUE(response[110]);
2✔
228
    EXPECT_FALSE(response[111]);
2✔
229
    EXPECT_TRUE(response[112]);
2✔
230
}
2✔
231

232
TEST(TModbusExtTest, EventsEnablerRangesWithHoles)
8✔
233
{
234
    TPortMock port;
4✔
235
    // 0x03 = 0b00000011
236
    // 0xF3 = 0b11110011
237
    // 0x02 = 0b00000010
238
    // 0x03 = 0b00000011
239
    port.Response = {0x0A, 0x46, 0x18, 0x04, 0x03, 0xF3, 0x02, 0x03, 0xA4, 0xBE};
2✔
240

241
    std::map<uint16_t, bool> response;
4✔
242
    ModbusExt::TEventsEnabler ev(
243
        10,
244
        port,
245
        [&response](uint8_t type, uint16_t reg, bool enabled) { response[reg] = enabled; },
14✔
246
        ModbusExt::TEventsEnabler::DISABLE_EVENTS_IN_HOLES);
4✔
247

248
    ev.AddRegister(101, ModbusExt::TEventType::COIL, ModbusExt::TEventPriority::HIGH);
2✔
249
    ev.AddRegister(102, ModbusExt::TEventType::COIL, ModbusExt::TEventPriority::HIGH);
2✔
250
    ev.AddRegister(103, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
251
    ev.AddRegister(104, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
252
    ev.AddRegister(107, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
253
    ev.AddRegister(108, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
254
    ev.AddRegister(109, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::LOW);
2✔
255
    ev.AddRegister(110, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
256
    ev.AddRegister(111, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
257
    ev.AddRegister(112, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
258
    ev.AddRegister(118, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
259
    ev.AddRegister(119, ModbusExt::TEventType::INPUT, ModbusExt::TEventPriority::HIGH);
2✔
260

261
    EXPECT_NO_THROW(ev.SendRequests());
2✔
262

263
    EXPECT_EQ(port.Request.size(), 32);
2✔
264
    EXPECT_EQ(port.Request[0], 0x0A); // slave id
2✔
265
    EXPECT_EQ(port.Request[1], 0x46); // command
2✔
266
    EXPECT_EQ(port.Request[2], 0x18); // subcommand
2✔
267
    EXPECT_EQ(port.Request[3], 0x1A); // settings size
2✔
268

269
    EXPECT_EQ(port.Request[4], 0x01); // event type
2✔
270
    EXPECT_EQ(port.Request[5], 0x00); // address MSB
2✔
271
    EXPECT_EQ(port.Request[6], 0x65); // address LSB
2✔
272
    EXPECT_EQ(port.Request[7], 0x02); // count
2✔
273
    EXPECT_EQ(port.Request[8], 0x02); // priority 101
2✔
274
    EXPECT_EQ(port.Request[9], 0x02); // priority 102
2✔
275

276
    EXPECT_EQ(port.Request[10], 0x04); // event type
2✔
277
    EXPECT_EQ(port.Request[11], 0x00); // address MSB
2✔
278
    EXPECT_EQ(port.Request[12], 0x67); // address LSB
2✔
279
    EXPECT_EQ(port.Request[13], 0x0A); // count
2✔
280
    EXPECT_EQ(port.Request[14], 0x01); // priority 103
2✔
281
    EXPECT_EQ(port.Request[15], 0x01); // priority 104
2✔
282
    EXPECT_EQ(port.Request[16], 0x00); // priority 105
2✔
283
    EXPECT_EQ(port.Request[17], 0x00); // priority 106
2✔
284
    EXPECT_EQ(port.Request[18], 0x01); // priority 107
2✔
285
    EXPECT_EQ(port.Request[19], 0x01); // priority 108
2✔
286
    EXPECT_EQ(port.Request[20], 0x01); // priority 109
2✔
287
    EXPECT_EQ(port.Request[21], 0x02); // priority 110
2✔
288
    EXPECT_EQ(port.Request[22], 0x02); // priority 111
2✔
289
    EXPECT_EQ(port.Request[23], 0x02); // priority 112
2✔
290

291
    EXPECT_EQ(port.Request[24], 0x04); // event type
2✔
292
    EXPECT_EQ(port.Request[25], 0x00); // address MSB
2✔
293
    EXPECT_EQ(port.Request[26], 0x76); // address LSB
2✔
294
    EXPECT_EQ(port.Request[27], 0x02); // count
2✔
295
    EXPECT_EQ(port.Request[28], 0x02); // priority 118
2✔
296
    EXPECT_EQ(port.Request[29], 0x02); // priority 119
2✔
297

298
    EXPECT_EQ(port.Request[30], 0x81); // CRC16 LSB
2✔
299
    EXPECT_EQ(port.Request[31], 0x54); // CRC16 MSB
2✔
300

301
    EXPECT_EQ(response.size(), 14);
2✔
302
    EXPECT_TRUE(response[101]);
2✔
303
    EXPECT_TRUE(response[102]);
2✔
304
    EXPECT_TRUE(response[103]);
2✔
305
    EXPECT_TRUE(response[104]);
2✔
306
    EXPECT_FALSE(response[105]);
2✔
307
    EXPECT_FALSE(response[106]);
2✔
308
    EXPECT_TRUE(response[107]);
2✔
309
    EXPECT_TRUE(response[108]);
2✔
310
    EXPECT_TRUE(response[109]);
2✔
311
    EXPECT_TRUE(response[110]);
2✔
312
    EXPECT_FALSE(response[111]);
2✔
313
    EXPECT_TRUE(response[112]);
2✔
314
    EXPECT_EQ(response.count(113), 0);
2✔
315
    EXPECT_EQ(response.count(114), 0);
2✔
316
    EXPECT_EQ(response.count(115), 0);
2✔
317
    EXPECT_EQ(response.count(116), 0);
2✔
318
    EXPECT_EQ(response.count(117), 0);
2✔
319
    EXPECT_TRUE(response[118]);
2✔
320
    EXPECT_TRUE(response[119]);
2✔
321
}
2✔
322

323
TEST(TModbusExtTest, ReadEventsNoEventsNoConfirmation)
8✔
324
{
325
    TPortMock port;
4✔
326
    TTestEventsVisitor visitor;
4✔
327
    ModbusExt::TEventConfirmationState state;
2✔
328

329
    port.Response = {0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x12, 0x52, 0x5D}; // No events
2✔
330
    bool ret = true;
2✔
331
    EXPECT_NO_THROW(ret = ModbusExt::ReadEvents(port, std::chrono::milliseconds(100), 0, state, visitor));
2✔
332
    EXPECT_FALSE(ret);
2✔
333

334
    EXPECT_EQ(port.Request.size(), 9);
2✔
335
    EXPECT_EQ(port.Request[0], 0xFD); // broadcast
2✔
336
    EXPECT_EQ(port.Request[1], 0x46); // command
2✔
337
    EXPECT_EQ(port.Request[2], 0x10); // subcommand
2✔
338
    EXPECT_EQ(port.Request[3], 0x00); // min slave id
2✔
339
    EXPECT_EQ(port.Request[4], 0xF8); // max length
2✔
340
    EXPECT_EQ(port.Request[5], 0x00); // slave id (confirmation)
2✔
341
    EXPECT_EQ(port.Request[6], 0x00); // flag (confirmation)
2✔
342

343
    EXPECT_EQ(port.Request[7], 0x79); // CRC16 LSB
2✔
344
    EXPECT_EQ(port.Request[8], 0x5B); // CRC16 MSB
2✔
345

346
    EXPECT_EQ(visitor.Events.size(), 0);
2✔
347
}
2✔
348

349
TEST(TModbusExtTest, ReadEventsWithConfirmation)
8✔
350
{
351
    TPortMock port;
4✔
352
    TTestEventsVisitor visitor;
4✔
353
    ModbusExt::TEventConfirmationState state;
2✔
354

UNCOV
355
    port.Response =
×
356
        {0xFF, 0xFF, 0xFF, 0x05, 0x46, 0x11, 0x01, 0x01, 0x06, 0x02, 0x04, 0x01, 0xD0, 0x04, 0x00, 0x2B, 0xAC};
2✔
357
    bool ret = false;
2✔
358
    EXPECT_NO_THROW(ret = ModbusExt::ReadEvents(port, std::chrono::milliseconds(100), 0, state, visitor));
2✔
359
    EXPECT_TRUE(ret);
2✔
360

361
    EXPECT_EQ(port.Request.size(), 9);
2✔
362
    EXPECT_EQ(port.Request[0], 0xFD); // slave id
2✔
363
    EXPECT_EQ(port.Request[1], 0x46); // command
2✔
364
    EXPECT_EQ(port.Request[2], 0x10); // subcommand
2✔
365
    EXPECT_EQ(port.Request[3], 0x00); // min slave id
2✔
366
    EXPECT_EQ(port.Request[4], 0xF8); // max length
2✔
367
    EXPECT_EQ(port.Request[5], 0x00); // slave id (confirmation)
2✔
368
    EXPECT_EQ(port.Request[6], 0x00); // flag (confirmation)
2✔
369

370
    EXPECT_EQ(port.Request[7], 0x79); // CRC16 LSB
2✔
371
    EXPECT_EQ(port.Request[8], 0x5B); // CRC16 MSB
2✔
372

373
    EXPECT_EQ(visitor.Events.size(), 1);
2✔
374
    EXPECT_EQ(visitor.Events[464], 4);
2✔
375

376
    port.Response = {0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x12, 0x52, 0x5D}; // No events
2✔
377
    visitor.Events.clear();
2✔
378
    EXPECT_NO_THROW(ret = ModbusExt::ReadEvents(port, std::chrono::milliseconds(5), 5, state, visitor));
2✔
379
    EXPECT_FALSE(ret);
2✔
380

381
    EXPECT_EQ(port.Request.size(), 9);
2✔
382
    EXPECT_EQ(port.Request[0], 0xFD); // slave id
2✔
383
    EXPECT_EQ(port.Request[1], 0x46); // command
2✔
384
    EXPECT_EQ(port.Request[2], 0x10); // subcommand
2✔
385
    EXPECT_EQ(port.Request[3], 0x05); // min slave id
2✔
386
    EXPECT_EQ(port.Request[4], 0x2C); // max length
2✔
387
    EXPECT_EQ(port.Request[5], 0x05); // slave id (confirmation)
2✔
388
    EXPECT_EQ(port.Request[6], 0x01); // flag (confirmation)
2✔
389

390
    EXPECT_EQ(port.Request[7], 0xFB); // CRC16 LSB
2✔
391
    EXPECT_EQ(port.Request[8], 0x3F); // CRC16 MSB
2✔
392

393
    EXPECT_EQ(visitor.Events.size(), 0);
2✔
394
}
2✔
395

396
TEST(TModbusExtTest, ReadEventsReboot)
8✔
397
{
398
    TPortMock port;
4✔
399
    TTestEventsVisitor visitor;
4✔
400
    ModbusExt::TEventConfirmationState state;
2✔
401

UNCOV
402
    port.Response =
×
403
        {0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x11, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0xFF, 0x5E}; // Reboot event
2✔
404
    bool ret = false;
2✔
405
    EXPECT_NO_THROW(ret = ModbusExt::ReadEvents(port, std::chrono::milliseconds(100), 0, state, visitor));
2✔
406
    EXPECT_TRUE(ret);
2✔
407

408
    EXPECT_TRUE(visitor.Reboot);
2✔
409
}
2✔
410

411
TEST(TModbusExtTest, GetPacketStart)
8✔
412
{
413
    uint8_t goodPacket[] = {0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x11, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0xFF, 0x5E};
2✔
414
    uint8_t goodPacketWithGlitch[] = {0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFD, 0x46, 0x12, 0x52, 0x5D};
2✔
415
    EXPECT_EQ(ModbusExt::GetPacketStart(goodPacket, sizeof(goodPacket)), goodPacket + 3);
2✔
416
    EXPECT_EQ(ModbusExt::GetPacketStart(goodPacketWithGlitch, sizeof(goodPacketWithGlitch)), goodPacketWithGlitch + 5);
2✔
417

418
    uint8_t notEnoughDataPacket[] = {0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x11, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00};
2✔
419
    uint8_t badCrcPacket[] = {0xFF, 0xFF, 0xFF, 0xFD, 0x46, 0x11, 0x00, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x11, 0x5E};
2✔
420
    EXPECT_EQ(ModbusExt::GetPacketStart(notEnoughDataPacket, sizeof(notEnoughDataPacket)), nullptr);
2✔
421
    EXPECT_EQ(ModbusExt::GetPacketStart(badCrcPacket, sizeof(badCrcPacket)), nullptr);
2✔
422
}
2✔
423

424
class TModbusExtTraitsTest: public testing::Test
425
{
426
public:
427
    void TestEqual(const std::vector<uint8_t>& b1, const std::vector<uint8_t>& b2) const
1✔
428
    {
429
        ASSERT_EQ(b1.size(), b2.size());
1✔
430

431
        for (size_t i = 0; i < b1.size(); ++i) {
6✔
432
            ASSERT_EQ(b1[i], b2[i]) << i;
5✔
433
        }
434
    }
435
};
436

437
TEST_F(TModbusExtTraitsTest, ReadFrameGood)
8✔
438
{
439
    TPortMock port;
2✔
440
    port.Response = {0xfd, 0x46, 0x09, 0xfe, 0xca, 0xe7, 0xe5, 0x06, 0x00, 0x80, 0x00, 0x02, 0x1c, 0xef};
2✔
441
    ModbusExt::TModbusTraits traits;
2✔
442
    traits.SetSn(0xfecae7e5);
2✔
443
    std::chrono::milliseconds t(10);
2✔
444

445
    std::vector<uint8_t> req = {0x06, 0x00, 0x80, 0x00, 0x02};
2✔
446

447
    auto resp = traits.Transaction(port, 0, req, req.size(), t, t);
2✔
448
    ASSERT_EQ(resp.Pdu.size(), req.size());
2✔
449

450
    TestEqual(resp.Pdu, req);
2✔
451
}
452

453
TEST_F(TModbusExtTraitsTest, ReadFrameTooSmallError)
8✔
454
{
455
    TPortMock port;
2✔
456
    port.Response = {0xfd, 0x46, 0x09, 0xfe, 0xca};
2✔
457
    ModbusExt::TModbusTraits traits;
2✔
458
    traits.SetSn(0xfecae7e5);
2✔
459
    std::chrono::milliseconds t(10);
2✔
460

461
    std::vector<uint8_t> req = {0x06, 0x00, 0x80, 0x00, 0x02};
2✔
462

463
    ASSERT_THROW(traits.Transaction(port, 0, req, req.size(), t, t), Modbus::TMalformedResponseError);
2✔
464
}
465

466
TEST_F(TModbusExtTraitsTest, ReadFrameInvalidCrc)
8✔
467
{
468
    TPortMock port;
2✔
469
    port.Response = {0xfd, 0x46, 0x09, 0xfe, 0xca, 0xe7, 0xe5, 0x06, 0x00, 0x80, 0x00, 0x02, 0x10, 0xef};
2✔
470
    ModbusExt::TModbusTraits traits;
2✔
471
    traits.SetSn(0xfecae7e5);
2✔
472
    std::chrono::milliseconds t(10);
2✔
473

474
    std::vector<uint8_t> req = {0x06, 0x00, 0x80, 0x00, 0x02};
2✔
475

476
    ASSERT_THROW(traits.Transaction(port, 0, req, req.size(), t, t), Modbus::TMalformedResponseError);
2✔
477
}
478

479
TEST_F(TModbusExtTraitsTest, ReadFrameInvalidHeader)
4✔
480
{
481
    TPortMock port;
1✔
482
    port.Response = {0xfe, 0x46, 0x09, 0xfe, 0xca, 0xe7, 0xe5, 0x06, 0x00, 0x80, 0x00, 0x02, 0x1c, 0xef};
1✔
483
    SetCrc(port.Response);
1✔
484

485
    ModbusExt::TModbusTraits traits;
1✔
486
    traits.SetSn(0xfecae7e5);
1✔
487
    std::chrono::milliseconds t(10);
1✔
488

489
    std::vector<uint8_t> req = {0x06, 0x00, 0x80, 0x00, 0x02};
1✔
490

491
    ASSERT_THROW(
2✔
492
        {
493
            try {
494
                traits.Transaction(port, 0, req, req.size(), t, t);
495
            } catch (const Modbus::TUnexpectedResponseError& e) {
496
                EXPECT_STREQ("invalid response address", e.what());
497
                throw;
498
            }
499
        },
500
        Modbus::TUnexpectedResponseError);
501

502
    port.Response[0] = 0xfd;
1✔
503
    port.Response[1] = 0x45;
1✔
504
    SetCrc(port.Response);
1✔
505
    ASSERT_THROW(
2✔
506
        {
507
            try {
508
                traits.Transaction(port, 0, req, req.size(), t, t);
509
            } catch (const Modbus::TUnexpectedResponseError& e) {
510
                EXPECT_STREQ("invalid response command", e.what());
511
                throw;
512
            }
513
        },
514
        Modbus::TUnexpectedResponseError);
515

516
    port.Response[1] = 0x46;
1✔
517
    port.Response[2] = 0x10;
1✔
518
    SetCrc(port.Response);
1✔
519
    ASSERT_THROW(
2✔
520
        {
521
            try {
522
                traits.Transaction(port, 0, req, req.size(), t, t);
523
            } catch (const Modbus::TUnexpectedResponseError& e) {
524
                EXPECT_STREQ("invalid response subcommand", e.what());
525
                throw;
526
            }
527
        },
528
        Modbus::TUnexpectedResponseError);
529

530
    port.Response[2] = 0x09;
1✔
531
    port.Response[3] = 0x01;
1✔
532
    SetCrc(port.Response);
1✔
533
    ASSERT_THROW(
2✔
534
        {
535
            try {
536
                traits.Transaction(port, 0, req, req.size(), t, t);
537
            } catch (const Modbus::TUnexpectedResponseError& e) {
538
                EXPECT_STREQ("SN mismatch: got 30074853, wait 4274710501", e.what());
539
                throw;
540
            }
541
        },
542
        Modbus::TUnexpectedResponseError);
543
}
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