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

wirenboard / wb-mqtt-serial / 1

08 Jul 2025 01:20PM UTC coverage: 73.854% (+1.0%) from 72.836%
1

Pull #963

github

39d9bc
KraPete
Bump version
Pull Request #963: Bump version

6444 of 9057 branches covered (71.15%)

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

92.54
/src/devices/mercury200_device.cpp
1
#include <chrono>
2
#include <cstdint>
3

4
#include "bcd_utils.h"
5
#include "crc16.h"
6
#include "mercury200_device.h"
7
#include "serial_device.h"
8

9
namespace
10
{
11
    const size_t RESPONSE_BUF_LEN = 100;
12
    const size_t REQUEST_LEN = 7;
13
    const ptrdiff_t HEADER_SZ = 5;
14

15
    const TRegisterTypes RegisterTypes{{0, "param8", "value", U8, true},
16
                                       {0, "param16", "value", BCD16, true},
17
                                       {0, "param24", "value", BCD24, true},
18
                                       {0, "param32", "value", BCD32, true}};
19

20
    class TMercury200DeviceRegisterAddressFactory: public TUint32RegisterAddressFactory
21
    {
22
    public:
23
        TRegisterDesc LoadRegisterAddress(const Json::Value& regCfg,
8✔
24
                                          const IRegisterAddress& deviceBaseAddress,
25
                                          uint32_t stride,
26
                                          uint32_t registerByteWidth) const override
27
        {
28
            auto addr = LoadRegisterBitsAddress(regCfg, SerialConfig::ADDRESS_PROPERTY_NAME);
8✔
29
            TRegisterDesc res;
8✔
30
            res.DataOffset = (addr.Address & 0xFF);
8✔
31
            res.Address = std::make_shared<TUint32RegisterAddress>(addr.Address >> 8);
8✔
32
            return res;
16✔
33
        }
34
    };
35
}
36

37
void TMercury200Device::Register(TSerialDeviceFactory& factory)
143✔
38
{
39
    factory.RegisterProtocol(new TUint32SlaveIdProtocol("mercury200", RegisterTypes),
429✔
40
                             new TBasicDeviceFactory<TMercury200Device, TMercury200DeviceRegisterAddressFactory>(
41
                                 "#/definitions/simple_device",
42
                                 "#/definitions/common_channel"));
286✔
43
}
143✔
44

45
TMercury200Device::TMercury200Device(PDeviceConfig config, PProtocol protocol)
4✔
46
    : TSerialDevice(config, protocol),
47
      TUInt32SlaveId(config->SlaveId)
4✔
48
{}
4✔
49

50
std::vector<uint8_t> TMercury200Device::ExecCommand(TPort& port, uint8_t cmd)
16✔
51
{
52
    auto it = CmdResultCache.find(cmd);
16✔
53
    if (it != CmdResultCache.end()) {
16✔
54
        return it->second;
10✔
55
    }
56

57
    uint8_t buf[100] = {0x00};
6✔
58
    auto readn = RequestResponse(port, SlaveId, cmd, buf);
6✔
59
    if (readn < 4) { // fixme 4
6✔
60
        throw TSerialDeviceTransientErrorException("mercury200: read frame too short for command response");
×
61
    }
62
    if (IsBadHeader(SlaveId, cmd, buf)) {
6✔
63
        throw TSerialDeviceTransientErrorException("mercury200: bad response header for command");
×
64
    }
65
    if (IsCrcValid(buf, readn)) {
6✔
66
        throw TSerialDeviceTransientErrorException("mercury200: bad CRC for command");
×
67
    }
68
    uint8_t* payload = buf + HEADER_SZ;
6✔
69
    std::vector<uint8_t> result = {0};
6✔
70
    result.assign(payload, payload + readn - HEADER_SZ);
6✔
71
    return CmdResultCache.insert({cmd, result}).first->second;
12✔
72
}
73

74
TRegisterValue TMercury200Device::ReadRegisterImpl(TPort& port, const TRegisterConfig& reg)
16✔
75
{
76
    uint8_t cmd = (GetUint32RegisterAddress(reg.GetAddress()) & 0xFF);
16✔
77
    auto result = ExecCommand(port, cmd);
32✔
78
    auto size = RegisterFormatByteWidth(reg.Format);
16✔
79
    if (result.size() < reg.GetDataOffset() + size)
16✔
80
        throw TSerialDeviceException("mercury200: register address is out of range");
×
81

82
    return TRegisterValue{PackBytes(result.data() + reg.GetDataOffset(), static_cast<WordSizes>(size))};
32✔
83
}
84

85
void TMercury200Device::InvalidateReadCache()
5✔
86
{
87
    CmdResultCache.clear();
5✔
88

89
    TSerialDevice::InvalidateReadCache();
5✔
90
}
5✔
91

92
bool TMercury200Device::IsCrcValid(uint8_t* buf, int sz) const
6✔
93
{
94
    auto actual_crc = CRC16::CalculateCRC16(buf, static_cast<uint16_t>(sz));
6✔
95
    uint16_t sent_crc = ((uint16_t)buf[sz] << 8) | ((uint16_t)buf[sz + 1]);
6✔
96
    return actual_crc != sent_crc;
6✔
97
}
98

99
int TMercury200Device::RequestResponse(TPort& port, uint32_t slave, uint8_t cmd, uint8_t* response) const
6✔
100
{
101
    using namespace std::chrono;
102

103
    uint8_t request[REQUEST_LEN];
104
    FillCommand(request, slave, cmd);
6✔
105
    port.WriteBytes(request, REQUEST_LEN);
6✔
106
    return port.ReadFrame(response, RESPONSE_BUF_LEN, GetResponseTimeout(port), GetFrameTimeout(port)).Count;
6✔
107
}
108

109
void TMercury200Device::FillCommand(uint8_t* buf, uint32_t id, uint8_t cmd) const
6✔
110
{
111
    buf[0] = static_cast<uint8_t>(id >> 24);
6✔
112
    buf[1] = static_cast<uint8_t>(id >> 16);
6✔
113
    buf[2] = static_cast<uint8_t>(id >> 8);
6✔
114
    buf[3] = static_cast<uint8_t>(id);
6✔
115
    buf[4] = cmd;
6✔
116
    auto crc = CRC16::CalculateCRC16(buf, 5);
6✔
117
    buf[5] = static_cast<uint8_t>(crc >> 8);
6✔
118
    buf[6] = static_cast<uint8_t>(crc);
6✔
119
}
6✔
120

121
bool TMercury200Device::IsBadHeader(uint32_t slave_expected, uint8_t cmd_expected, uint8_t* response) const
6✔
122
{
123
    uint32_t actual_slave = ((uint32_t)response[0] << 24) | ((uint32_t)response[1] << 16) |
6✔
124
                            ((uint32_t)response[2] << 8) | (uint32_t)response[3];
6✔
125
    if (actual_slave != slave_expected) {
6✔
126
        return true;
×
127
    }
128
    return response[4] != cmd_expected;
6✔
129
}
130

131
std::chrono::milliseconds TMercury200Device::GetFrameTimeout(TPort& port) const
7✔
132
{
133
    return std::max(DeviceConfig()->FrameTimeout,
14✔
134
                    std::chrono::ceil<std::chrono::milliseconds>(port.GetSendTimeBytes(6)));
14✔
135
}
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