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

wirenboard / wb-mqtt-adc / 4

23 Jun 2025 01:29PM UTC coverage: 56.503% (-1.2%) from 57.722%
4

Pull #46

github

ekateluv
clang
Pull Request #46: Test coveralls

242 of 422 branches covered (57.35%)

414 of 739 relevant lines covered (56.02%)

4.44 hits per line

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

64.94
/src/sysfs_adc.cpp
1
#include "sysfs_adc.h"
2

3
#include <chrono>
4
#include <fnmatch.h>
5
#include <iomanip>
6
#include <math.h>
7
#include <unistd.h>
8

9
#include <wblib/utils.h>
10

11
#include "file_utils.h"
12

13
TChannelReader::TChannelReader(double defaultIIOScale,
1✔
14
                               const TChannelReader::TSettings& cfg,
15
                               WBMQTT::TLogger& debugLogger,
16
                               WBMQTT::TLogger& infoLogger,
17
                               const std::string& sysfsIIODir)
1✔
18
    : Cfg(cfg),
19
      SysfsIIODir(sysfsIIODir),
20
      IIOScale(defaultIIOScale),
21
      AverageCounter(cfg.AveragingWindow),
1✔
22
      DebugLogger(debugLogger),
23
      InfoLogger(infoLogger),
24
      LastMeasureTimestamp(Timestamp::min()),
1✔
25
      NextPollTimestamp(Timestamp::min()),
1✔
26
      FirstPollInLoopTimestamp(Timestamp::min())
2✔
27
{
28
    SelectScale(infoLogger);
1✔
29
}
1✔
30

31
std::string TChannelReader::GetValue() const
1✔
32
{
33
    return MeasuredV;
1✔
34
}
35

36
TChannelReader::Timestamp TChannelReader::GetLastMeasureTimestamp() const
1✔
37
{
38
    return LastMeasureTimestamp;
1✔
39
}
40

41
TChannelReader::Timestamp TChannelReader::GetNextPollTimestamp() const
1✔
42
{
43
    return NextPollTimestamp;
1✔
44
}
45

46
std::chrono::milliseconds TChannelReader::GetDelayBetweenMeasurements() const
×
47
{
48
    return Cfg.DelayBetweenMeasurements;
×
49
}
50

51
std::chrono::milliseconds TChannelReader::GetPollInterval() const
×
52
{
53
    return Cfg.PollInterval;
×
54
}
55

56
void TChannelReader::Poll(Timestamp now, const std::string& debugMessagePrefix)
1✔
57
{
58
    // skip this poll if scheduled time is not come yet
59
    if (NextPollTimestamp > now) {
1✔
60
        return;
×
61
    }
62

63
    // if it is out very first poll, save its start time
64
    if (FirstPollInLoopTimestamp == Timestamp::min()) {
1✔
65
        FirstPollInLoopTimestamp = now;
1✔
66
    }
67

68
    if (NextPollTimestamp == Timestamp::min()) {
1✔
69
        NextPollTimestamp = now;
1✔
70
    }
71

72
    // perform scheduled measurement
73
    int32_t adcMeasurement = ReadFromADC();
1✔
74
    DebugLogger.Log() << debugMessagePrefix << Cfg.ChannelNumber << " = " << adcMeasurement;
1✔
75
    AverageCounter.AddValue(adcMeasurement);
1✔
76

77
    // if average counter is awaiting more data, schedule next poll and exit
78
    if (!AverageCounter.IsReady()) {
1✔
79
        NextPollTimestamp = NextPollTimestamp + Cfg.DelayBetweenMeasurements;
×
80
        return;
×
81
    }
82

83
    // average counter collected enough data, publish it
84
    int32_t value = AverageCounter.GetAverage();
1✔
85
    double v = IIOScale * value;
1✔
86
    if (v > Cfg.MaxScaledVoltage) {
1✔
87
        throw std::runtime_error(debugMessagePrefix + Cfg.ChannelNumber + " scaled value (" + std::to_string(v) +
×
88
                                 ") is bigger than maximum (" + std::to_string(Cfg.MaxScaledVoltage) + ")");
×
89
    }
90

91
    double res = v * Cfg.VoltageMultiplier / 1000.0; // got mV let's divide it by 1000 to obtain V
1✔
92

93
    std::ostringstream out;
2✔
94
    out << std::setprecision(Cfg.DecimalPlaces) << std::fixed << res;
1✔
95
    MeasuredV = out.str();
1✔
96

97
    // update current measurement timestamp info and schedule next one
98
    NextPollTimestamp = FirstPollInLoopTimestamp + Cfg.PollInterval;
1✔
99
    FirstPollInLoopTimestamp = NextPollTimestamp;
1✔
100

101
    LastMeasureTimestamp = now;
1✔
102

103
    // reset accumulator
104
    AverageCounter.Reset();
1✔
105
}
106

