blob: 0367d2a62e2cc88987801726f325903548bc77f9 [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*/
Josh Lehan0830c7b2019-10-08 16:35:09 -070016
Patrick Ventureca44b2f2019-10-31 11:02:26 -070017#include "PSUEvent.hpp"
18#include "PSUSensor.hpp"
19#include "Utils.hpp"
20
Patrick Venture96e97db2019-10-31 13:44:38 -070021#include <array>
Cheng C Yang209ec562019-03-12 16:37:44 +080022#include <boost/algorithm/string/predicate.hpp>
23#include <boost/algorithm/string/replace.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070024#include <boost/container/flat_map.hpp>
Cheng C Yang209ec562019-03-12 16:37:44 +080025#include <boost/container/flat_set.hpp>
Josh Lehan74d9bd92019-10-31 08:51:58 -070026#include <cmath>
James Feist24f02f22019-04-15 11:05:39 -070027#include <filesystem>
Cheng C Yang209ec562019-03-12 16:37:44 +080028#include <fstream>
Patrick Venture96e97db2019-10-31 13:44:38 -070029#include <functional>
Cheng C Yang58b2b532019-05-31 00:19:45 +080030#include <iostream>
Cheng C Yang209ec562019-03-12 16:37:44 +080031#include <regex>
32#include <sdbusplus/asio/connection.hpp>
33#include <sdbusplus/asio/object_server.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070034#include <sdbusplus/bus/match.hpp>
35#include <string>
36#include <utility>
37#include <variant>
38#include <vector>
Cheng C Yang209ec562019-03-12 16:37:44 +080039
Josh Lehan49cfba92019-10-08 16:50:42 -070040static constexpr bool DEBUG = false;
41
Alex Qiu6690d8f2020-01-22 17:56:33 -080042static constexpr std::array<const char*, 7> sensorTypes = {
Devjit Gopalpurde65ef72019-10-30 04:47:35 -070043 "xyz.openbmc_project.Configuration.INA230",
Alex Qiu6690d8f2020-01-22 17:56:33 -080044 "xyz.openbmc_project.Configuration.ISL68137",
45 "xyz.openbmc_project.Configuration.MAX20730",
46 "xyz.openbmc_project.Configuration.MAX20734",
47 "xyz.openbmc_project.Configuration.MAX20796",
48 "xyz.openbmc_project.Configuration.MAX34451",
49 "xyz.openbmc_project.Configuration.pmbus"};
Cheng C Yang209ec562019-03-12 16:37:44 +080050
Alex Qiu6690d8f2020-01-22 17:56:33 -080051static std::vector<std::string> pmbusNames = {
52 "isl68137", "ina219", "ina230", "max20730", "max20734",
53 "max20796", "max34451", "pmbus", "pxe1610"};
Josh Lehan0830c7b2019-10-08 16:35:09 -070054
Cheng C Yang209ec562019-03-12 16:37:44 +080055namespace fs = std::filesystem;
56
Cheng C Yang916360b2019-05-07 18:47:16 +080057static boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>>
58 sensors;
Cheng C Yang58b2b532019-05-31 00:19:45 +080059static boost::container::flat_map<std::string, std::unique_ptr<PSUCombineEvent>>
60 combineEvents;
Cheng C Yang916360b2019-05-07 18:47:16 +080061static boost::container::flat_map<std::string, std::unique_ptr<PwmSensor>>
62 pwmSensors;
63static boost::container::flat_map<std::string, std::string> sensorTable;
64static boost::container::flat_map<std::string, PSUProperty> labelMatch;
65static boost::container::flat_map<std::string, std::string> pwmTable;
Cheng C Yang58b2b532019-05-31 00:19:45 +080066static boost::container::flat_map<std::string, std::vector<std::string>>
67 eventMatch;
Cheng C Yang202a1ff2020-01-09 09:34:22 +080068static boost::container::flat_map<
69 std::string,
70 boost::container::flat_map<std::string, std::vector<std::string>>>
71 groupEventMatch;
Cheng C Yang58b2b532019-05-31 00:19:45 +080072static boost::container::flat_map<std::string, std::vector<std::string>>
73 limitEventMatch;
74
Josh Lehan74d9bd92019-10-31 08:51:58 -070075static std::vector<PSUProperty> psuProperties;
76
Cheng C Yang58b2b532019-05-31 00:19:45 +080077// Function CheckEvent will check each attribute from eventMatch table in the
78// sysfs. If the attributes exists in sysfs, then store the complete path
79// of the attribute into eventPathList.
80void checkEvent(
81 const std::string& directory,
82 const boost::container::flat_map<std::string, std::vector<std::string>>&
83 eventMatch,
84 boost::container::flat_map<std::string, std::vector<std::string>>&
85 eventPathList)
86{
87 for (const auto& match : eventMatch)
88 {
89 const std::vector<std::string>& eventAttrs = match.second;
90 const std::string& eventName = match.first;
91 for (const auto& eventAttr : eventAttrs)
92 {
93 auto eventPath = directory + "/" + eventAttr;
94
95 std::ifstream eventFile(eventPath);
96 if (!eventFile.good())
97 {
98 continue;
99 }
100
101 eventPathList[eventName].push_back(eventPath);
102 }
103 }
104}
105
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800106// Check Group Events which contains more than one targets in each combine
107// events.
108void checkGroupEvent(
109 const std::string& directory,
110 const boost::container::flat_map<
111 std::string,
112 boost::container::flat_map<std::string, std::vector<std::string>>>&
113 groupEventMatch,
114 boost::container::flat_map<
115 std::string,
116 boost::container::flat_map<std::string, std::vector<std::string>>>&
117 groupEventPathList)
118{
119 for (const auto& match : groupEventMatch)
120 {
121 const std::string& groupEventName = match.first;
122 const boost::container::flat_map<std::string, std::vector<std::string>>
123 events = match.second;
124 boost::container::flat_map<std::string, std::vector<std::string>>
125 pathList;
126 for (const auto& match : events)
127 {
128 const std::string& eventName = match.first;
129 const std::vector<std::string>& eventAttrs = match.second;
130 for (const auto& eventAttr : eventAttrs)
131 {
132 auto eventPath = directory + "/" + eventAttr;
133 std::ifstream eventFile(eventPath);
134 if (!eventFile.good())
135 {
136 continue;
137 }
138
139 pathList[eventName].push_back(eventPath);
140 }
141 }
142 groupEventPathList[groupEventName] = pathList;
143 }
144}
145
Cheng C Yang58b2b532019-05-31 00:19:45 +0800146// Function checkEventLimits will check all the psu related xxx_input attributes
147// in sysfs to see if xxx_crit_alarm xxx_lcrit_alarm xxx_max_alarm
148// xxx_min_alarm exist, then store the existing paths of the alarm attributes
149// to eventPathList.
150void checkEventLimits(
151 const std::string& sensorPathStr,
152 const boost::container::flat_map<std::string, std::vector<std::string>>&
153 limitEventMatch,
154 boost::container::flat_map<std::string, std::vector<std::string>>&
155 eventPathList)
156{
157 for (const auto& limitMatch : limitEventMatch)
158 {
159 const std::vector<std::string>& limitEventAttrs = limitMatch.second;
160 const std::string& eventName = limitMatch.first;
161 for (const auto& limitEventAttr : limitEventAttrs)
162 {
163 auto limitEventPath =
164 boost::replace_all_copy(sensorPathStr, "input", limitEventAttr);
165 std::ifstream eventFile(limitEventPath);
166 if (!eventFile.good())
167 {
168 continue;
169 }
170 eventPathList[eventName].push_back(limitEventPath);
171 }
172 }
173}
Cheng C Yang916360b2019-05-07 18:47:16 +0800174
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530175static void
176 checkPWMSensor(const fs::path& sensorPath, std::string& labelHead,
177 const std::string& interfacePath,
178 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
179 sdbusplus::asio::object_server& objectServer,
180 const std::string& psuName)
Cheng C Yang916360b2019-05-07 18:47:16 +0800181{
182 for (const auto& pwmName : pwmTable)
183 {
184 if (pwmName.first != labelHead)
185 {
186 continue;
187 }
188
189 const std::string& sensorPathStr = sensorPath.string();
190 const std::string& pwmPathStr =
191 boost::replace_all_copy(sensorPathStr, "input", "target");
192 std::ifstream pwmFile(pwmPathStr);
193 if (!pwmFile.good())
194 {
195 continue;
196 }
197
198 auto findPWMSensor = pwmSensors.find(psuName + labelHead);
199 if (findPWMSensor != pwmSensors.end())
200 {
201 continue;
202 }
203
204 pwmSensors[psuName + labelHead] = std::make_unique<PwmSensor>(
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530205 "Pwm_" + psuName + "_" + pwmName.second, pwmPathStr, dbusConnection,
206 objectServer, interfacePath + "_" + pwmName.second, "PSU");
Cheng C Yang916360b2019-05-07 18:47:16 +0800207 }
208}
209
210void createSensors(boost::asio::io_service& io,
211 sdbusplus::asio::object_server& objectServer,
212 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
Cheng C Yang209ec562019-03-12 16:37:44 +0800213{
214
215 ManagedObjectType sensorConfigs;
Josh Lehan49cfba92019-10-08 16:50:42 -0700216 int numCreated = 0;
Cheng C Yang209ec562019-03-12 16:37:44 +0800217 bool useCache = false;
218
Cheng C Yang58b2b532019-05-31 00:19:45 +0800219 // TODO may need only modify the ones that need to be changed.
220 sensors.clear();
Cheng C Yang92498eb2019-09-26 21:59:25 +0800221 combineEvents.clear();
Cheng C Yang209ec562019-03-12 16:37:44 +0800222 for (const char* type : sensorTypes)
223 {
224 if (!getSensorConfiguration(type, dbusConnection, sensorConfigs,
225 useCache))
226 {
227 std::cerr << "error get sensor config from entity manager\n";
228 return;
229 }
230 useCache = true;
231 }
232
233 std::vector<fs::path> pmbusPaths;
234 if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
235 {
236 std::cerr << "No PSU sensors in system\n";
237 return;
238 }
239
240 boost::container::flat_set<std::string> directories;
241 for (const auto& pmbusPath : pmbusPaths)
242 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800243 boost::container::flat_map<std::string, std::vector<std::string>>
244 eventPathList;
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800245 boost::container::flat_map<
246 std::string,
247 boost::container::flat_map<std::string, std::vector<std::string>>>
248 groupEventPathList;
Cheng C Yang58b2b532019-05-31 00:19:45 +0800249
250 std::ifstream nameFile(pmbusPath);
251 if (!nameFile.good())
252 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700253 std::cerr << "Failure finding pmbus path " << pmbusPath << "\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800254 continue;
255 }
256
257 std::string pmbusName;
258 std::getline(nameFile, pmbusName);
259 nameFile.close();
Vijay Khemka996bad12019-05-28 15:15:16 -0700260
261 if (std::find(pmbusNames.begin(), pmbusNames.end(), pmbusName) ==
262 pmbusNames.end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800263 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700264 // To avoid this error message, add your driver name to
265 // the pmbusNames vector at the top of this file.
266 std::cerr << "Driver name " << pmbusName
267 << " not found in sensor whitelist\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800268 continue;
269 }
270
271 const std::string* psuName;
Cheng C Yang209ec562019-03-12 16:37:44 +0800272 auto directory = pmbusPath.parent_path();
273
274 auto ret = directories.insert(directory.string());
275 if (!ret.second)
276 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700277 std::cerr << "Duplicate path " << directory.string() << "\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800278 continue; // check if path has already been searched
Cheng C Yang209ec562019-03-12 16:37:44 +0800279 }
280
James Feistb6c0b912019-07-09 12:21:44 -0700281 fs::path device = directory / "device";
Cheng C Yang209ec562019-03-12 16:37:44 +0800282 std::string deviceName = fs::canonical(device).stem();
283 auto findHyphen = deviceName.find("-");
284 if (findHyphen == std::string::npos)
285 {
286 std::cerr << "found bad device" << deviceName << "\n";
287 continue;
288 }
289 std::string busStr = deviceName.substr(0, findHyphen);
290 std::string addrStr = deviceName.substr(findHyphen + 1);
291
292 size_t bus = 0;
293 size_t addr = 0;
294
295 try
296 {
297 bus = std::stoi(busStr);
298 addr = std::stoi(addrStr, 0, 16);
299 }
James Feistb6c0b912019-07-09 12:21:44 -0700300 catch (std::invalid_argument&)
Cheng C Yang209ec562019-03-12 16:37:44 +0800301 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700302 std::cerr << "Error parsing bus " << busStr << " addr " << addrStr
303 << "\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800304 continue;
305 }
306
Cheng C Yang209ec562019-03-12 16:37:44 +0800307 const std::pair<std::string, boost::container::flat_map<
308 std::string, BasicVariantType>>*
309 baseConfig = nullptr;
310 const SensorData* sensorData = nullptr;
311 const std::string* interfacePath = nullptr;
312 const char* sensorType = nullptr;
313
314 for (const std::pair<sdbusplus::message::object_path, SensorData>&
315 sensor : sensorConfigs)
316 {
317 sensorData = &(sensor.second);
318 for (const char* type : sensorTypes)
319 {
320 auto sensorBase = sensorData->find(type);
321 if (sensorBase != sensorData->end())
322 {
323 baseConfig = &(*sensorBase);
324 sensorType = type;
325 break;
326 }
327 }
328 if (baseConfig == nullptr)
329 {
330 std::cerr << "error finding base configuration for "
331 << deviceName << "\n";
332 continue;
333 }
334
335 auto configBus = baseConfig->second.find("Bus");
336 auto configAddress = baseConfig->second.find("Address");
337
338 if (configBus == baseConfig->second.end() ||
339 configAddress == baseConfig->second.end())
340 {
Cheng C Yang58b2b532019-05-31 00:19:45 +0800341 std::cerr << "error finding necessary entry in configuration\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800342 continue;
343 }
344
Cheng C Yang58b2b532019-05-31 00:19:45 +0800345 const uint64_t* confBus;
346 const uint64_t* confAddr;
347 if (!(confBus = std::get_if<uint64_t>(&(configBus->second))) ||
348 !(confAddr = std::get_if<uint64_t>(&(configAddress->second))))
349 {
350 std::cerr
Josh Lehan49cfba92019-10-08 16:50:42 -0700351 << "Cannot get bus or address, invalid configuration\n";
Cheng C Yang58b2b532019-05-31 00:19:45 +0800352 continue;
353 }
354
355 if ((*confBus != bus) || (*confAddr != addr))
Cheng C Yang209ec562019-03-12 16:37:44 +0800356 {
Josh Lehan432d1ed2019-10-16 12:23:31 -0700357 std::cerr << "Configuration skipping " << *confBus << "-"
358 << *confAddr << " because not " << bus << "-" << addr
359 << "\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800360 continue;
361 }
362
363 interfacePath = &(sensor.first.str);
364 break;
365 }
366 if (interfacePath == nullptr)
367 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700368 // To avoid this error message, add your export map entry,
369 // from Entity Manager, to sensorTypes at the top of this file.
Cheng C Yang209ec562019-03-12 16:37:44 +0800370 std::cerr << "failed to find match for " << deviceName << "\n";
371 continue;
372 }
373
Cheng C Yange50345b2019-04-02 17:26:15 +0800374 auto findPSUName = baseConfig->second.find("Name");
375 if (findPSUName == baseConfig->second.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800376 {
377 std::cerr << "could not determine configuration name for "
378 << deviceName << "\n";
379 continue;
380 }
381
Cheng C Yang58b2b532019-05-31 00:19:45 +0800382 if (!(psuName = std::get_if<std::string>(&(findPSUName->second))))
383 {
384 std::cerr << "Cannot find psu name, invalid configuration\n";
385 continue;
386 }
387 checkEvent(directory.string(), eventMatch, eventPathList);
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800388 checkGroupEvent(directory.string(), groupEventMatch,
389 groupEventPathList);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800390
Vijay Khemka996bad12019-05-28 15:15:16 -0700391 /* Check if there are more sensors in the same interface */
392 int i = 1;
393 std::vector<std::string> psuNames;
394 do
395 {
Josh Lehan49cfba92019-10-08 16:50:42 -0700396 // Individual string fields: Name, Name1, Name2, Name3, ...
Vijay Khemka996bad12019-05-28 15:15:16 -0700397 psuNames.push_back(std::get<std::string>(findPSUName->second));
398 findPSUName = baseConfig->second.find("Name" + std::to_string(i++));
399 } while (findPSUName != baseConfig->second.end());
400
Cheng C Yange50345b2019-04-02 17:26:15 +0800401 std::vector<fs::path> sensorPaths;
James Feistb6c0b912019-07-09 12:21:44 -0700402 if (!findFiles(directory, R"(\w\d+_input$)", sensorPaths, 0))
Cheng C Yang209ec562019-03-12 16:37:44 +0800403 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800404 std::cerr << "No PSU non-label sensor in PSU\n";
Cheng C Yang209ec562019-03-12 16:37:44 +0800405 continue;
406 }
407
Vijay Khemka996bad12019-05-28 15:15:16 -0700408 /* Find array of labels to be exposed if it is defined in config */
409 std::vector<std::string> findLabels;
410 auto findLabelObj = baseConfig->second.find("Labels");
411 if (findLabelObj != baseConfig->second.end())
412 {
413 findLabels =
414 std::get<std::vector<std::string>>(findLabelObj->second);
415 }
416
Jason Ling5747fab2019-10-02 16:46:23 -0700417 std::regex sensorNameRegEx("([A-Za-z]+)[0-9]*_");
418 std::smatch matches;
419
Cheng C Yange50345b2019-04-02 17:26:15 +0800420 for (const auto& sensorPath : sensorPaths)
Cheng C Yang209ec562019-03-12 16:37:44 +0800421 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800422 std::string labelHead;
423 std::string sensorPathStr = sensorPath.string();
424 std::string sensorNameStr = sensorPath.filename();
Jason Ling5747fab2019-10-02 16:46:23 -0700425 std::string sensorNameSubStr{""};
426 if (std::regex_search(sensorNameStr, matches, sensorNameRegEx))
427 {
Josh Lehan06494452019-10-31 09:49:16 -0700428 // hwmon *_input filename without number:
429 // in, curr, power, temp, ...
Jason Ling5747fab2019-10-02 16:46:23 -0700430 sensorNameSubStr = matches[1];
431 }
432 else
433 {
Josh Lehan06494452019-10-31 09:49:16 -0700434 std::cerr << "Could not extract the alpha prefix from "
Jason Ling5747fab2019-10-02 16:46:23 -0700435 << sensorNameStr;
436 continue;
437 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800438
Cheng C Yangecba9de2019-09-12 23:41:50 +0800439 auto labelPath =
440 boost::replace_all_copy(sensorPathStr, "input", "label");
441 std::ifstream labelFile(labelPath);
442 if (!labelFile.good())
Cheng C Yang209ec562019-03-12 16:37:44 +0800443 {
Josh Lehan432d1ed2019-10-16 12:23:31 -0700444 std::cerr << "Input file " << sensorPath
445 << " has no corresponding label file\n";
Josh Lehan06494452019-10-31 09:49:16 -0700446
447 // hwmon *_input filename with number:
448 // temp1, temp2, temp3, ...
Cheng C Yange50345b2019-04-02 17:26:15 +0800449 labelHead = sensorNameStr.substr(0, sensorNameStr.find("_"));
Cheng C Yang209ec562019-03-12 16:37:44 +0800450 }
451 else
452 {
Cheng C Yange50345b2019-04-02 17:26:15 +0800453 std::string label;
454 std::getline(labelFile, label);
455 labelFile.close();
Cheng C Yange50345b2019-04-02 17:26:15 +0800456 auto findSensor = sensors.find(label);
457 if (findSensor != sensors.end())
458 {
459 continue;
460 }
461
Josh Lehan06494452019-10-31 09:49:16 -0700462 // hwmon corresponding *_label file contents:
463 // vin1, vout1, ...
Cheng C Yange50345b2019-04-02 17:26:15 +0800464 labelHead = label.substr(0, label.find(" "));
465 }
466
Josh Lehan74d9bd92019-10-31 08:51:58 -0700467 if constexpr (DEBUG)
468 {
469 std::cerr << "Sensor type=\"" << sensorNameSubStr
470 << "\" label=\"" << labelHead << "\"\n";
471 }
472
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530473 checkPWMSensor(sensorPath, labelHead, *interfacePath,
474 dbusConnection, objectServer, psuNames[0]);
Cheng C Yang916360b2019-05-07 18:47:16 +0800475
Vijay Khemka996bad12019-05-28 15:15:16 -0700476 if (!findLabels.empty())
477 {
478 /* Check if this labelHead is enabled in config file */
479 if (std::find(findLabels.begin(), findLabels.end(),
480 labelHead) == findLabels.end())
481 {
Josh Lehan06494452019-10-31 09:49:16 -0700482 std::cerr << "could not find " << labelHead
Jason Ling5747fab2019-10-02 16:46:23 -0700483 << " in the Labels list\n";
Vijay Khemka996bad12019-05-28 15:15:16 -0700484 continue;
485 }
486 }
Cheng C Yange50345b2019-04-02 17:26:15 +0800487
Cheng C Yange50345b2019-04-02 17:26:15 +0800488 auto findProperty = labelMatch.find(labelHead);
489 if (findProperty == labelMatch.end())
Cheng C Yang209ec562019-03-12 16:37:44 +0800490 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700491 if constexpr (DEBUG)
492 {
493 std::cerr << "Could not find matching default property for "
494 << labelHead << "\n";
495 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800496 continue;
497 }
498
Josh Lehan74d9bd92019-10-31 08:51:58 -0700499 // Protect the hardcoded labelMatch list from changes,
500 // by making a copy and modifying that instead.
501 // Avoid bleedthrough of one device's customizations to
502 // the next device, as each should be independently customizable.
503 psuProperties.push_back(findProperty->second);
504 auto psuProperty = psuProperties.rbegin();
505
506 // Use label head as prefix for reading from config file,
507 // example if temp1: temp1_Name, temp1_Scale, temp1_Min, ...
508 std::string keyName = labelHead + "_Name";
509 std::string keyScale = labelHead + "_Scale";
510 std::string keyMin = labelHead + "_Min";
511 std::string keyMax = labelHead + "_Max";
512
513 bool customizedName = false;
514 auto findCustomName = baseConfig->second.find(keyName);
515 if (findCustomName != baseConfig->second.end())
Josh Lehan432d1ed2019-10-16 12:23:31 -0700516 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700517 try
518 {
519 psuProperty->labelTypeName = std::visit(
520 VariantToStringVisitor(), findCustomName->second);
521 }
522 catch (std::invalid_argument&)
523 {
524 std::cerr << "Unable to parse " << keyName << "\n";
525 continue;
526 }
527
528 // All strings are valid, including empty string
529 customizedName = true;
530 }
531
532 bool customizedScale = false;
533 auto findCustomScale = baseConfig->second.find(keyScale);
534 if (findCustomScale != baseConfig->second.end())
535 {
536 try
537 {
538 psuProperty->sensorScaleFactor = std::visit(
539 VariantToUnsignedIntVisitor(), findCustomScale->second);
540 }
541 catch (std::invalid_argument&)
542 {
543 std::cerr << "Unable to parse " << keyScale << "\n";
544 continue;
545 }
546
547 // Avoid later division by zero
548 if (psuProperty->sensorScaleFactor > 0)
549 {
550 customizedScale = true;
551 }
552 else
553 {
554 std::cerr << "Unable to accept " << keyScale << "\n";
555 continue;
556 }
557 }
558
559 auto findCustomMin = baseConfig->second.find(keyMin);
560 if (findCustomMin != baseConfig->second.end())
561 {
562 try
563 {
564 psuProperty->minReading = std::visit(
565 VariantToDoubleVisitor(), findCustomMin->second);
566 }
567 catch (std::invalid_argument&)
568 {
569 std::cerr << "Unable to parse " << keyMin << "\n";
570 continue;
571 }
572 }
573
574 auto findCustomMax = baseConfig->second.find(keyMax);
575 if (findCustomMax != baseConfig->second.end())
576 {
577 try
578 {
579 psuProperty->maxReading = std::visit(
580 VariantToDoubleVisitor(), findCustomMax->second);
581 }
582 catch (std::invalid_argument&)
583 {
584 std::cerr << "Unable to parse " << keyMax << "\n";
585 continue;
586 }
587 }
588
589 if (!(psuProperty->minReading < psuProperty->maxReading))
590 {
591 std::cerr << "Min must be less than Max\n";
592 continue;
593 }
594
595 // If the sensor name is being customized by config file,
596 // then prefix/suffix composition becomes not necessary,
597 // and in fact not wanted, because it gets in the way.
598 std::string psuNameFromIndex;
599 if (!customizedName)
600 {
601 /* Find out sensor name index for this label */
602 std::regex rgx("[A-Za-z]+([0-9]+)");
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500603 size_t nameIndex{0};
Josh Lehan74d9bd92019-10-31 08:51:58 -0700604 if (std::regex_search(labelHead, matches, rgx))
605 {
606 nameIndex = std::stoi(matches[1]);
607
608 // Decrement to preserve alignment, because hwmon
609 // human-readable filenames and labels use 1-based
610 // numbering, but the "Name", "Name1", "Name2", etc. naming
611 // convention (the psuNames vector) uses 0-based numbering.
612 if (nameIndex > 0)
613 {
614 --nameIndex;
615 }
616 }
617 else
618 {
619 nameIndex = 0;
620 }
621
622 if (psuNames.size() <= nameIndex)
623 {
624 std::cerr << "Could not pair " << labelHead
625 << " with a Name field\n";
626 continue;
627 }
628
629 psuNameFromIndex = psuNames[nameIndex];
630
631 if constexpr (DEBUG)
632 {
633 std::cerr << "Sensor label head " << labelHead
634 << " paired with " << psuNameFromIndex
635 << " at index " << nameIndex << "\n";
636 }
Josh Lehan432d1ed2019-10-16 12:23:31 -0700637 }
638
Cheng C Yang58b2b532019-05-31 00:19:45 +0800639 checkEventLimits(sensorPathStr, limitEventMatch, eventPathList);
640
Josh Lehan74d9bd92019-10-31 08:51:58 -0700641 // Similarly, if sensor scaling factor is being customized,
642 // then the below power-of-10 constraint becomes unnecessary,
643 // as config should be able to specify an arbitrary divisor.
644 unsigned int factor = psuProperty->sensorScaleFactor;
645 if (!customizedScale)
Vijay Khemka53ca4442019-07-23 11:03:55 -0700646 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700647 // Preserve existing usage of hardcoded labelMatch table below
648 factor = std::pow(10.0, factor);
Vijay Khemka53ca4442019-07-23 11:03:55 -0700649
Josh Lehan74d9bd92019-10-31 08:51:58 -0700650 /* Change first char of substring to uppercase */
651 char firstChar = sensorNameSubStr[0] - 0x20;
652 std::string strScaleFactor =
653 firstChar + sensorNameSubStr.substr(1) + "ScaleFactor";
654
655 // Preserve existing configs by accepting earlier syntax,
656 // example CurrScaleFactor, PowerScaleFactor, ...
657 auto findScaleFactor = baseConfig->second.find(strScaleFactor);
658 if (findScaleFactor != baseConfig->second.end())
659 {
660 factor = std::visit(VariantToIntVisitor(),
661 findScaleFactor->second);
662 }
663
664 if constexpr (DEBUG)
665 {
666 std::cerr << "Sensor scaling factor " << factor
667 << " string " << strScaleFactor << "\n";
668 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700669 }
670
Vijay Khemka996bad12019-05-28 15:15:16 -0700671 std::vector<thresholds::Threshold> sensorThresholds;
672
Joshi, Mansi14f0ad82019-11-21 10:52:30 +0530673 if (!parseThresholdsFromConfig(*sensorData, sensorThresholds,
674 &labelHead))
Cheng C Yange50345b2019-04-02 17:26:15 +0800675 {
James Feist17ab6e02019-06-25 12:28:13 -0700676 std::cerr << "error populating thresholds for "
677 << sensorNameSubStr << "\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800678 }
679
680 auto findSensorType = sensorTable.find(sensorNameSubStr);
681 if (findSensorType == sensorTable.end())
682 {
Jason Ling5747fab2019-10-02 16:46:23 -0700683 std::cerr << sensorNameSubStr
Josh Lehan06494452019-10-31 09:49:16 -0700684 << " is not a recognized sensor type\n";
Cheng C Yange50345b2019-04-02 17:26:15 +0800685 continue;
686 }
687
Josh Lehan49cfba92019-10-08 16:50:42 -0700688 if constexpr (DEBUG)
689 {
Josh Lehan74d9bd92019-10-31 08:51:58 -0700690 std::cerr << "Sensor properties: Name \""
691 << psuProperty->labelTypeName << "\" Scale "
692 << psuProperty->sensorScaleFactor << " Min "
693 << psuProperty->minReading << " Max "
694 << psuProperty->maxReading << "\n";
695 }
696
697 std::string sensorName = psuProperty->labelTypeName;
698 if (customizedName)
699 {
700 if (sensorName.empty())
701 {
702 // Allow selective disabling of an individual sensor,
703 // by customizing its name to an empty string.
704 std::cerr << "Sensor disabled, empty string\n";
705 continue;
706 }
707 }
708 else
709 {
710 // Sensor name not customized, do prefix/suffix composition,
711 // preserving default behavior by using psuNameFromIndex.
712 sensorName =
713 psuNameFromIndex + " " + psuProperty->labelTypeName;
714 }
715
716 if constexpr (DEBUG)
717 {
718 std::cerr << "Sensor name \"" << sensorName << "\" path \""
719 << sensorPathStr << "\" type \"" << sensorType
720 << "\"\n";
Josh Lehan49cfba92019-10-08 16:50:42 -0700721 }
722
Cheng C Yang58b2b532019-05-31 00:19:45 +0800723 sensors[sensorName] = std::make_unique<PSUSensor>(
Cheng C Yange50345b2019-04-02 17:26:15 +0800724 sensorPathStr, sensorType, objectServer, dbusConnection, io,
Cheng C Yang209ec562019-03-12 16:37:44 +0800725 sensorName, std::move(sensorThresholds), *interfacePath,
Josh Lehan74d9bd92019-10-31 08:51:58 -0700726 findSensorType->second, factor, psuProperty->maxReading,
727 psuProperty->minReading);
728
729 ++numCreated;
730 if constexpr (DEBUG)
731 {
732 std::cerr << "Created " << numCreated << " sensors so far\n";
733 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800734 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800735
736 // OperationalStatus event
Cheng C Yang92498eb2019-09-26 21:59:25 +0800737 combineEvents[*psuName + "OperationalStatus"] = nullptr;
Cheng C Yang58b2b532019-05-31 00:19:45 +0800738 combineEvents[*psuName + "OperationalStatus"] =
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800739 std::make_unique<PSUCombineEvent>(objectServer, io, *psuName,
740 eventPathList, groupEventPathList,
741 "OperationalStatus");
Cheng C Yang209ec562019-03-12 16:37:44 +0800742 }
Josh Lehan49cfba92019-10-08 16:50:42 -0700743
744 if constexpr (DEBUG)
745 {
746 std::cerr << "Created total of " << numCreated << " sensors\n";
747 }
Cheng C Yang209ec562019-03-12 16:37:44 +0800748 return;
749}
750
Cheng C Yang916360b2019-05-07 18:47:16 +0800751void propertyInitialize(void)
Cheng C Yang209ec562019-03-12 16:37:44 +0800752{
Cheng C Yange50345b2019-04-02 17:26:15 +0800753 sensorTable = {{"power", "power/"},
754 {"curr", "current/"},
755 {"temp", "temperature/"},
756 {"in", "voltage/"},
757 {"fan", "fan_tach/"}};
758
759 labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
760 {"pout1", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700761 {"pout2", PSUProperty("Output Power", 3000, 0, 6)},
762 {"pout3", PSUProperty("Output Power", 3000, 0, 6)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700763 {"power1", PSUProperty("Output Power", 3000, 0, 6)},
Cheng C Yangbdbde962019-04-26 22:50:34 +0800764 {"vin", PSUProperty("Input Voltage", 300, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700765 {"vout1", PSUProperty("Output Voltage", 255, 0, 3)},
766 {"vout2", PSUProperty("Output Voltage", 255, 0, 3)},
767 {"vout3", PSUProperty("Output Voltage", 255, 0, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700768 {"vout4", PSUProperty("Output Voltage", 255, 0, 3)},
769 {"vout5", PSUProperty("Output Voltage", 255, 0, 3)},
770 {"vout6", PSUProperty("Output Voltage", 255, 0, 3)},
771 {"vout7", PSUProperty("Output Voltage", 255, 0, 3)},
772 {"vout8", PSUProperty("Output Voltage", 255, 0, 3)},
773 {"vout9", PSUProperty("Output Voltage", 255, 0, 3)},
774 {"vout10", PSUProperty("Output Voltage", 255, 0, 3)},
775 {"vout11", PSUProperty("Output Voltage", 255, 0, 3)},
776 {"vout12", PSUProperty("Output Voltage", 255, 0, 3)},
777 {"vout13", PSUProperty("Output Voltage", 255, 0, 3)},
778 {"vout14", PSUProperty("Output Voltage", 255, 0, 3)},
779 {"vout15", PSUProperty("Output Voltage", 255, 0, 3)},
780 {"vout16", PSUProperty("Output Voltage", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700781 {"in1", PSUProperty("Output Voltage", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800782 {"iin", PSUProperty("Input Current", 20, 0, 3)},
783 {"iout1", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700784 {"iout2", PSUProperty("Output Current", 255, 0, 3)},
785 {"iout3", PSUProperty("Output Current", 255, 0, 3)},
Peter Lundgren48b44232020-01-30 16:02:11 -0800786 {"iout4", PSUProperty("Output Current", 255, 0, 3)},
787 {"iout5", PSUProperty("Output Current", 255, 0, 3)},
788 {"iout6", PSUProperty("Output Current", 255, 0, 3)},
789 {"iout7", PSUProperty("Output Current", 255, 0, 3)},
790 {"iout8", PSUProperty("Output Current", 255, 0, 3)},
791 {"iout9", PSUProperty("Output Current", 255, 0, 3)},
792 {"iout10", PSUProperty("Output Current", 255, 0, 3)},
793 {"iout11", PSUProperty("Output Current", 255, 0, 3)},
794 {"iout12", PSUProperty("Output Current", 255, 0, 3)},
795 {"iout13", PSUProperty("Output Current", 255, 0, 3)},
796 {"iout14", PSUProperty("Output Current", 255, 0, 3)},
Vijay Khemkabe664412019-06-28 12:10:04 -0700797 {"curr1", PSUProperty("Output Current", 255, 0, 3)},
Cheng C Yange50345b2019-04-02 17:26:15 +0800798 {"temp1", PSUProperty("Temperature", 127, -128, 3)},
Vijay Khemka996bad12019-05-28 15:15:16 -0700799 {"temp2", PSUProperty("Temperature", 127, -128, 3)},
800 {"temp3", PSUProperty("Temperature", 127, -128, 3)},
Jason Ling5747fab2019-10-02 16:46:23 -0700801 {"temp4", PSUProperty("Temperature", 127, -128, 3)},
802 {"temp5", PSUProperty("Temperature", 127, -128, 3)},
Cheng C Yang8dbb3952019-05-23 00:03:12 +0800803 {"fan1", PSUProperty("Fan Speed 1", 30000, 0, 0)},
804 {"fan2", PSUProperty("Fan Speed 2", 30000, 0, 0)}};
Cheng C Yang916360b2019-05-07 18:47:16 +0800805
806 pwmTable = {{"fan1", "Fan_1"}, {"fan2", "Fan_2"}};
Cheng C Yang58b2b532019-05-31 00:19:45 +0800807
808 limitEventMatch = {{"PredictiveFailure", {"max_alarm", "min_alarm"}},
809 {"Failure", {"crit_alarm", "lcrit_alarm"}}};
810
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800811 eventMatch = {{"PredictiveFailure", {"power1_alarm"}},
812 {"Failure", {"in2_alarm"}},
813 {"ACLost", {"in1_beep"}},
814 {"ConfigureError", {"in1_fault"}}};
815
816 groupEventMatch = {{"FanFault",
817 {{"fan1", {"fan1_alarm", "fan1_fault"}},
818 {"fan2", {"fan2_alarm", "fan2_fault"}}}}};
Cheng C Yang209ec562019-03-12 16:37:44 +0800819}
820
James Feistb6c0b912019-07-09 12:21:44 -0700821int main()
Cheng C Yang209ec562019-03-12 16:37:44 +0800822{
823 boost::asio::io_service io;
824 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
825
826 systemBus->request_name("xyz.openbmc_project.PSUSensor");
827 sdbusplus::asio::object_server objectServer(systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800828 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Cheng C Yang209ec562019-03-12 16:37:44 +0800829
Cheng C Yang916360b2019-05-07 18:47:16 +0800830 propertyInitialize();
Cheng C Yang209ec562019-03-12 16:37:44 +0800831
Cheng C Yang916360b2019-05-07 18:47:16 +0800832 io.post([&]() { createSensors(io, objectServer, systemBus); });
Cheng C Yang209ec562019-03-12 16:37:44 +0800833 boost::asio::deadline_timer filterTimer(io);
834 std::function<void(sdbusplus::message::message&)> eventHandler =
835 [&](sdbusplus::message::message& message) {
836 if (message.is_method_error())
837 {
838 std::cerr << "callback method error\n";
839 return;
840 }
Cheng C Yanga97f1342020-02-11 15:10:41 +0800841 filterTimer.expires_from_now(boost::posix_time::seconds(3));
Cheng C Yang209ec562019-03-12 16:37:44 +0800842 filterTimer.async_wait([&](const boost::system::error_code& ec) {
843 if (ec == boost::asio::error::operation_aborted)
844 {
845 return;
846 }
847 else if (ec)
848 {
849 std::cerr << "timer error\n";
850 }
Cheng C Yang916360b2019-05-07 18:47:16 +0800851 createSensors(io, objectServer, systemBus);
Cheng C Yang209ec562019-03-12 16:37:44 +0800852 });
853 };
854
855 for (const char* type : sensorTypes)
856 {
857 auto match = std::make_unique<sdbusplus::bus::match::match>(
858 static_cast<sdbusplus::bus::bus&>(*systemBus),
859 "type='signal',member='PropertiesChanged',path_namespace='" +
860 std::string(inventoryPath) + "',arg0namespace='" + type + "'",
861 eventHandler);
862 matches.emplace_back(std::move(match));
863 }
864 io.run();
865}