blob: bcbb5a56e9632fd119e9821fe675e9c3a8720445 [file] [log] [blame]
/*
// Copyright (c) 2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#include "filesystem.hpp"
#include <PSUSensor.hpp>
#include <Utils.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/container/flat_set.hpp>
#include <fstream>
#include <regex>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
static constexpr std::array<const char*, 1> sensorTypes = {
"xyz.openbmc_project.Configuration.pmbus"};
namespace fs = std::filesystem;
void createSensors(
boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>>&
sensors,
boost::container::flat_map<std::string, std::string>& sensorTable,
boost::container::flat_map<std::string, PSUProperty>& labelMatch)
{
ManagedObjectType sensorConfigs;
bool useCache = false;
for (const char* type : sensorTypes)
{
if (!getSensorConfiguration(type, dbusConnection, sensorConfigs,
useCache))
{
std::cerr << "error get sensor config from entity manager\n";
return;
}
useCache = true;
}
std::vector<fs::path> pmbusPaths;
if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
{
std::cerr << "No PSU sensors in system\n";
return;
}
boost::container::flat_set<std::string> directories;
for (const auto& pmbusPath : pmbusPaths)
{
const std::string pathStr = pmbusPath.string();
auto directory = pmbusPath.parent_path();
auto ret = directories.insert(directory.string());
if (!ret.second)
{
continue; // check if path i1 already searched
}
auto device = fs::path(directory / "device");
std::string deviceName = fs::canonical(device).stem();
auto findHyphen = deviceName.find("-");
if (findHyphen == std::string::npos)
{
std::cerr << "found bad device" << deviceName << "\n";
continue;
}
std::string busStr = deviceName.substr(0, findHyphen);
std::string addrStr = deviceName.substr(findHyphen + 1);
size_t bus = 0;
size_t addr = 0;
try
{
bus = std::stoi(busStr);
addr = std::stoi(addrStr, 0, 16);
}
catch (std::invalid_argument)
{
continue;
}
std::ifstream nameFile(pmbusPath);
if (!nameFile.good())
{
std::cerr << "Failure reading " << pmbusPath << "\n";
continue;
}
std::string pmbusName;
std::getline(nameFile, pmbusName);
nameFile.close();
if (pmbusName != "pmbus")
{
continue;
}
const std::pair<std::string, boost::container::flat_map<
std::string, BasicVariantType>>*
baseConfig = nullptr;
const SensorData* sensorData = nullptr;
const std::string* interfacePath = nullptr;
const char* sensorType = nullptr;
for (const std::pair<sdbusplus::message::object_path, SensorData>&
sensor : sensorConfigs)
{
sensorData = &(sensor.second);
for (const char* type : sensorTypes)
{
auto sensorBase = sensorData->find(type);
if (sensorBase != sensorData->end())
{
baseConfig = &(*sensorBase);
sensorType = type;
break;
}
}
if (baseConfig == nullptr)
{
std::cerr << "error finding base configuration for "
<< deviceName << "\n";
continue;
}
auto configBus = baseConfig->second.find("Bus");
auto configAddress = baseConfig->second.find("Address");
if (configBus == baseConfig->second.end() ||
configAddress == baseConfig->second.end())
{
std::cerr << "error finding necessary entry in configuration";
continue;
}
if (std::get<uint64_t>(configBus->second) != bus ||
std::get<uint64_t>(configAddress->second) != addr)
{
continue;
}
interfacePath = &(sensor.first.str);
break;
}
if (interfacePath == nullptr)
{
std::cerr << "failed to find match for " << deviceName << "\n";
continue;
}
auto findPSUName = baseConfig->second.find("Name");
if (findPSUName == baseConfig->second.end())
{
std::cerr << "could not determine configuration name for "
<< deviceName << "\n";
continue;
}
std::vector<fs::path> sensorPaths;
if (!findFiles(fs::path(directory), R"(\w\d+_input$)", sensorPaths, 0))
{
std::cerr << "No PSU non-label sensor in PSU\n";
continue;
}
for (const auto& sensorPath : sensorPaths)
{
std::string labelHead;
std::string sensorPathStr = sensorPath.string();
std::string sensorNameStr = sensorPath.filename();
std::string sensorNameSubStr =
sensorNameStr.substr(0, sensorNameStr.find("_") - 1);
std::string labelPathStr =
boost::replace_all_copy(sensorNameStr, "input", "label");
std::vector<fs::path> labelPaths;
if (!findFiles(fs::path(directory), labelPathStr, labelPaths, 0))
{
std::cerr << "No PSU non-label sensor in PSU\n";
continue;
}
if (labelPaths.empty())
{
labelHead = sensorNameStr.substr(0, sensorNameStr.find("_"));
}
else
{
auto labelPath =
boost::replace_all_copy(sensorPathStr, "input", "label");
std::ifstream labelFile(labelPath);
if (!labelFile.good())
{
std::cerr << "Failure reading " << sensorPath << "\n";
continue;
}
std::string label;
std::getline(labelFile, label);
labelFile.close();
auto findSensor = sensors.find(label);
if (findSensor != sensors.end())
{
continue;
}
labelHead = label.substr(0, label.find(" "));
}
std::vector<thresholds::Threshold> sensorThresholds;
parseThresholdsFromConfig(*sensorData, sensorThresholds,
&labelHead);
auto findProperty = labelMatch.find(labelHead);
if (findProperty == labelMatch.end())
{
continue;
}
unsigned int factor =
std::pow(10, findProperty->second.sensorScaleFactor);
if (sensorThresholds.empty())
{
if (!parseThresholdsFromAttr(sensorThresholds, sensorPathStr,
factor))
{
std::cerr << "error populating thresholds\n";
}
}
auto findSensorType = sensorTable.find(sensorNameSubStr);
if (findSensorType == sensorTable.end())
{
std::cerr << "Cannot find PSU sensorType\n";
continue;
}
std::string sensorName =
std::get<std::string>(findPSUName->second) + " " +
findProperty->second.labelTypeName;
sensors[sensorName] = std::make_unique<PSUSensor>(
sensorPathStr, sensorType, objectServer, dbusConnection, io,
sensorName, std::move(sensorThresholds), *interfacePath,
findSensorType->second, factor, findProperty->second.maxReading,
findProperty->second.minReading);
}
}
return;
}
void propertyInitialize(
boost::container::flat_map<std::string, std::string>& sensorTable,
boost::container::flat_map<std::string, PSUProperty>& labelMatch)
{
sensorTable = {{"power", "power/"},
{"curr", "current/"},
{"temp", "temperature/"},
{"in", "voltage/"},
{"fan", "fan_tach/"}};
labelMatch = {{"pin", PSUProperty("Input Power", 3000, 0, 6)},
{"pout1", PSUProperty("Output Power", 3000, 0, 6)},
{"vin", PSUProperty("Input Voltage", 255, 0, 3)},
{"iin", PSUProperty("Input Current", 20, 0, 3)},
{"iout1", PSUProperty("Output Current", 255, 0, 3)},
{"temp1", PSUProperty("Temperature", 127, -128, 3)},
{"fan1", PSUProperty("Fan Speed 1", 10000, 0, 0)},
{"fan2", PSUProperty("Fan Speed 2", 10000, 0, 0)}};
}
int main(int argc, char** argv)
{
boost::asio::io_service io;
auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
systemBus->request_name("xyz.openbmc_project.PSUSensor");
sdbusplus::asio::object_server objectServer(systemBus);
boost::container::flat_map<std::string, std::unique_ptr<PSUSensor>> sensors;
boost::container::flat_map<std::string, std::string> sensorTable;
std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
boost::container::flat_map<std::string, PSUProperty> labelMatch;
propertyInitialize(sensorTable, labelMatch);
io.post([&]() {
createSensors(io, objectServer, systemBus, sensors, sensorTable,
labelMatch);
});
boost::asio::deadline_timer filterTimer(io);
std::function<void(sdbusplus::message::message&)> eventHandler =
[&](sdbusplus::message::message& message) {
if (message.is_method_error())
{
std::cerr << "callback method error\n";
return;
}
filterTimer.expires_from_now(boost::posix_time::seconds(1));
filterTimer.async_wait([&](const boost::system::error_code& ec) {
if (ec == boost::asio::error::operation_aborted)
{
return;
}
else if (ec)
{
std::cerr << "timer error\n";
}
createSensors(io, objectServer, systemBus, sensors, sensorTable,
labelMatch);
});
};
for (const char* type : sensorTypes)
{
auto match = std::make_unique<sdbusplus::bus::match::match>(
static_cast<sdbusplus::bus::bus&>(*systemBus),
"type='signal',member='PropertiesChanged',path_namespace='" +
std::string(inventoryPath) + "',arg0namespace='" + type + "'",
eventHandler);
matches.emplace_back(std::move(match));
}
io.run();
}