107
int32_t TChannelReader::ReadFromADC()
1✔
108
{
109
    std::string fileName = SysfsIIODir + "/in_" + Cfg.ChannelNumber + "_raw";
3✔
110
    for (size_t i = 0; i < 3; ++i) { // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53984
1✔
111
        {
112
            std::ifstream adcValStream;
1✔
113
            OpenWithException(adcValStream, fileName);
1✔
114
            int32_t val;
115
            try {
116
                adcValStream >> val;
1✔
117
                if (adcValStream.good())
1✔
118
                    return val;
2✔
119
            } catch (const std::ios_base::failure& er) {
×
120
                DebugLogger.Log() << er.what() << " " << fileName;
×
121
            }
122
        }
123
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
×
124
    }
125
    throw std::runtime_error("Can't read from " + fileName);
×
126
}
127

128
void TChannelReader::SelectScale(WBMQTT::TLogger& infoLogger)
1✔
129
{
130
    std::string scalePrefix = SysfsIIODir + "/in_" + Cfg.ChannelNumber + "_scale";
2✔
131

132
    std::ifstream scaleFile;
1✔
133
    TryOpen({scalePrefix + "_available", SysfsIIODir + "/in_voltage_scale_available", SysfsIIODir + "/scale_available"},
1✔
134
            scaleFile);
5✔
135

136
    if (scaleFile.is_open()) {
1✔
137
        auto contents = std::string((std::istreambuf_iterator<char>(scaleFile)), std::istreambuf_iterator<char>());
×
138
        infoLogger.Log() << "Available scales: " << contents;
×
139

140
        std::string bestScaleStr = FindBestScale(WBMQTT::StringSplit(contents, " "), Cfg.DesiredScale);
×
141

142
        if (!bestScaleStr.empty()) {
×
143
            IIOScale = std::stod(bestScaleStr);
×
144
            WriteToFile(scalePrefix, bestScaleStr);
×
145
            infoLogger.Log() << scalePrefix << " is set to " << bestScaleStr;
×
146
            return;
×
147
        }
148
    }
149

150
    // scale_available file is not present read the current scale(in_voltageX_scale) from sysfs or
151
    // from group scale(in_voltage_scale)
152
    TryOpen({scalePrefix, SysfsIIODir + "/in_voltage_scale"}, scaleFile);
3✔
153
    if (scaleFile.is_open()) {
1✔
154
        scaleFile >> IIOScale;
1✔
155
    }
156
    infoLogger.Log() << scalePrefix << " = " << IIOScale;
1✔
157
}
158

159
std::string FindSysfsIIODir(const std::string& matchIIO)
×
160
{
161
    if (matchIIO.empty()) {
×
162
        return "/sys/bus/iio/devices/iio:device0";
×
163
    }
164

165
    std::string pattern = "*" + matchIIO + "*";
×
166

167
    auto fn = [&](const std::string& d) {
×
168
        char buf[512];
169
        int len;
170
        if ((len = readlink(d.c_str(), buf, 512)) < 0)
×
171
            return false;
×
172
        buf[len] = 0;
×
173
        return (fnmatch(pattern.c_str(), buf, 0) == 0);
×
174
    };
×
175

176
    return IterateDirByPattern("/sys/bus/iio/devices", "iio:device", fn);
×
177
}
178

179
std::string FindBestScale(const std::vector<std::string>& scales, double desiredScale)
5✔
180
{
181
    std::string bestScaleStr;
5✔
182
    double bestScaleDouble = 0;
5✔
183

184
    for (auto& scaleStr: scales) {
16✔
185
        double val;
186
        try {
187
            val = stod(scaleStr);
11✔
188
        } catch (const std::invalid_argument& e) {
10✔
189
            continue;
5✔
190
        }
191
        // best scale is either maximum scale or the one closest to user request
192
        if (((desiredScale > 0) && (fabs(val - desiredScale) <= fabs(bestScaleDouble - desiredScale))) // user request
6✔
193
            || ((desiredScale <= 0) && (val >= bestScaleDouble))                                       // maximum scale
×
194
        )
195
        {
196
            bestScaleDouble = val;
6✔
197
            bestScaleStr = scaleStr;
6✔
198
        }
199
    }
200
    return bestScaleStr;
5✔
201
}
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