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

wirenboard / wb-mqtt-iec104 / 38

31 Jul 2026 10:43AM UTC coverage: 52.549% (-0.2%) from 52.705%
38

push

github

web-flow
Use exported vars instead of MAKEFLAGS for coverage options (#30)

236 of 389 branches covered (60.67%)

402 of 765 relevant lines covered (52.55%)

3.58 hits per line

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

80.0
/src/gateway.cpp
1
#include "gateway.h"
2

3
#include "log.h"
4

5
using namespace std;
6
using namespace WBMQTT;
7

8
#define LOG(logger) ::logger.Log() << "[gateway] "
9

10
namespace
11
{
12
    bool Append(IEC104::TInformationObjects& objs,
18✔
13
                PControl control,
14
                const std::string& v,
15
                const TIecInformationObject& obj) noexcept
16
    {
17
        if (v.empty()) {
18✔
18
            return false;
×
19
        }
20

21
        auto now = std::chrono::system_clock::now();
18✔
22

23
        try {
24
            switch (obj.Type) {
18✔
25
                case SinglePoint: {
3✔
26
                    if (v == "0") {
3✔
27
                        objs.SinglePoint.emplace_back(obj.Address, false);
1✔
28
                        return true;
1✔
29
                    }
30
                    if (v == "1") {
2✔
31
                        objs.SinglePoint.emplace_back(obj.Address, true);
2✔
32
                        return true;
2✔
33
                    }
34
                    throw std::runtime_error(v + " is not convertible to bool");
×
35
                }
36
                case MeasuredValueShort: {
3✔
37
                    objs.MeasuredValueShort.emplace_back(obj.Address, stof(v));
3✔
38
                    return true;
3✔
39
                }
40
                case MeasuredValueScaled: {
3✔
41
                    objs.MeasuredValueScaled.emplace_back(obj.Address, stoi(v));
3✔
42
                    return true;
3✔
43
                }
44
                case SinglePointWithTimestamp: {
3✔
45
                    if (v == "0") {
3✔
46
                        objs.SinglePointWithTimestamp.emplace_back(obj.Address, now, false);
2✔
47
                        return true;
2✔
48
                    }
49
                    if (v == "1") {
1✔
50
                        objs.SinglePointWithTimestamp.emplace_back(obj.Address, now, true);
1✔
51
                        return true;
1✔
52
                    }
53
                    throw std::runtime_error(v + " is not convertible to bool");
×
54
                }
55
                case MeasuredValueShortWithTimestamp: {
3✔
56
                    objs.MeasuredValueShortWithTimestamp.emplace_back(obj.Address, now, stof(v));
3✔
57
                    return true;
3✔
58
                }
59
                case MeasuredValueScaledWithTimestamp: {
3✔
60
                    objs.MeasuredValueScaledWithTimestamp.emplace_back(obj.Address, now, stoi(v));
3✔
61
                    return true;
3✔
62
                }
63
            }
64
        } catch (const std::exception& e) {
×
65
            LOG(Warn) << "'" << control->GetDevice()->GetId() << "'/'" << control->GetId() << "' = '" << v
×
66
                      << "' is not convertible to IEC 608760-5-104 information object: " << e.what();
×
67
        }
×
68
        return false;
×
69
    }
70

71
    std::string GetFullName(PControl control)
6✔
72
    {
73
        return "'" + control->GetDevice()->GetId() + "'/'" + control->GetId() + "'";
6✔
74
    }
75
}
76

77
TGateway::TGateway(PDeviceDriver driver, IEC104::IServer* iecServer, const TDeviceConfig& devices)
3✔
78
    : Driver(driver),
3✔
79
      Devices(devices),
3✔
80
      IecServer(iecServer)
6✔
81
{
82
    std::vector<std::string> deviceIds;
3✔
83
    for (const auto& device: devices) {
6✔
84
        LOG(Debug) << "'" << device.first << "' is added to filter";
3✔
85
        deviceIds.emplace_back(device.first);
3✔
86
    }
87
    Driver->SetFilter(GetDeviceListFilter(deviceIds));
3✔
88
    Driver->WaitForReady();
3✔
89

90
    for (const auto& device: devices) {
6✔
91
        for (const auto& control: device.second) {
21✔
92
            IoaToControls[control.second.Address] = {device.first, control.first};
18✔
93
        }
94
    }
95

96
    Driver->On<TControlValueEvent>([this](const WBMQTT::TControlValueEvent& event) { OnValueChanged(event); });
16✔
97
    iecServer->SetHandler(this);
3✔
98
}
21✔
99

100
void TGateway::Stop()
×
101
{
102
    IecServer->Stop();
×
103
    Driver->StopLoop();
×
104
}
×
105

106
void TGateway::OnValueChanged(const WBMQTT::TControlValueEvent& event)
13✔
107
{
108
    auto itDevice = Devices.find(event.Control->GetDevice()->GetId());
13✔
109
    if (itDevice == Devices.end()) {
13✔
110
        LOG(Debug) << "Got message from " << GetFullName(event.Control) << ". No config for device";
×
111
        return;
×
112
    }
113

114
    auto itControl = itDevice->second.equal_range(event.Control->GetId());
13✔
115
    if (itControl.first == itDevice->second.end()) {
13✔
116
        LOG(Debug) << "Got message from " << GetFullName(event.Control) << ". No config for control";
×
117
        return;
×
118
    }
119

120
    bool hasObjs = false;
13✔
121
    IEC104::TInformationObjects objs;
13✔
122
    while (itControl.first != itControl.second) {
25✔
123
        hasObjs |= Append(objs, event.Control, event.RawValue, itControl.first->second);
12✔
124
        ++itControl.first;
12✔
125
    }
126
    if (hasObjs) {
13✔
127
        IecServer->SendSpontaneous(objs);
12✔
128
    }
129
}
13✔
130

131
IEC104::TInformationObjects TGateway::GetInformationObjectsValues() const noexcept
1✔
132
{
133
    IEC104::TInformationObjects objs;
1✔
134
    try {
135
        auto tx = Driver->BeginTx();
1✔
136
        for (const auto& device: Devices) {
2✔
137
            auto pDevice = tx->GetDevice(device.first);
1✔
138
            if (pDevice) {
1✔
139
                for (const auto& control: device.second) {
7✔
140
                    auto pControl = pDevice->GetControl(control.first);
6✔
141
                    if (pControl) {
6✔
142
                        Append(objs, pControl, pControl->GetRawValue(), control.second);
6✔
143
                    }
144
                }
6✔
145
            }
146
        }
1✔
147
    } catch (const std::exception& e) {
1✔
148
        LOG(Warn) << "TGateway::GetInformationObjectsValues() error: " << e.what();
×
149
    }
×
150
    LOG(Debug) << "TGateway::GetInformationObjectsValues()\n"
1✔
151
               << "\n\tSinglePoint:" << objs.SinglePoint.size()
1✔
152
               << "\n\tMeasuredValueShort:" << objs.MeasuredValueShort.size()
1✔
153
               << "\n\tMeasuredValueScaled:" << objs.MeasuredValueScaled.size()
1✔
154
               << "\n\tSinglePointWithTimestamp:" << objs.SinglePointWithTimestamp.size()
1✔
155
               << "\n\tMeasuredValueShortWithTimestamp:" << objs.MeasuredValueShortWithTimestamp.size()
1✔
156
               << "\n\tMeasuredValueScaledWithTimestamp:" << objs.MeasuredValueScaledWithTimestamp.size();
1✔
157
    return objs;
1✔
158
}
159

160
bool TGateway::SetParameter(uint32_t ioa, const std::string& value) noexcept
7✔
161
{
162
    auto it = IoaToControls.find(ioa);
7✔
163
    if (it == IoaToControls.end()) {
7✔
164
        LOG(Warn) << "Can't find configuration for IOA: " << ioa;
1✔
165
        return false;
1✔
166
    }
167

168
    try {
169
        auto tx = Driver->BeginTx();
6✔
170
        auto pDevice = tx->GetDevice(it->second.Device);
6✔
171
        if (!pDevice) {
6✔
172
            throw std::runtime_error("MQTT broker doesn't have '" + it->second.Device + "' device");
×
173
        }
174
        auto pControl = pDevice->GetControl(it->second.Control);
6✔
175
        if (!pControl) {
6✔
176
            throw std::runtime_error("'" + it->second.Device + "' doesn't contain control '" + it->second.Control +
×
177
                                     "'");
×
178
        }
179
        pControl->SetRawValue(tx, value).Sync();
6✔
180
        LOG(Info) << "Set " << GetFullName(pControl) << " = '" << value << "'";
6✔
181
        return true;
6✔
182
    } catch (std::exception& e) {
6✔
183
        LOG(Warn) << "Can't execute setup command IOA: " << ioa << ", value: " << value << ": " << e.what();
×
184
    }
×
185
    return false;
×
186
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc