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

wirenboard / wb-mqtt-w1 / 2

20 Oct 2025 10:38AM UTC coverage: 61.795% (+2.6%) from 59.202%
2

push

github

u236
add tests

168 of 244 branches covered (68.85%)

26 of 26 new or added lines in 1 file covered. (100.0%)

11 existing lines in 1 file now uncovered.

296 of 479 relevant lines covered (61.8%)

4.48 hits per line

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

86.92
/sysfs_w1.cpp
1
#include "sysfs_w1.h"
2

3
#include "file_utils.h"
4
#include <algorithm>
5
#include <fcntl.h>
6
#include <fstream>
7
#include <unistd.h>
8
#include <wblib/utils.h>
9

10
using namespace std::chrono;
11

12
#define LOG(logger) logger.Log() << "[w1 driver] "
13

14
namespace
15
{
16
    const char BULK_CONVERSION_TRIGGER[] = "trigger";
17
    const auto MAX_CONVERSION_TIME = milliseconds(2000);
18
    const auto MAX_VALUE_CHANGE = 10 * 1000; // 1 degree per second for DEFAULT_POLL_INTERVALL_MS
19

20
    std::unordered_map<std::string, int> lastValueMap;
21

22
    template<class T, class Pred> void erase_if(T& c, Pred pred)
9✔
23
    {
24
        auto last = c.end();
9✔
25
        for (auto i = c.begin(); i != last;) {
11✔
26
            if (pred(*i)) {
2✔
27
                i = c.erase(i);
1✔
28
            } else {
29
                ++i;
1✔
30
            }
31
        }
32
    }
9✔
33

34
    std::string ReadLine(const std::string& fileName)
7✔
35
    {
36
        std::ifstream file;
14✔
37
        OpenWithException(file, fileName);
7✔
38
        std::string str;
7✔
39
        std::getline(file, str);
7✔
40
        return str;
14✔
41
    }
42

43
    bool RunBulkRead(const std::string& fileName, WBMQTT::TLogger& logger)
2✔
44
    {
45
        auto fd = open(fileName.c_str(), O_WRONLY | O_APPEND);
2✔
46
        if (fd < 0) {
2✔
47
            LOG(logger) << "Can't open file:" << fileName;
×
48
            return false;
×
49
        }
50
        auto s = write(fd, BULK_CONVERSION_TRIGGER, sizeof(BULK_CONVERSION_TRIGGER)); // Write trailing zero
2✔
51
        close(fd);
2✔
52
        return (s == sizeof(BULK_CONVERSION_TRIGGER));
2✔
53
    }
54

55
    double GetTemperatureFromString(const std::string& str, const std::string& deviceFileName)
10✔
56
    {
57
        if (str.empty()) {
10✔
58
            throw TOneWireReadErrorException("Can't read temperature", deviceFileName);
×
59
        }
60

61
        int dataInt = std::stoi(str.c_str());
10✔
62

63
        // Thermometer can't measure temperature?
64
        if (dataInt == 85000) {
10✔
65
            auto it = lastValueMap.find(deviceFileName);
2✔
66
            if (it == lastValueMap.end() || abs(dataInt - it->second) > MAX_VALUE_CHANGE) {
2✔
67
                throw TOneWireReadErrorException("Measurement error", deviceFileName);
1✔
68
            }
69
        }
70

71
        // returned max possible temp, probably an error (it happens for chineese clones)
72
        if (dataInt == 127937) {
9✔
UNCOV
73
            throw TOneWireReadErrorException("Thermometer error", deviceFileName);
×
74
        }
75

76
        lastValueMap[deviceFileName] = dataInt;
9✔
77
        return dataInt / 1000.0; // Temperature given by kernel is in thousandths of degrees
9✔
78
    }
79

80
    double GetBulkReadTemperature(const std::string& deviceFileName)
5✔
81
    {
82
        return GetTemperatureFromString(ReadLine(deviceFileName), deviceFileName);
5✔
83
    }
84

85
    double GetDirectConvertedTemperature(const std::string& deviceFileName)
7✔
86
    {
87
        std::string data;
14✔
88
        bool crcOk = false;
7✔
89

90
        const std::string tag("t=");
14✔
91

92
        std::ifstream file;
14✔
93
        OpenWithException(file, deviceFileName);
7✔
94

95
        /*  reading file till eof could lead to a stuck
96
            when device is removed */
97
        while (file.good()) {
30✔
98
            std::string sLine;
48✔
99
            std::getline(file, sLine);
24✔
100
            if (sLine.find("crc=") != std::string::npos) {
24✔
101
                if (sLine.find("YES") != std::string::npos) {
6✔
102
                    crcOk = true;
5✔
103
                }
104
            } else {
105
                size_t tpos = sLine.find(tag);
18✔
106
                if (tpos != std::string::npos) {
18✔
107
                    data = sLine.substr(tpos + tag.length());
6✔
108
                }
109
            }
110
        }
111

112
        if (!crcOk) {
6✔
113
            throw TOneWireReadErrorException("Bad CRC", deviceFileName);
1✔
114
        }
115

116
        return GetTemperatureFromString(data, deviceFileName);
10✔
117
    }
118

119
    struct TBusMaster
120
    {
121
        std::string Dir;
122
        bool SupportsBulkRead;
123
    };
124
}
125

126
TSysfsOneWireThermometer::TSysfsOneWireThermometer(const std::string& id, const std::string& dir, bool bulkRead)
16✔
127
    : Id(id),
128
      Status(TSysfsOneWireThermometer::New),
129
      BulkRead(bulkRead)
16✔
130
{
131
    SetDeviceFileName(dir);
16✔
132
}
16✔
133

