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

wirenboard / wb-mqtt-serial / 666

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

push

github

web-flow
Port handling refactoring (#960)

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

6444 of 9057 branches covered (71.15%)

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

6 existing lines in 5 files now uncovered.

12341 of 16710 relevant lines covered (73.85%)

305.53 hits per line

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

78.95
/src/devices/pulsar_device.cpp
1
/* vim: set ts=4 sw=4: */
2

3
#include <cstdlib>
4
#include <cstring>
5

6
#include "pulsar_device.h"
7

8
namespace
9
{
10
    const TRegisterTypes RegisterTypes{{TPulsarDevice::REG_DEFAULT, "default", "value", Double, true},
11
                                       {TPulsarDevice::REG_SYSTIME, "systime", "value", U64, true}};
12
}
13

14
void TPulsarDevice::Register(TSerialDeviceFactory& factory)
143✔
15
{
16
    factory.RegisterProtocol(
143✔
17
        new TUint32SlaveIdProtocol("pulsar", RegisterTypes),
286✔
18
        new TBasicDeviceFactory<TPulsarDevice>("#/definitions/simple_device", "#/definitions/common_channel"));
286✔
19
}
143✔
20

21
TPulsarDevice::TPulsarDevice(PDeviceConfig config, PProtocol protocol)
1✔
22
    : TSerialDevice(config, protocol),
23
      TUInt32SlaveId(config->SlaveId),
2✔
24
      RequestID(0)
1✔
25
{}
1✔
26

27
uint16_t TPulsarDevice::CalculateCRC16(const uint8_t* buffer, size_t size)
2✔
28
{
29
    uint16_t w;
30
    uint16_t shift_cnt, f;
31
    const uint8_t* ptrByte;
32

33
    uint16_t byte_cnt = size;
2✔
34
    ptrByte = buffer;
2✔
35
    w = (uint16_t)0xFFFF;
2✔
36

37
    for (; byte_cnt > 0; byte_cnt--) {
26✔
38
        w = (uint16_t)(w ^ (uint16_t)(*ptrByte++));
24✔
39
        for (shift_cnt = 0; shift_cnt < 8; shift_cnt++) {
216✔
40
            f = (uint8_t)((w)&0x01);
192✔
41
            w >>= 1;
192✔
42
            if (f)
192✔
43
                w = (uint16_t)(w ^ 0xa001);
87✔
44
        }
45
    }
46

47
    return w;
2✔
48
}
49

50
void TPulsarDevice::WriteBCD(uint64_t value, uint8_t* buffer, size_t size, bool big_endian)
1✔
51
{
52
    for (size_t i = 0; i < size; i++) {
5✔
53
        // form byte from the end of value
54
        uint8_t byte = value % 10;
4✔
55
        value /= 10;
4✔
56
        byte |= (value % 10) << 4;
4✔
57
        value /= 10;
4✔
58

59
        buffer[big_endian ? size - i - 1 : i] = byte;
4✔
60
    }
61
}
1✔
62

63
void TPulsarDevice::WriteHex(uint64_t value, uint8_t* buffer, size_t size, bool big_endian)
3✔
64
{
65
    for (size_t i = 0; i < size; i++) {
11✔
66
        buffer[big_endian ? size - i - 1 : i] = value & 0xFF;
8✔
67
        value >>= 8;
8✔
68
    }
69
}
3✔
70

71
uint64_t TPulsarDevice::ReadBCD(const uint8_t* buffer, size_t size, bool big_endian)
1✔
72
{
73
    uint64_t result = 0;
1✔
74

75
    for (size_t i = 0; i < size; i++) {
5✔
76
        result *= 100;
4✔
77

78
        uint8_t bcd_byte = buffer[big_endian ? i : size - i - 1];
4✔
79
        uint8_t dec_byte = (bcd_byte & 0x0F) + 10 * ((bcd_byte >> 4) & 0x0F);
4✔
80

81
        result += dec_byte;
4✔
82
    }
83

84
    return result;
1✔
85
}
86

87
uint64_t TPulsarDevice::ReadHex(const uint8_t* buffer, size_t size, bool big_endian)
3✔
88
{
89
    uint64_t result = 0;
3✔
90

91
    for (size_t i = 0; i < size; i++) {
11✔
92
        result <<= 8;
8✔
93
        result |= buffer[big_endian ? i : size - i - 1];
8✔
94
    }
95

96
    return result;
3✔
97
}
98

99
void TPulsarDevice::WriteDataRequest(TPort& port, uint32_t addr, uint32_t mask, uint16_t id)
1✔
100
{
101
    port.CheckPortOpen();
1✔
102

103
    uint8_t buf[14];
104

105
    /* header = device address in big-endian BCD */
106
    WriteBCD(addr, buf, sizeof(uint32_t), true);
1✔
107

108
    /* data request => F == 1, L == 14 */
109
    buf[4] = 1;
1✔
110
    buf[5] = 14;
1✔
111

112
    /* data mask in little-endian */
113
    WriteHex(mask, &buf[6], sizeof(uint32_t), false);
1✔
114

115
    /* request ID */
116
    WriteHex(id, &buf[10], sizeof(uint16_t), false);
1✔
117

118
    /* CRC16 */
119
    uint16_t crc = CalculateCRC16(buf, 12);
1✔
120
    WriteHex(crc, &buf[12], sizeof(uint16_t), false);
1✔
121

122
    port.WriteBytes(buf, 14);
1✔
123
}
1✔
124

NEW
125
void TPulsarDevice::WriteSysTimeRequest(TPort& port, uint32_t addr, uint16_t id)
×
126
{
NEW
127
    port.CheckPortOpen();
×
128

129
    uint8_t buf[10];
130

131
    /* header = device address in big-endian BCD */
132
    WriteBCD(addr, buf, sizeof(uint32_t), true);
×
133

134
    /* sys time request => F == 4, L == 10 */
135
    buf[4] = 1;
×
136
    buf[5] = 10;
×
137

138
    /* request ID */
139
    WriteHex(id, &buf[6], sizeof(uint16_t), false);
×
140

141
    /* CRC16 */
142
    uint16_t crc = CalculateCRC16(buf, 8);
×
143
    WriteHex(crc, &buf[8], sizeof(uint16_t), false);
×
144

NEW
145
    port.WriteBytes(buf, 10);
×
146
}
147

148
void TPulsarDevice::ReadResponse(TPort& port, uint32_t addr, uint8_t* payload, size_t size, uint16_t id)
1✔
149
{
150
    const int exp_size = size + 10; /* payload size + service bytes */
1✔
151
    std::vector<uint8_t> response(exp_size);
1✔
152

153
    int nread = port.ReadFrame(response.data(),
1✔
154
                               response.size(),
155
                               GetResponseTimeout(port),
1✔
156
                               GetFrameTimeout(port),
1✔
157
                               [](uint8_t* buf, int size) { return size >= 6 && size == buf[5]; })
17✔
158
                    .Count;
1✔
159

160
    /* check size */
161
    if (nread < 6)
1✔
162
        throw TSerialDeviceTransientErrorException("frame is too short");
×
163

164
    if (nread != exp_size)
1✔
165
        throw TSerialDeviceTransientErrorException("unexpected end of frame");
×
166

167
    if (exp_size != response[5])
1✔
168
        throw TSerialDeviceTransientErrorException("unexpected frame length");
×
169

170
    /* check CRC16 */
171
    uint16_t crc_recv = ReadHex(&response[nread - 2], sizeof(uint16_t), false);
1✔
172
    if (crc_recv != CalculateCRC16(response.data(), nread - 2))
1✔
173
        throw TSerialDeviceTransientErrorException("CRC mismatch");
×
174

175
    /* check address */
176
    uint32_t addr_recv = ReadBCD(response.data(), sizeof(uint32_t), true);
1✔
177
    if (addr_recv != addr)
1✔
178
        throw TSerialDeviceTransientErrorException("slave address mismatch");
×
179

180
    /* check request ID */
181
    uint16_t id_recv = ReadHex(&response[nread - 4], sizeof(uint16_t), false);
1✔
182
    if (id_recv != id)
1✔
183
        throw TSerialDeviceTransientErrorException("request ID mismatch");
×
184

185
    /* copy payload data to external buffer */
186
    memcpy(payload, response.data() + 6, size);
1✔
187
}
1✔
188

189
TRegisterValue TPulsarDevice::ReadDataRegister(TPort& port, const TRegisterConfig& reg)
1✔
190
{
191
    auto addr = GetUint32RegisterAddress(reg.GetAddress());
1✔
192
    // raw payload data
193
    uint8_t payload[sizeof(uint64_t)];
194

195
    // form register mask from address
196
    uint32_t mask = 1 << addr;
1✔
197

198
    // send data request and receive response
199
    WriteDataRequest(port, SlaveId, mask, RequestID);
1✔
200
    ReadResponse(port, SlaveId, payload, reg.GetByteWidth(), RequestID);
1✔
201

202
    ++RequestID;
1✔
203

204
    // decode little-endian double64_t value
205
    return TRegisterValue{ReadHex(payload, reg.GetByteWidth(), false)};
2✔
206
}
207

NEW
208
TRegisterValue TPulsarDevice::ReadSysTimeRegister(TPort& port, const TRegisterConfig& reg)
×
209
{
210
    // raw payload data
211
    uint8_t payload[6];
212

213
    // send system time request and receive response
NEW
214
    WriteSysTimeRequest(port, SlaveId, RequestID);
×
NEW
215
    ReadResponse(port, SlaveId, payload, sizeof(payload), RequestID);
×
216

217
    ++RequestID;
×
218

219
    // decode little-endian double64_t value
220
    return TRegisterValue{ReadHex(payload, sizeof(payload), false)};
×
221
}
222

223
TRegisterValue TPulsarDevice::ReadRegisterImpl(TPort& port, const TRegisterConfig& reg)
1✔
224
{
225
    port.SkipNoise();
1✔
226

227
    switch (reg.Type) {
1✔
228
        case REG_DEFAULT:
1✔
229
            return ReadDataRegister(port, reg);
1✔
230
        case REG_SYSTIME:
×
NEW
231
            return ReadSysTimeRegister(port, reg);
×
232
        default:
×
233
            throw TSerialDeviceException("Pulsar protocol: wrong register type");
×
234
    }
235
}
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