blob: 6bfe3adf69230a30a1a82747bd054654cc1ace41 [file] [log] [blame]
Cheng C Yang209ec562019-03-12 16:37:44 +08001/*
2// Copyright (c) 2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Cheng C Yang209ec562019-03-12 16:37:44 +080017#include <PSUSensor.hpp>
18#include <Utils.hpp>
19#include <boost/algorithm/string/predicate.hpp>
20#include <boost/algorithm/string/replace.hpp>
21#include <boost/container/flat_set.hpp>
James Feist24f02f22019-04-15 11:05:39 -070022#include <filesystem>
Cheng C Yang209ec562019-03-12 16:37:44 +080023#include <fstream>
24#include <regex>
25#include <sdbusplus/asio/connection.hpp>
26#include <sdbusplus/asio/object_server.hpp>
27
28static constexpr std::array<const char*, 1> sensorTypes = {
29 "xyz.openbmc_project.Configuration.pmbus"};
30
31namespace fs = std::filesystem;
32
33void createSensors(
34 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
35 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
36 boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>>&
37 sensors,
Cheng C Yange50345b2019-04-02 17:26:15 +080038 boost::container::flat_map<std::string, std::string>& sensorTable,
39 boost::container::flat_map<std::string, PSUProperty>& labelMatch)
Cheng C Yang209ec562019-03-12 16:37:44 +080040{
41
42 ManagedObjectType sensorConfigs;
43 bool useCache = false;
44
45 for (const char* type : sensorTypes)
46 {
47 if (!getSensorConfiguration(type, dbusConnection, sensorConfigs,
48 useCache))
49 {
50 std::cerr << "error get sensor config from entity manager\n";
51 return;
52 }
53 useCache = true;
54 }
55
56 std::vector<fs::path> pmbusPaths;
57 if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
58 {
59 std::cerr << "No PSU sensors in system\n";
60 return;
61 }
62
63 boost::container::flat_set<std::string> directories;
64 for (const auto& pmbusPath : pmbusPaths)
65 {
66 const std::string pathStr = pmbusPath.string();
67 auto directory = pmbusPath.parent_path();
68
69 auto ret = directories.insert(directory.string());
70 if (!ret.second)
71 {
72 continue; // check if path i1 already searched
73 }
74
75 auto device = fs::path(directory / "device");
76 std::string deviceName = fs::canonical(device).stem();
77 auto findHyphen = deviceName.find("-");
78 if (findHyphen == std::string::npos)
79 {
80 std::cerr << "found bad device" << deviceName << "\n";
81 continue;
82 }
83 std::string busStr = deviceName.substr(0, findHyphen);
84 std::string addrStr = deviceName.substr(findHyphen + 1);
85
86 size_t bus = 0;
87 size_t addr = 0;
88
89 try
90 {
91 bus = std::stoi(busStr);
92 addr = std::stoi(addrStr, 0, 16);
93 }
94 catch (std::invalid_argument)
95 {
96 continue;
97 }
98
99 std::ifstream nameFile(pmbusPath);
100 if (!nameFile.good())
101 {
102 std::cerr << "Failure reading " << pmbusPath << "\n";
103 continue;
104 }
105
106 std::string pmbusName;
107 std::getline(nameFile, pmbusName);
108 nameFile.close();
109 if (pmbusName != "pmbus")
110 {
111 continue;
112 }
113
114 const std::pair<std::string, boost::container::flat_map<
115 std::string, BasicVariantType>>*
116 baseConfig = nullptr;
117 const SensorData* sensorData = nullptr;
118 const std::string* interfacePath = nullptr;
119 const char* sensorType = nullptr;
120
121 for (const std::pair<sdbusplus::message::object_path, SensorData>&
122 sensor : sensorConfigs)
123 {
124 sensorData = &(sensor.second);
125 for (const char* type : sensorTypes)
126 {
127 auto sensorBase = sensorData->find(type);
128 if (sensorBase != sensorData->end())
129 {
130 baseConfig = &(*sensorBase);
131 sensorType = type;
132 break;
133 }
134 }
135 if (baseConfig == nullptr)
136 {
137 std::cerr << "error finding base configuration for "
138 << deviceName << "\n";
139 continue;
140 }
141
142 auto configBus = baseConfig->second.find("Bus");
143 auto configAddress = baseConfig->second.find("Address");
144
145 if (configBus == baseConfig->second.end() ||
146 configAddress == baseConfig->second.end())
147 {
148 std::cerr << "error finding necessary entry in configuration";
149 continue;
150 }
151
152 if (std::get<uint64_t>(configBus->second) != bus ||
153 std::get<uint64_t>(configAddress->second) != addr)
154 {
155 continue;
156 }
157
158 interfacePath = &(sensor.first.str);
159 break;
160 }
161 if (interfacePath == nullptr)
162 {
163 std::cerr << "failed to find match for " << deviceName << "\n";
164 continue;
165 }
166
Cheng C Yange50345b2019-04-02 17:26:15 +0800167 auto findPSUName = baseConfig->second.find("Name");
168 if (findPSUName == baseConfig->second.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800169 {
170 std::cerr << "could not determine configuration name for "
171 << deviceName << "\n";
172 continue;
173 }
174
Cheng C Yange50345b2019-04-02 17:26:15 +0800175 std::vector<fs::path> sensorPaths;
176 if (!findFiles(fs::path(directory), R"(\w\d+_input$)", sensorPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800177 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800178 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800179 continue;
180 }
181
Cheng C Yange50345b2019-04-02 17:26:15 +0800182 for (const auto& sensorPath : sensorPaths)
Cheng C Yang209ec562019-03-12 16:37:44 +0800183 {
Cheng C Yang209ec562019-03-12 16:37:44 +0800184
Cheng C Yange50345b2019-04-02 17:26:15 +0800185 std::string labelHead;
186 std::string sensorPathStr = sensorPath.string();
187 std::string sensorNameStr = sensorPath.filename();
188 std::string sensorNameSubStr =
189 sensorNameStr.substr(0, sensorNameStr.find("_") - 1);
190
191 std::string labelPathStr =
192 boost::replace_all_copy(sensorNameStr, "input", "label");
193 std::vector<fs::path> labelPaths;
194 if (!findFiles(fs::path(directory), labelPathStr, labelPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800195 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800196 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800197 continue;
198 }
199
Cheng C Yange50345b2019-04-02 17:26:15 +0800200 if (labelPaths.empty())
Cheng C Yang209ec562019-03-12 16:37:44 +0800201 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800202 labelHead = sensorNameStr.substr(0, sensorNameStr.find("_"));
Cheng C Yang209ec562019-03-12 16:37:44 +0800203 }
204 else
205 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800206 auto labelPath =
207 boost::replace_all_copy(sensorPathStr, "input", "label");
208 std::ifstream labelFile(labelPath);
209 if (!labelFile.good())
210 {
211 std::cerr << "Failure reading " << sensorPath << "\n";
212 continue;
213 }
214 std::string label;
215 std::getline(labelFile, label);
216 labelFile.close();
Cheng C Yang209ec562019-03-12 16:37:44 +0800217
Cheng C Yange50345b2019-04-02 17:26:15 +0800218 auto findSensor = sensors.find(label);
219 if (findSensor != sensors.end())
220 {
221 continue;
222 }
223
224 labelHead = label.substr(0, label.find(" "));
225 }
226
227 std::vector<thresholds::Threshold> sensorThresholds;
228
229 parseThresholdsFromConfig(*sensorData, sensorThresholds,
230 &labelHead);
231
232 auto findProperty = labelMatch.find(labelHead);
233 if (findProperty == labelMatch.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800234 {
Cheng C Yang209ec562019-03-12 16:37:44 +0800235 continue;
236 }
237
Cheng C Yange50345b2019-04-02 17:26:15 +0800238 unsigned int factor =
239 std::pow(10, findProperty->second.sensorScaleFactor);
240 if (sensorThresholds.empty())
241 {
242 if (!parseThresholdsFromAttr(sensorThresholds, sensorPathStr,
243 factor))
244 {
245 std::cerr << "error populating thresholds\n";
246 }
247 }
248
249 auto findSensorType = sensorTable.find(sensorNameSubStr);
250 if (findSensorType == sensorTable.end())
251 {
252 std::cerr << "Cannot find PSU sensorType\n";
253 continue;
254 }
255
256 std::string sensorName =
257 std::get<std::string>(findPSUName->second) + " " +
258 findProperty->second.labelTypeName;
259
James Feistbfaf2382019-04-19 13:57:17 -0700260 auto& newSensor = sensors[sensorName];
261 newSensor = nullptr; // destroy old one if it exists
262 newSensor = std::make_unique<PSUSensor>(
Cheng C Yange50345b2019-04-02 17:26:15 +0800263 sensorPathStr, sensorType, objectServer, dbusConnection, io,
Cheng C Yang209ec562019-03-12 16:37:44 +0800264 sensorName, std::move(sensorThresholds), *interfacePath,
Cheng C Yange50345b2019-04-02 17:26:15 +0800265 findSensorType->second, factor, findProperty->second.maxReading,
266 findProperty->second.minReading);
Cheng C Yang209ec562019-03-12 16:37:44 +0800267 }
268 }
269 return;
270}
271
272void propertyInitialize(
Cheng C Yange50345b2019-04-02 17:26:15 +0800273 boost::container::flat_map<std::string, std::string>& sensorTable,
274 boost::container::flat_map<std::string, PSUProperty>& labelMatch)
Cheng C Yang209ec562019-03-12 16:37:44 +0800275{
Cheng C Yange50345b2019-04-02 17:26:15 +0800276 sensorTable = {{"power", "power/"},
277 {"curr", "current/"},
278 {"temp", "temperature/"},
279 {"in", "voltage/"},
280 {"fan", "fan_tach/"}};
281
282 labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
283 {"pout1", PSUProperty("Output Power", 3000, 0, 6)},
284 {"vin", PSUProperty("Input Voltage", 255, 0, 3)},
285 {"iin", PSUProperty("Input Current", 20, 0, 3)},
286 {"iout1", PSUProperty("Output Current", 255, 0, 3)},
287 {"temp1", PSUProperty("Temperature", 127, -128, 3)},
288 {"fan1", PSUProperty("Fan Speed 1", 10000, 0, 0)},
289 {"fan2", PSUProperty("Fan Speed 2", 10000, 0, 0)}};
Cheng C Yang209ec562019-03-12 16:37:44 +0800290}
291
292int main(int argc, char** argv)
293{
294 boost::asio::io_service io;
295 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
296
297 systemBus->request_name("xyz.openbmc_project.PSUSensor");
298 sdbusplus::asio::object_server objectServer(systemBus);
299 boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>> sensors;
Cheng C Yange50345b2019-04-02 17:26:15 +0800300 boost::container::flat_map<std::string, std::string> sensorTable;
Cheng C Yang209ec562019-03-12 16:37:44 +0800301 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Cheng C Yange50345b2019-04-02 17:26:15 +0800302 boost::container::flat_map<std::string, PSUProperty> labelMatch;
Cheng C Yang209ec562019-03-12 16:37:44 +0800303
304 propertyInitialize(sensorTable, labelMatch);
305
306 io.post([&]() {
307 createSensors(io, objectServer, systemBus, sensors, sensorTable,
308 labelMatch);
309 });
310 boost::asio::deadline_timer filterTimer(io);
311 std::function<void(sdbusplus::message::message&)> eventHandler =
312 [&](sdbusplus::message::message& message) {
313 if (message.is_method_error())
314 {
315 std::cerr << "callback method error\n";
316 return;
317 }
318 filterTimer.expires_from_now(boost::posix_time::seconds(1));
319 filterTimer.async_wait([&](const boost::system::error_code& ec) {
320 if (ec == boost::asio::error::operation_aborted)
321 {
322 return;
323 }
324 else if (ec)
325 {
326 std::cerr << "timer error\n";
327 }
328 createSensors(io, objectServer, systemBus, sensors, sensorTable,
329 labelMatch);
330 });
331 };
332
333 for (const char* type : sensorTypes)
334 {
335 auto match = std::make_unique<sdbusplus::bus::match::match>(
336 static_cast<sdbusplus::bus::bus&>(*systemBus),
337 "type='signal',member='PropertiesChanged',path_namespace='" +
338 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
339 eventHandler);
340 matches.emplace_back(std::move(match));
341 }
342 io.run();
343}