134
void TSysfsOneWireThermometer::SetDeviceFileName(const std::string& dir)
16✔
135
{
136
    DeviceFileName = dir + "/" + Id + (BulkRead ? "/temperature" : "/w1_slave");
16✔
137
}
16✔
138

139
double TSysfsOneWireThermometer::GetTemperature() const
12✔
140
{
141
    if (BulkRead) {
12✔
142
        return GetBulkReadTemperature(DeviceFileName);
5✔
143
    }
144
    return GetDirectConvertedTemperature(DeviceFileName);
7✔
145
}
146

147
const std::string& TSysfsOneWireThermometer::GetId() const
20✔
148
{
149
    return Id;
20✔
150
}
151

152
TSysfsOneWireThermometer::PresenceStatus TSysfsOneWireThermometer::GetStatus() const
8✔
153
{
154
    return Status;
8✔
155
}
156

157
void TSysfsOneWireThermometer::MarkAsDisconnected()
1✔
158
{
159
    Status = Disconnected;
1✔
160
}
1✔
161

162
bool TSysfsOneWireThermometer::FoundAgain(const std::string& dir)
×
163
{
UNCOV
164
    Status = Connected;
×
165
    if (WBMQTT::StringStartsWith(DeviceFileName, dir)) {
×
166
        return true;
×
167
    }
UNCOV
168
    SetDeviceFileName(dir);
×
UNCOV
169
    return false;
×
170
}
171

172
TSysfsOneWireManager::TSysfsOneWireManager(const std::string& devicesDir,
7✔
173
                                           WBMQTT::TLogger& debugLogger,
174
                                           WBMQTT::TLogger& errorLogger)
7✔
175
    : DevicesDir(devicesDir),
176
      DebugLogger(debugLogger),
177
      ErrorLogger(errorLogger)
7✔
178
{}
7✔
179

180
std::vector<std::shared_ptr<TSysfsOneWireThermometer>> TSysfsOneWireManager::RescanBusAndRead()
9✔
181
{
182
    const auto prefixes = {"28-", "10-", "22-"};
9✔
183
    erase_if(Devices, [](const auto& it) { return it.second->GetStatus() == TSysfsOneWireThermometer::Disconnected; });
11✔
184
    for (auto& d: Devices) {
10✔
185
        d.second->MarkAsDisconnected();
1✔
186
    }
187

188
    std::vector<TBusMaster> busMasters;
18✔
189

190
    IterateDir(DevicesDir, [&](const auto& name) {
9✔
191
        if (!WBMQTT::StringStartsWith(name, "w1_bus_master")) {
11✔
UNCOV
192
            return false;
×
193
        }
194
        TBusMaster bm;
11✔
195
        bm.Dir = DevicesDir + name;
11✔
196
        bm.SupportsBulkRead = (access((bm.Dir + "/therm_bulk_read").c_str(), F_OK) == 0);
11✔
197
        busMasters.push_back(bm);
11✔
198

199
        if (bm.SupportsBulkRead) {
11✔
200
            RunBulkRead(bm.Dir + "/therm_bulk_read", ErrorLogger);
2✔
201
        }
202

203
        IterateDir(bm.Dir, [&](const auto& name) {
11✔
204
            for (const auto& prefix: prefixes) {
67✔
205
                if (WBMQTT::StringStartsWith(name, prefix)) {
42✔
206
                    auto it = Devices.find(name);
10✔
207
                    if (it == Devices.end()) {
10✔
208
                        it =
10✔
209
                            Devices
210
                                .insert({name,
211
                                         std::make_shared<TSysfsOneWireThermometer>(name, bm.Dir, bm.SupportsBulkRead)})
20✔
212
                                .first;
213
                    } else {
UNCOV
214
                        if (!it->second->FoundAgain(bm.Dir)) {
×
UNCOV
215
                            LOG(DebugLogger) << name << " is switched to " << bm.Dir;
×
216
                        }
217
                    }
218
                }
219
            }
220
            return false;
14✔
221
        });
222
        return false;
11✔
223
    });
18✔
224

225
    auto time = steady_clock::now();
9✔
226
    bool inConversion = true;
9✔
227
    while (inConversion && (duration_cast<milliseconds>(steady_clock::now() - time) < MAX_CONVERSION_TIME)) {
18✔
228
        inConversion = false;
9✔
229
        for (auto& bm: busMasters) {
20✔
230
            if (bm.SupportsBulkRead) {
11✔
231
                try {
232
                    auto status = ReadLine(bm.Dir + "/therm_bulk_read");
2✔
233
                    inConversion |= (status.empty() || status[0] != '1');
2✔
UNCOV
234
                } catch (const std::exception& e) {
×
UNCOV
235
                    LOG(ErrorLogger) << e.what();
×
236
                }
237
            }
238
        }
239
        if (inConversion) {
9✔
UNCOV
240
            std::this_thread::sleep_for(milliseconds(100));
×
241
        }
242
    }
243
    if (inConversion) {
9✔
UNCOV
244
        LOG(DebugLogger) << "Conversion takes too much time";
×
245
    }
246

247
    std::vector<std::shared_ptr<TSysfsOneWireThermometer>> res;
9✔
248
    for (auto& d: Devices) {
20✔
249
        res.push_back(d.second);
11✔
250
    }
251
    std::sort(res.begin(), res.end(), [](const auto& v1, const auto& v2) { return v1->GetId() < v2->GetId(); });
15✔
252
    return res;
18✔
253
}
254

255
TOneWireReadErrorException::TOneWireReadErrorException(const std::string& message, const std::string& deviceFileName)
2✔
256
    : std::runtime_error(message + " (" + deviceFileName + ")")
2✔
257
{}
2✔
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