blob: bd8960ba5c21b9e1a6b441cda516c74c2e7d1820 [file] [log] [blame]
/*
// Copyright (c) 2018 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 "ExitAirTempSensor.hpp"
#include "SensorPaths.hpp"
#include "Thresholds.hpp"
#include "Utils.hpp"
#include "VariantVisitors.hpp"
#include "sensor.hpp"
#include <boost/algorithm/string/replace.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/container/flat_map.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/bus/match.hpp>
#include <sdbusplus/message.hpp>
#include <algorithm>
#include <array>
#include <chrono>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <iostream>
#include <limits>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include <variant>
#include <vector>
constexpr const double altitudeFactor = 1.14;
constexpr const char* exitAirType = "ExitAirTempSensor";
constexpr const char* cfmType = "CFMSensor";
// todo: this *might* need to be configurable
constexpr const char* inletTemperatureSensor = "temperature/Front_Panel_Temp";
constexpr const char* pidConfigurationType =
"xyz.openbmc_project.Configuration.Pid";
constexpr const char* settingsDaemon = "xyz.openbmc_project.Settings";
constexpr const char* cfmSettingPath = "/xyz/openbmc_project/control/cfm_limit";
constexpr const char* cfmSettingIface = "xyz.openbmc_project.Control.CFMLimit";
static constexpr bool debug = false;
static constexpr double cfmMaxReading = 255;
static constexpr double cfmMinReading = 0;
static constexpr size_t minSystemCfm = 50;
constexpr const auto monitorTypes{
std::to_array<const char*>({exitAirType, cfmType})};
static std::vector<std::shared_ptr<CFMSensor>> cfmSensors;
static void setupSensorMatch(
std::vector<sdbusplus::bus::match_t>& matches, sdbusplus::bus_t& connection,
const std::string& type,
std::function<void(const double&, sdbusplus::message_t&)>&& callback)
{
std::function<void(sdbusplus::message_t & message)> eventHandler =
[callback{std::move(callback)}](sdbusplus::message_t& message) {
std::string objectName;
boost::container::flat_map<std::string,
std::variant<double, int64_t>>
values;
message.read(objectName, values);
auto findValue = values.find("Value");
if (findValue == values.end())
{
return;
}
double value =
std::visit(VariantToDoubleVisitor(), findValue->second);
if (std::isnan(value))
{
return;
}
callback(value, message);
};
matches.emplace_back(
connection,
"type='signal',"
"member='PropertiesChanged',interface='org."
"freedesktop.DBus.Properties',path_"
"namespace='/xyz/openbmc_project/sensors/" +
std::string(type) + "',arg0='xyz.openbmc_project.Sensor.Value'",
std::move(eventHandler));
}
static void setMaxPWM(const std::shared_ptr<sdbusplus::asio::connection>& conn,
double value)
{
using GetSubTreeType = std::vector<std::pair<
std::string,
std::vector<std::pair<std::string, std::vector<std::string>>>>>;
conn->async_method_call(
[conn,
value](const boost::system::error_code ec, const GetSubTreeType& ret) {
if (ec)
{
std::cerr << "Error calling mapper\n";
return;
}
for (const auto& [path, objDict] : ret)
{
if (objDict.empty())
{
return;
}
const std::string& owner = objDict.begin()->first;
conn->async_method_call(
[conn, value, owner,
path{path}](const boost::system::error_code ec,
const std::variant<std::string>& classType) {
if (ec)
{
std::cerr << "Error getting pid class\n";
return;
}
const auto* classStr =
std::get_if<std::string>(&classType);
if (classStr == nullptr || *classStr != "fan")
{
return;
}
conn->async_method_call(
[](boost::system::error_code& ec) {
if (ec)
{
std::cerr << "Error setting pid class\n";
return;
}
},
owner, path, "org.freedesktop.DBus.Properties",
"Set", pidConfigurationType, "OutLimitMax",
std::variant<double>(value));
},
owner, path, "org.freedesktop.DBus.Properties", "Get",
pidConfigurationType, "Class");
}
},
mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
0, std::array<std::string, 1>{pidConfigurationType});
}
CFMSensor::CFMSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
const std::string& sensorName,
const std::string& sensorConfiguration,
sdbusplus::asio::object_server& objectServer,
std::vector<thresholds::Threshold>&& thresholdData,
std::shared_ptr<ExitAirTempSensor>& parent) :
Sensor(escapeName(sensorName), std::move(thresholdData),
sensorConfiguration, "CFMSensor", false, false, cfmMaxReading,
cfmMinReading, conn, PowerState::on),
parent(parent), objServer(objectServer)
{
sensorInterface = objectServer.add_interface(
"/xyz/openbmc_project/sensors/airflow/" + name,
"xyz.openbmc_project.Sensor.Value");
for (const auto& threshold : thresholds)
{
std::string interface = thresholds::getInterface(threshold.level);
thresholdInterfaces[static_cast<size_t>(threshold.level)] =
objectServer.add_interface(
"/xyz/openbmc_project/sensors/airflow/" + name, interface);
}
association = objectServer.add_interface(
"/xyz/openbmc_project/sensors/airflow/" + name, association::interface);
setInitialProperties(sensor_paths::unitCFM);
pwmLimitIface =
objectServer.add_interface("/xyz/openbmc_project/control/pwm_limit",
"xyz.openbmc_project.Control.PWMLimit");
cfmLimitIface =
objectServer.add_interface("/xyz/openbmc_project/control/MaxCFM",
"xyz.openbmc_project.Control.CFMLimit");
}
void CFMSensor::setupMatches()
{
std::weak_ptr<CFMSensor> weakRef = weak_from_this();
setupSensorMatch(
matches, *dbusConnection, "fan_tach",
[weakRef](const double& value, sdbusplus::message_t& message) {
auto self = weakRef.lock();
if (!self)
{
return;
}
self->tachReadings[message.get_path()] = value;
if (self->tachRanges.find(message.get_path()) ==
self->tachRanges.end())
{
// calls update reading after updating ranges
self->addTachRanges(message.get_sender(), message.get_path());
}
else
{
self->updateReading();
}
});
dbusConnection->async_method_call(
[weakRef](const boost::system::error_code ec,
const std::variant<double> cfmVariant) {
auto self = weakRef.lock();
if (!self)
{
return;
}
uint64_t maxRpm = 100;
if (!ec)
{
const auto* cfm = std::get_if<double>(&cfmVariant);
if (cfm != nullptr && *cfm >= minSystemCfm)
{
maxRpm = self->getMaxRpm(*cfm);
}
}
self->pwmLimitIface->register_property("Limit", maxRpm);
self->pwmLimitIface->initialize();
setMaxPWM(self->dbusConnection, maxRpm);
},
settingsDaemon, cfmSettingPath, "org.freedesktop.DBus.Properties",
"Get", cfmSettingIface, "Limit");
matches.emplace_back(
*dbusConnection,
"type='signal',"
"member='PropertiesChanged',interface='org."
"freedesktop.DBus.Properties',path='" +
std::string(cfmSettingPath) + "',arg0='" +
std::string(cfmSettingIface) + "'",
[weakRef](sdbusplus::message_t& message) {
auto self = weakRef.lock();
if (!self)
{
return;
}
boost::container::flat_map<std::string, std::variant<double>>
values;
std::string objectName;
message.read(objectName, values);
const auto findValue = values.find("Limit");
if (findValue == values.end())
{
return;
}
auto* const reading = std::get_if<double>(&(findValue->second));
if (reading == nullptr)
{
std::cerr << "Got CFM Limit of wrong type\n";
return;
}
if (*reading < minSystemCfm && *reading != 0)
{
std::cerr << "Illegal CFM setting detected\n";
return;
}
uint64_t maxRpm = self->getMaxRpm(*reading);
self->pwmLimitIface->set_property("Limit", maxRpm);
setMaxPWM(self->dbusConnection, maxRpm);
});
}
CFMSensor::~CFMSensor()
{
for (const auto& iface : thresholdInterfaces)
{
objServer.remove_interface(iface);
}
objServer.remove_interface(sensorInterface);
objServer.remove_interface(association);
objServer.remove_interface(cfmLimitIface);
objServer.remove_interface(pwmLimitIface);
}
void CFMSensor::createMaxCFMIface()
{
cfmLimitIface->register_property("Limit", c2 * maxCFM * tachs.size());
cfmLimitIface->initialize();
}
void CFMSensor::addTachRanges(const std::string& serviceName,
const std::string& path)
{
std::weak_ptr<CFMSensor> weakRef = weak_from_this();
dbusConnection->async_method_call(
[weakRef, path](const boost::system::error_code ec,
const SensorBaseConfigMap& data) {
if (ec)
{
std::cerr << "Error getting properties from " << path << "\n";
return;
}
auto self = weakRef.lock();
if (!self)
{
return;
}
double max = loadVariant<double>(data, "MaxValue");
double min = loadVariant<double>(data, "MinValue");
self->tachRanges[path] = std::make_pair(min, max);
self->updateReading();
},
serviceName, path, "org.freedesktop.DBus.Properties", "GetAll",
"xyz.openbmc_project.Sensor.Value");
}
void CFMSensor::checkThresholds()
{
thresholds::checkThresholds(this);
}
void CFMSensor::updateReading()
{
double val = 0.0;
if (calculate(val))
{
if (value != val && parent)
{
parent->updateReading();
}
updateValue(val);
}
else
{
updateValue(std::numeric_limits<double>::quiet_NaN());
}
}
uint64_t CFMSensor::getMaxRpm(uint64_t cfmMaxSetting) const
{
uint64_t pwmPercent = 100;
double totalCFM = std::numeric_limits<double>::max();
if (cfmMaxSetting == 0)
{
return pwmPercent;
}
bool firstLoop = true;
while (totalCFM > cfmMaxSetting)
{
if (firstLoop)
{
firstLoop = false;
}
else
{
pwmPercent--;
}
double ci = 0;
if (pwmPercent == 0)
{
ci = 0;
}
else if (pwmPercent < tachMinPercent)
{
ci = c1;
}
else if (pwmPercent > tachMaxPercent)
{
ci = c2;
}
else
{
ci = c1 + (((c2 - c1) * (pwmPercent - tachMinPercent)) /
(tachMaxPercent - tachMinPercent));
}
// Now calculate the CFM for this tach
// CFMi = Ci * Qmaxi * TACHi
totalCFM = ci * maxCFM * pwmPercent;
totalCFM *= tachs.size();
// divide by 100 since pwm is in percent
totalCFM /= 100;
if (pwmPercent <= 0)
{
break;
}
}
return pwmPercent;
}
bool CFMSensor::calculate(double& value)
{
double totalCFM = 0;
for (const std::string& tachName : tachs)
{
auto findReading = std::find_if(
tachReadings.begin(), tachReadings.end(),
[&](const auto& item) { return item.first.ends_with(tachName); });
auto findRange = std::find_if(
tachRanges.begin(), tachRanges.end(),
[&](const auto& item) { return item.first.ends_with(tachName); });
if (findReading == tachReadings.end())
{
if constexpr (debug)
{
std::cerr << "Can't find " << tachName << "in readings\n";
}
continue; // haven't gotten a reading
}
if (findRange == tachRanges.end())
{
std::cerr << "Can't find " << tachName << " in ranges\n";
return false; // haven't gotten a max / min
}
// avoid divide by 0
if (findRange->second.second == 0)
{
std::cerr << "Tach Max Set to 0 " << tachName << "\n";
return false;
}
double rpm = findReading->second;
// for now assume the min for a fan is always 0, divide by max to get
// percent and mult by 100
rpm /= findRange->second.second;
rpm *= 100;
if constexpr (debug)
{
std::cout << "Tach " << tachName << "at " << rpm << "\n";
}
// Do a linear interpolation to get Ci
// Ci = C1 + (C2 - C1)/(RPM2 - RPM1) * (TACHi - TACH1)
double ci = 0;
if (rpm == 0)
{
ci = 0;
}
else if (rpm < tachMinPercent)
{
ci = c1;
}
else if (rpm > tachMaxPercent)
{
ci = c2;
}
else
{
ci = c1 + (((c2 - c1) * (rpm - tachMinPercent)) /
(tachMaxPercent - tachMinPercent));
}
// Now calculate the CFM for this tach
// CFMi = Ci * Qmaxi * TACHi
totalCFM += ci * maxCFM * rpm;
if constexpr (debug)
{
std::cerr << "totalCFM = " << totalCFM << "\n";
std::cerr << "Ci " << ci << " MaxCFM " << maxCFM << " rpm " << rpm
<< "\n";
std::cerr << "c1 " << c1 << " c2 " << c2 << " max "
<< tachMaxPercent << " min " << tachMinPercent << "\n";
}
}
// divide by 100 since rpm is in percent
value = totalCFM / 100;
if constexpr (debug)
{
std::cerr << "cfm value = " << value << "\n";
}
return true;
}
static constexpr double exitAirMaxReading = 127;
static constexpr double exitAirMinReading = -128;
ExitAirTempSensor::ExitAirTempSensor(
std::shared_ptr<sdbusplus::asio::connection>& conn,
const std::string& sensorName, const std::string& sensorConfiguration,
sdbusplus::asio::object_server& objectServer,
std::vector<thresholds::Threshold>&& thresholdData) :
Sensor(escapeName(sensorName), std::move(thresholdData),
sensorConfiguration, "ExitAirTemp", false, false, exitAirMaxReading,
exitAirMinReading, conn, PowerState::on),
objServer(objectServer)
{
sensorInterface = objectServer.add_interface(
"/xyz/openbmc_project/sensors/temperature/" + name,
"xyz.openbmc_project.Sensor.Value");
for (const auto& threshold : thresholds)
{
std::string interface = thresholds::getInterface(threshold.level);
thresholdInterfaces[static_cast<size_t>(threshold.level)] =
objectServer.add_interface(
"/xyz/openbmc_project/sensors/temperature/" + name, interface);
}
association = objectServer.add_interface(
"/xyz/openbmc_project/sensors/temperature/" + name,
association::interface);
setInitialProperties(sensor_paths::unitDegreesC);
}
ExitAirTempSensor::~ExitAirTempSensor()
{
for (const auto& iface : thresholdInterfaces)
{
objServer.remove_interface(iface);
}
objServer.remove_interface(sensorInterface);
objServer.remove_interface(association);
}
void ExitAirTempSensor::setupMatches()
{
constexpr const auto matchTypes{
std::to_array<const char*>({"power", inletTemperatureSensor})};
std::weak_ptr<ExitAirTempSensor> weakRef = weak_from_this();
for (const std::string type : matchTypes)
{
setupSensorMatch(
matches, *dbusConnection, type,
[weakRef,
type](const double& value, sdbusplus::message_t& message) {
auto self = weakRef.lock();
if (!self)
{
return;
}
if (type == "power")
{
std::string path = message.get_path();
if (path.find("PS") != std::string::npos &&
path.ends_with("Input_Power"))
{
self->powerReadings[message.get_path()] = value;
}
}
else if (type == inletTemperatureSensor)
{
self->inletTemp = value;
}
self->updateReading();
});
}
dbusConnection->async_method_call(
[weakRef](boost::system::error_code ec,
const std::variant<double>& value) {
if (ec)
{
// sensor not ready yet
return;
}
auto self = weakRef.lock();
if (!self)
{
return;
}
self->inletTemp = std::visit(VariantToDoubleVisitor(), value);
},
"xyz.openbmc_project.HwmonTempSensor",
std::string("/xyz/openbmc_project/sensors/") + inletTemperatureSensor,
properties::interface, properties::get, sensorValueInterface, "Value");
dbusConnection->async_method_call(
[weakRef](boost::system::error_code ec, const GetSubTreeType& subtree) {
if (ec)
{
std::cerr << "Error contacting mapper\n";
return;
}
auto self = weakRef.lock();
if (!self)
{
return;
}
for (const auto& [path, matches] : subtree)
{
size_t lastSlash = path.rfind('/');
if (lastSlash == std::string::npos ||
lastSlash == path.size() || matches.empty())
{
continue;
}
std::string sensorName = path.substr(lastSlash + 1);
if (sensorName.starts_with("PS") &&
sensorName.ends_with("Input_Power"))
{
// lambda capture requires a proper variable (not a
// structured binding)
const std::string& cbPath = path;
self->dbusConnection->async_method_call(
[weakRef, cbPath](boost::system::error_code ec,
const std::variant<double>& value) {
if (ec)
{
std::cerr << "Error getting value from "
<< cbPath << "\n";
}
auto self = weakRef.lock();
if (!self)
{
return;
}
double reading =
std::visit(VariantToDoubleVisitor(), value);
if constexpr (debug)
{
std::cerr
<< cbPath << "Reading " << reading << "\n";
}
self->powerReadings[cbPath] = reading;
},
matches[0].first, cbPath, properties::interface,
properties::get, sensorValueInterface, "Value");
}
}
},
mapper::busName, mapper::path, mapper::interface, mapper::subtree,
"/xyz/openbmc_project/sensors/power", 0,
std::array<const char*, 1>{sensorValueInterface});
}
void ExitAirTempSensor::updateReading()
{
double val = 0.0;
if (calculate(val))
{
val = std::floor(val + 0.5);
updateValue(val);
}
else
{
updateValue(std::numeric_limits<double>::quiet_NaN());
}
}
double ExitAirTempSensor::getTotalCFM()
{
double sum = 0;
for (auto& sensor : cfmSensors)
{
double reading = 0;
if (!sensor->calculate(reading))
{
return -1;
}
sum += reading;
}
return sum;
}
bool ExitAirTempSensor::calculate(double& val)
{
constexpr size_t maxErrorPrint = 5;
static bool firstRead = false;
static size_t errorPrint = maxErrorPrint;
double cfm = getTotalCFM();
if (cfm <= 0)
{
std::cerr << "Error getting cfm\n";
return false;
}
// Though cfm is not expected to be less than qMin normally,
// it is not a hard limit for exit air temp calculation.
// 50% qMin is chosen as a generic limit between providing
// a valid derived exit air temp and reporting exit air temp not available.
constexpr const double cfmLimitFactor = 0.5;
if (cfm < (qMin * cfmLimitFactor))
{
if (errorPrint > 0)
{
errorPrint--;
std::cerr << "cfm " << cfm << " is too low, expected qMin " << qMin
<< "\n";
}
val = 0;
return false;
}
// if there is an error getting inlet temp, return error
if (std::isnan(inletTemp))
{
if (errorPrint > 0)
{
errorPrint--;
std::cerr << "Cannot get inlet temp\n";
}
val = 0;
return false;
}
// if fans are off, just make the exit temp equal to inlet
if (!isPowerOn())
{
val = inletTemp;
return true;
}
double totalPower = 0;
for (const auto& [path, reading] : powerReadings)
{
if (std::isnan(reading))
{
continue;
}
totalPower += reading;
}
// Calculate power correction factor
// Ci = CL + (CH - CL)/(QMax - QMin) * (CFM - QMin)
double powerFactor = 0.0;
if (cfm <= qMin)
{
powerFactor = powerFactorMin;
}
else if (cfm >= qMax)
{
powerFactor = powerFactorMax;
}
else
{
powerFactor = powerFactorMin + ((powerFactorMax - powerFactorMin) /
(qMax - qMin) * (cfm - qMin));
}
totalPower *= powerFactor;
totalPower += pOffset;
if (totalPower == 0)
{
if (errorPrint > 0)
{
errorPrint--;
std::cerr << "total power 0\n";
}
val = 0;
return false;
}
if constexpr (debug)
{
std::cout << "Power Factor " << powerFactor << "\n";
std::cout << "Inlet Temp " << inletTemp << "\n";
std::cout << "Total Power" << totalPower << "\n";
}
// Calculate the exit air temp
// Texit = Tfp + (1.76 * TotalPower / CFM * Faltitude)
double reading = 1.76 * totalPower * altitudeFactor;
reading /= cfm;
reading += inletTemp;
if constexpr (debug)
{
std::cout << "Reading 1: " << reading << "\n";
}
// Now perform the exponential average
// Calculate alpha based on SDR values and CFM
// Ai = As + (Af - As)/(QMax - QMin) * (CFM - QMin)
double alpha = 0.0;
if (cfm < qMin)
{
alpha = alphaS;
}
else if (cfm >= qMax)
{
alpha = alphaF;
}
else
{
alpha = alphaS + ((alphaF - alphaS) * (cfm - qMin) / (qMax - qMin));
}
auto time = std::chrono::steady_clock::now();
if (!firstRead)
{
firstRead = true;
lastTime = time;
lastReading = reading;
}
double alphaDT =
std::chrono::duration_cast<std::chrono::seconds>(time - lastTime)
.count() *
alpha;
// cap at 1.0 or the below fails
if (alphaDT > 1.0)
{
alphaDT = 1.0;
}
if constexpr (debug)
{
std::cout << "AlphaDT: " << alphaDT << "\n";
}
reading = ((reading * alphaDT) + (lastReading * (1.0 - alphaDT)));
if constexpr (debug)
{
std::cout << "Reading 2: " << reading << "\n";
}
val = reading;
lastReading = reading;
lastTime = time;
errorPrint = maxErrorPrint;
return true;
}
void ExitAirTempSensor::checkThresholds()
{
thresholds::checkThresholds(this);
}
static void loadVariantPathArray(const SensorBaseConfigMap& data,
const std::string& key,
std::vector<std::string>& resp)
{
auto it = data.find(key);
if (it == data.end())
{
std::cerr << "Configuration missing " << key << "\n";
throw std::invalid_argument("Key Missing");
}
BasicVariantType copy = it->second;
std::vector<std::string> config = std::get<std::vector<std::string>>(copy);
for (auto& str : config)
{
boost::replace_all(str, " ", "_");
}
resp = std::move(config);
}
void createSensor(sdbusplus::asio::object_server& objectServer,
std::shared_ptr<ExitAirTempSensor>& exitAirSensor,
std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
{
if (!dbusConnection)
{
std::cerr << "Connection not created\n";
return;
}
auto getter = std::make_shared<GetSensorConfiguration>(
dbusConnection, [&objectServer, &dbusConnection,
&exitAirSensor](const ManagedObjectType& resp) {
cfmSensors.clear();
for (const auto& [path, interfaces] : resp)
{
for (const auto& [intf, cfg] : interfaces)
{
if (intf == configInterfaceName(exitAirType))
{
// thresholds should be under the same path
std::vector<thresholds::Threshold> sensorThresholds;
parseThresholdsFromConfig(interfaces, sensorThresholds);
std::string name =
loadVariant<std::string>(cfg, "Name");
exitAirSensor = nullptr;
exitAirSensor = std::make_shared<ExitAirTempSensor>(
dbusConnection, name, path.str, objectServer,
std::move(sensorThresholds));
exitAirSensor->powerFactorMin =
loadVariant<double>(cfg, "PowerFactorMin");
exitAirSensor->powerFactorMax =
loadVariant<double>(cfg, "PowerFactorMax");
exitAirSensor->qMin = loadVariant<double>(cfg, "QMin");
exitAirSensor->qMax = loadVariant<double>(cfg, "QMax");
exitAirSensor->alphaS =
loadVariant<double>(cfg, "AlphaS");
exitAirSensor->alphaF =
loadVariant<double>(cfg, "AlphaF");
}
else if (intf == configInterfaceName(cfmType))
{
// thresholds should be under the same path
std::vector<thresholds::Threshold> sensorThresholds;
parseThresholdsFromConfig(interfaces, sensorThresholds);
std::string name =
loadVariant<std::string>(cfg, "Name");
auto sensor = std::make_shared<CFMSensor>(
dbusConnection, name, path.str, objectServer,
std::move(sensorThresholds), exitAirSensor);
loadVariantPathArray(cfg, "Tachs", sensor->tachs);
sensor->maxCFM = loadVariant<double>(cfg, "MaxCFM");
// change these into percent upon getting the data
sensor->c1 = loadVariant<double>(cfg, "C1") / 100;
sensor->c2 = loadVariant<double>(cfg, "C2") / 100;
sensor->tachMinPercent =
loadVariant<double>(cfg, "TachMinPercent");
sensor->tachMaxPercent =
loadVariant<double>(cfg, "TachMaxPercent");
sensor->createMaxCFMIface();
sensor->setupMatches();
cfmSensors.emplace_back(std::move(sensor));
}
}
}
if (exitAirSensor)
{
exitAirSensor->setupMatches();
exitAirSensor->updateReading();
}
});
getter->getConfiguration(
std::vector<std::string>(monitorTypes.begin(), monitorTypes.end()));
}
int main()
{
boost::asio::io_context io;
auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
sdbusplus::asio::object_server objectServer(systemBus, true);
objectServer.add_manager("/xyz/openbmc_project/sensors");
systemBus->request_name("xyz.openbmc_project.ExitAirTempSensor");
std::shared_ptr<ExitAirTempSensor> sensor =
nullptr; // wait until we find the config
boost::asio::post(io,
[&]() { createSensor(objectServer, sensor, systemBus); });
boost::asio::steady_timer configTimer(io);
std::function<void(sdbusplus::message_t&)> eventHandler =
[&](sdbusplus::message_t&) {
configTimer.expires_after(std::chrono::seconds(1));
// create a timer because normally multiple properties change
configTimer.async_wait([&](const boost::system::error_code& ec) {
if (ec == boost::asio::error::operation_aborted)
{
return; // we're being canceled
}
createSensor(objectServer, sensor, systemBus);
if (!sensor)
{
std::cout << "Configuration not detected\n";
}
});
};
std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
setupPropertiesChangedMatches(*systemBus, monitorTypes, eventHandler);
setupManufacturingModeMatch(*systemBus);
io.run();
return 0;
}