blob: d4220dd08ad33d3705d2199f3f82f651927314ca [file] [log] [blame]
James Feistbc896df2018-11-26 16:28:17 -08001/*
2// Copyright (c) 2018 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
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103017#include "ExitAirTempSensor.hpp"
18
Ed Tanouseacbfdd2024-04-04 12:00:24 -070019#include "SensorPaths.hpp"
20#include "Thresholds.hpp"
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103021#include "Utils.hpp"
22#include "VariantVisitors.hpp"
Ed Tanouseacbfdd2024-04-04 12:00:24 -070023#include "sensor.hpp"
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103024
James Feistbc896df2018-11-26 16:28:17 -080025#include <boost/algorithm/string/replace.hpp>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070026#include <boost/asio/error.hpp>
27#include <boost/asio/io_context.hpp>
28#include <boost/asio/post.hpp>
29#include <boost/asio/steady_timer.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070030#include <boost/container/flat_map.hpp>
George Liuce1abb62025-02-20 10:44:47 +080031#include <phosphor-logging/lg2.hpp>
James Feist38fb5982020-05-28 10:09:54 -070032#include <sdbusplus/asio/connection.hpp>
33#include <sdbusplus/asio/object_server.hpp>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070034#include <sdbusplus/bus.hpp>
James Feist38fb5982020-05-28 10:09:54 -070035#include <sdbusplus/bus/match.hpp>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070036#include <sdbusplus/message.hpp>
James Feist38fb5982020-05-28 10:09:54 -070037
Ed Tanouseacbfdd2024-04-04 12:00:24 -070038#include <algorithm>
James Feist38fb5982020-05-28 10:09:54 -070039#include <array>
James Feistbc896df2018-11-26 16:28:17 -080040#include <chrono>
Patrick Venture96e97db2019-10-31 13:44:38 -070041#include <cmath>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070042#include <cstddef>
43#include <cstdint>
Patrick Venture96e97db2019-10-31 13:44:38 -070044#include <functional>
James Feistbc896df2018-11-26 16:28:17 -080045#include <limits>
Patrick Venture96e97db2019-10-31 13:44:38 -070046#include <memory>
Patrick Venture96e97db2019-10-31 13:44:38 -070047#include <stdexcept>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070048#include <string>
Patrick Venture96e97db2019-10-31 13:44:38 -070049#include <utility>
50#include <variant>
James Feistbc896df2018-11-26 16:28:17 -080051#include <vector>
52
Ed Tanous8a57ec02020-10-09 12:46:52 -070053constexpr const double altitudeFactor = 1.14;
Zev Weiss054aad82022-08-18 01:37:34 -070054constexpr const char* exitAirType = "ExitAirTempSensor";
55constexpr const char* cfmType = "CFMSensor";
James Feistbc896df2018-11-26 16:28:17 -080056
57// todo: this *might* need to be configurable
58constexpr const char* inletTemperatureSensor = "temperature/Front_Panel_Temp";
James Feist13452092019-03-07 16:38:12 -080059constexpr const char* pidConfigurationType =
60 "xyz.openbmc_project.Configuration.Pid";
61constexpr const char* settingsDaemon = "xyz.openbmc_project.Settings";
62constexpr const char* cfmSettingPath = "/xyz/openbmc_project/control/cfm_limit";
63constexpr const char* cfmSettingIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistbc896df2018-11-26 16:28:17 -080064
Ed Tanous8a57ec02020-10-09 12:46:52 -070065static constexpr bool debug = false;
James Feistbc896df2018-11-26 16:28:17 -080066
James Feistb2eb3f52018-12-04 16:17:50 -080067static constexpr double cfmMaxReading = 255;
68static constexpr double cfmMinReading = 0;
69
James Feist13452092019-03-07 16:38:12 -080070static constexpr size_t minSystemCfm = 50;
71
Zev Weiss054aad82022-08-18 01:37:34 -070072constexpr const auto monitorTypes{
73 std::to_array<const char*>({exitAirType, cfmType})};
James Feist655f3762020-10-05 15:28:15 -070074
James Feist9a25ed42019-10-15 15:43:44 -070075static std::vector<std::shared_ptr<CFMSensor>> cfmSensors;
76
James Feistb2eb3f52018-12-04 16:17:50 -080077static void setupSensorMatch(
Patrick Williams92f8f512022-07-22 19:26:55 -050078 std::vector<sdbusplus::bus::match_t>& matches, sdbusplus::bus_t& connection,
79 const std::string& type,
80 std::function<void(const double&, sdbusplus::message_t&)>&& callback)
James Feistb2eb3f52018-12-04 16:17:50 -080081{
Patrick Williams92f8f512022-07-22 19:26:55 -050082 std::function<void(sdbusplus::message_t & message)> eventHandler =
83 [callback{std::move(callback)}](sdbusplus::message_t& message) {
Patrick Williams2aaf7172024-08-16 15:20:40 -040084 std::string objectName;
85 boost::container::flat_map<std::string,
86 std::variant<double, int64_t>>
87 values;
88 message.read(objectName, values);
89 auto findValue = values.find("Value");
90 if (findValue == values.end())
91 {
92 return;
93 }
94 double value =
95 std::visit(VariantToDoubleVisitor(), findValue->second);
96 if (std::isnan(value))
97 {
98 return;
99 }
James Feist9566bfa2019-01-29 15:31:23 -0800100
Patrick Williams2aaf7172024-08-16 15:20:40 -0400101 callback(value, message);
102 };
103 matches.emplace_back(
104 connection,
105 "type='signal',"
106 "member='PropertiesChanged',interface='org."
107 "freedesktop.DBus.Properties',path_"
108 "namespace='/xyz/openbmc_project/sensors/" +
109 std::string(type) + "',arg0='xyz.openbmc_project.Sensor.Value'",
110 std::move(eventHandler));
James Feistb2eb3f52018-12-04 16:17:50 -0800111}
112
James Feist13452092019-03-07 16:38:12 -0800113static void setMaxPWM(const std::shared_ptr<sdbusplus::asio::connection>& conn,
114 double value)
115{
116 using GetSubTreeType = std::vector<std::pair<
117 std::string,
118 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
119
120 conn->async_method_call(
Patrick Williams2aaf7172024-08-16 15:20:40 -0400121 [conn,
122 value](const boost::system::error_code ec, const GetSubTreeType& ret) {
123 if (ec)
James Feist13452092019-03-07 16:38:12 -0800124 {
George Liuce1abb62025-02-20 10:44:47 +0800125 lg2::error("Error calling mapper");
James Feist13452092019-03-07 16:38:12 -0800126 return;
127 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400128 for (const auto& [path, objDict] : ret)
129 {
130 if (objDict.empty())
131 {
132 return;
133 }
134 const std::string& owner = objDict.begin()->first;
Ed Tanousbb679322022-05-16 16:10:00 -0700135
James Feist13452092019-03-07 16:38:12 -0800136 conn->async_method_call(
Patrick Williams2aaf7172024-08-16 15:20:40 -0400137 [conn, value, owner,
138 path{path}](const boost::system::error_code ec,
139 const std::variant<std::string>& classType) {
140 if (ec)
141 {
George Liuce1abb62025-02-20 10:44:47 +0800142 lg2::error("Error getting pid class");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400143 return;
144 }
145 const auto* classStr =
146 std::get_if<std::string>(&classType);
147 if (classStr == nullptr || *classStr != "fan")
148 {
149 return;
150 }
151 conn->async_method_call(
152 [](boost::system::error_code& ec) {
153 if (ec)
154 {
George Liuce1abb62025-02-20 10:44:47 +0800155 lg2::error("Error setting pid class");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400156 return;
157 }
158 },
159 owner, path, "org.freedesktop.DBus.Properties",
160 "Set", pidConfigurationType, "OutLimitMax",
161 std::variant<double>(value));
162 },
163 owner, path, "org.freedesktop.DBus.Properties", "Get",
164 pidConfigurationType, "Class");
165 }
166 },
James Feista5e58722019-04-22 14:43:11 -0700167 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
168 0, std::array<std::string, 1>{pidConfigurationType});
James Feist13452092019-03-07 16:38:12 -0800169}
170
James Feistb2eb3f52018-12-04 16:17:50 -0800171CFMSensor::CFMSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
172 const std::string& sensorName,
173 const std::string& sensorConfiguration,
174 sdbusplus::asio::object_server& objectServer,
James Feistb839c052019-05-15 10:25:24 -0700175 std::vector<thresholds::Threshold>&& thresholdData,
James Feistb2eb3f52018-12-04 16:17:50 -0800176 std::shared_ptr<ExitAirTempSensor>& parent) :
Zhikui Renda98f092021-11-01 09:41:08 -0700177 Sensor(escapeName(sensorName), std::move(thresholdData),
Zev Weiss054aad82022-08-18 01:37:34 -0700178 sensorConfiguration, "CFMSensor", false, false, cfmMaxReading,
179 cfmMinReading, conn, PowerState::on),
Ed Tanous2049bd22022-07-09 07:20:26 -0700180 parent(parent), objServer(objectServer)
James Feistb2eb3f52018-12-04 16:17:50 -0800181{
Basheer Ahmed Muddebihale5b867b2021-07-26 08:32:19 -0700182 sensorInterface = objectServer.add_interface(
183 "/xyz/openbmc_project/sensors/airflow/" + name,
184 "xyz.openbmc_project.Sensor.Value");
James Feistb2eb3f52018-12-04 16:17:50 -0800185
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530186 for (const auto& threshold : thresholds)
James Feistb2eb3f52018-12-04 16:17:50 -0800187 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530188 std::string interface = thresholds::getInterface(threshold.level);
189 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
190 objectServer.add_interface(
191 "/xyz/openbmc_project/sensors/airflow/" + name, interface);
James Feistb2eb3f52018-12-04 16:17:50 -0800192 }
James Feist078f2322019-03-08 11:09:05 -0800193
194 association = objectServer.add_interface(
Basheer Ahmed Muddebihale5b867b2021-07-26 08:32:19 -0700195 "/xyz/openbmc_project/sensors/airflow/" + name, association::interface);
James Feist078f2322019-03-08 11:09:05 -0800196
Andrei Kartashev39287412022-02-04 16:04:47 +0300197 setInitialProperties(sensor_paths::unitCFM);
James Feist9a25ed42019-10-15 15:43:44 -0700198
James Feist13452092019-03-07 16:38:12 -0800199 pwmLimitIface =
200 objectServer.add_interface("/xyz/openbmc_project/control/pwm_limit",
201 "xyz.openbmc_project.Control.PWMLimit");
202 cfmLimitIface =
203 objectServer.add_interface("/xyz/openbmc_project/control/MaxCFM",
204 "xyz.openbmc_project.Control.CFMLimit");
James Feist9a25ed42019-10-15 15:43:44 -0700205}
James Feist13452092019-03-07 16:38:12 -0800206
James Feist9a25ed42019-10-15 15:43:44 -0700207void CFMSensor::setupMatches()
208{
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700209 std::weak_ptr<CFMSensor> weakRef = weak_from_this();
Ed Tanous8a17c302021-09-02 15:07:11 -0700210 setupSensorMatch(
211 matches, *dbusConnection, "fan_tach",
Patrick Williams92f8f512022-07-22 19:26:55 -0500212 [weakRef](const double& value, sdbusplus::message_t& message) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400213 auto self = weakRef.lock();
214 if (!self)
215 {
216 return;
217 }
218 self->tachReadings[message.get_path()] = value;
219 if (self->tachRanges.find(message.get_path()) ==
220 self->tachRanges.end())
221 {
222 // calls update reading after updating ranges
223 self->addTachRanges(message.get_sender(), message.get_path());
224 }
225 else
226 {
227 self->updateReading();
228 }
229 });
James Feist9a25ed42019-10-15 15:43:44 -0700230
231 dbusConnection->async_method_call(
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700232 [weakRef](const boost::system::error_code ec,
233 const std::variant<double> cfmVariant) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400234 auto self = weakRef.lock();
235 if (!self)
Ed Tanousbb679322022-05-16 16:10:00 -0700236 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400237 return;
James Feist13452092019-03-07 16:38:12 -0800238 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400239
240 uint64_t maxRpm = 100;
241 if (!ec)
242 {
243 const auto* cfm = std::get_if<double>(&cfmVariant);
244 if (cfm != nullptr && *cfm >= minSystemCfm)
245 {
246 maxRpm = self->getMaxRpm(*cfm);
247 }
248 }
249 self->pwmLimitIface->register_property("Limit", maxRpm);
250 self->pwmLimitIface->initialize();
251 setMaxPWM(self->dbusConnection, maxRpm);
252 },
James Feist13452092019-03-07 16:38:12 -0800253 settingsDaemon, cfmSettingPath, "org.freedesktop.DBus.Properties",
254 "Get", cfmSettingIface, "Limit");
255
Patrick Williams2aaf7172024-08-16 15:20:40 -0400256 matches.emplace_back(
257 *dbusConnection,
258 "type='signal',"
259 "member='PropertiesChanged',interface='org."
260 "freedesktop.DBus.Properties',path='" +
261 std::string(cfmSettingPath) + "',arg0='" +
262 std::string(cfmSettingIface) + "'",
263 [weakRef](sdbusplus::message_t& message) {
264 auto self = weakRef.lock();
265 if (!self)
266 {
267 return;
268 }
269 boost::container::flat_map<std::string, std::variant<double>>
270 values;
271 std::string objectName;
272 message.read(objectName, values);
273 const auto findValue = values.find("Limit");
274 if (findValue == values.end())
275 {
276 return;
277 }
278 auto* const reading = std::get_if<double>(&(findValue->second));
279 if (reading == nullptr)
280 {
George Liuce1abb62025-02-20 10:44:47 +0800281 lg2::error("Got CFM Limit of wrong type");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400282 return;
283 }
284 if (*reading < minSystemCfm && *reading != 0)
285 {
George Liuce1abb62025-02-20 10:44:47 +0800286 lg2::error("Illegal CFM setting detected");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400287 return;
288 }
289 uint64_t maxRpm = self->getMaxRpm(*reading);
290 self->pwmLimitIface->set_property("Limit", maxRpm);
291 setMaxPWM(self->dbusConnection, maxRpm);
292 });
James Feistb2eb3f52018-12-04 16:17:50 -0800293}
294
James Feist9566bfa2019-01-29 15:31:23 -0800295CFMSensor::~CFMSensor()
296{
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530297 for (const auto& iface : thresholdInterfaces)
298 {
299 objServer.remove_interface(iface);
300 }
James Feist9566bfa2019-01-29 15:31:23 -0800301 objServer.remove_interface(sensorInterface);
James Feist078f2322019-03-08 11:09:05 -0800302 objServer.remove_interface(association);
James Feist13452092019-03-07 16:38:12 -0800303 objServer.remove_interface(cfmLimitIface);
304 objServer.remove_interface(pwmLimitIface);
305}
306
Ed Tanous201a1012024-04-03 18:07:28 -0700307void CFMSensor::createMaxCFMIface()
James Feist13452092019-03-07 16:38:12 -0800308{
James Feistb6c0b912019-07-09 12:21:44 -0700309 cfmLimitIface->register_property("Limit", c2 * maxCFM * tachs.size());
James Feist13452092019-03-07 16:38:12 -0800310 cfmLimitIface->initialize();
James Feist9566bfa2019-01-29 15:31:23 -0800311}
312
James Feistb2eb3f52018-12-04 16:17:50 -0800313void CFMSensor::addTachRanges(const std::string& serviceName,
314 const std::string& path)
315{
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700316 std::weak_ptr<CFMSensor> weakRef = weak_from_this();
James Feistb2eb3f52018-12-04 16:17:50 -0800317 dbusConnection->async_method_call(
Zev Weissafd15042022-07-18 12:28:40 -0700318 [weakRef, path](const boost::system::error_code ec,
319 const SensorBaseConfigMap& data) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400320 if (ec)
321 {
George Liuce1abb62025-02-20 10:44:47 +0800322 lg2::error("Error getting properties from '{PATH}'", "PATH",
323 path);
Patrick Williams2aaf7172024-08-16 15:20:40 -0400324 return;
325 }
326 auto self = weakRef.lock();
327 if (!self)
328 {
329 return;
330 }
331 double max = loadVariant<double>(data, "MaxValue");
332 double min = loadVariant<double>(data, "MinValue");
333 self->tachRanges[path] = std::make_pair(min, max);
334 self->updateReading();
335 },
James Feistb2eb3f52018-12-04 16:17:50 -0800336 serviceName, path, "org.freedesktop.DBus.Properties", "GetAll",
337 "xyz.openbmc_project.Sensor.Value");
338}
339
Ed Tanous201a1012024-04-03 18:07:28 -0700340void CFMSensor::checkThresholds()
James Feistb2eb3f52018-12-04 16:17:50 -0800341{
342 thresholds::checkThresholds(this);
343}
344
Ed Tanous201a1012024-04-03 18:07:28 -0700345void CFMSensor::updateReading()
James Feistb2eb3f52018-12-04 16:17:50 -0800346{
347 double val = 0.0;
348 if (calculate(val))
349 {
350 if (value != val && parent)
351 {
352 parent->updateReading();
353 }
354 updateValue(val);
355 }
356 else
357 {
358 updateValue(std::numeric_limits<double>::quiet_NaN());
359 }
360}
361
Ed Tanous2049bd22022-07-09 07:20:26 -0700362uint64_t CFMSensor::getMaxRpm(uint64_t cfmMaxSetting) const
James Feist13452092019-03-07 16:38:12 -0800363{
364 uint64_t pwmPercent = 100;
365 double totalCFM = std::numeric_limits<double>::max();
366 if (cfmMaxSetting == 0)
367 {
368 return pwmPercent;
369 }
370
James Feist52427952019-04-05 14:23:35 -0700371 bool firstLoop = true;
James Feist13452092019-03-07 16:38:12 -0800372 while (totalCFM > cfmMaxSetting)
373 {
James Feist52427952019-04-05 14:23:35 -0700374 if (firstLoop)
375 {
376 firstLoop = false;
377 }
378 else
379 {
380 pwmPercent--;
381 }
382
James Feist13452092019-03-07 16:38:12 -0800383 double ci = 0;
384 if (pwmPercent == 0)
385 {
386 ci = 0;
387 }
388 else if (pwmPercent < tachMinPercent)
389 {
390 ci = c1;
391 }
392 else if (pwmPercent > tachMaxPercent)
393 {
394 ci = c2;
395 }
396 else
397 {
398 ci = c1 + (((c2 - c1) * (pwmPercent - tachMinPercent)) /
399 (tachMaxPercent - tachMinPercent));
400 }
401
402 // Now calculate the CFM for this tach
403 // CFMi = Ci * Qmaxi * TACHi
404 totalCFM = ci * maxCFM * pwmPercent;
405 totalCFM *= tachs.size();
406 // divide by 100 since pwm is in percent
407 totalCFM /= 100;
408
James Feist13452092019-03-07 16:38:12 -0800409 if (pwmPercent <= 0)
410 {
411 break;
412 }
413 }
James Feist52427952019-04-05 14:23:35 -0700414
James Feist13452092019-03-07 16:38:12 -0800415 return pwmPercent;
416}
417
James Feistb2eb3f52018-12-04 16:17:50 -0800418bool CFMSensor::calculate(double& value)
419{
420 double totalCFM = 0;
421 for (const std::string& tachName : tachs)
422 {
423 auto findReading = std::find_if(
Zev Weiss6c106d62022-08-17 20:50:00 -0700424 tachReadings.begin(), tachReadings.end(),
425 [&](const auto& item) { return item.first.ends_with(tachName); });
Patrick Williams597e8422023-10-20 11:19:01 -0500426 auto findRange = std::find_if(
427 tachRanges.begin(), tachRanges.end(),
428 [&](const auto& item) { return item.first.ends_with(tachName); });
James Feistb2eb3f52018-12-04 16:17:50 -0800429 if (findReading == tachReadings.end())
430 {
Ed Tanous8a57ec02020-10-09 12:46:52 -0700431 if constexpr (debug)
James Feista96329f2019-01-24 10:08:27 -0800432 {
George Liuce1abb62025-02-20 10:44:47 +0800433 lg2::error("Can't find '{NAME}' in readings", "NAME", tachName);
James Feista96329f2019-01-24 10:08:27 -0800434 }
James Feist9566bfa2019-01-29 15:31:23 -0800435 continue; // haven't gotten a reading
James Feistb2eb3f52018-12-04 16:17:50 -0800436 }
437
438 if (findRange == tachRanges.end())
439 {
George Liuce1abb62025-02-20 10:44:47 +0800440 lg2::error("Can't find '{NAME}' in ranges", "NAME", tachName);
James Feistb2eb3f52018-12-04 16:17:50 -0800441 return false; // haven't gotten a max / min
442 }
443
444 // avoid divide by 0
445 if (findRange->second.second == 0)
446 {
George Liuce1abb62025-02-20 10:44:47 +0800447 lg2::error("Tach Max Set to 0, tachName: '{NAME}'", "NAME",
448 tachName);
James Feistb2eb3f52018-12-04 16:17:50 -0800449 return false;
450 }
451
452 double rpm = findReading->second;
453
454 // for now assume the min for a fan is always 0, divide by max to get
455 // percent and mult by 100
456 rpm /= findRange->second.second;
457 rpm *= 100;
458
Ed Tanous8a57ec02020-10-09 12:46:52 -0700459 if constexpr (debug)
James Feistb2eb3f52018-12-04 16:17:50 -0800460 {
George Liuce1abb62025-02-20 10:44:47 +0800461 lg2::info("Tach '{NAME}' at '{RPM}'", "NAME", tachName, "RPM", rpm);
James Feistb2eb3f52018-12-04 16:17:50 -0800462 }
463
464 // Do a linear interpolation to get Ci
465 // Ci = C1 + (C2 - C1)/(RPM2 - RPM1) * (TACHi - TACH1)
466
467 double ci = 0;
468 if (rpm == 0)
469 {
470 ci = 0;
471 }
472 else if (rpm < tachMinPercent)
473 {
474 ci = c1;
475 }
476 else if (rpm > tachMaxPercent)
477 {
478 ci = c2;
479 }
480 else
481 {
482 ci = c1 + (((c2 - c1) * (rpm - tachMinPercent)) /
483 (tachMaxPercent - tachMinPercent));
484 }
485
486 // Now calculate the CFM for this tach
487 // CFMi = Ci * Qmaxi * TACHi
488 totalCFM += ci * maxCFM * rpm;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700489 if constexpr (debug)
James Feista5e58722019-04-22 14:43:11 -0700490 {
George Liuce1abb62025-02-20 10:44:47 +0800491 lg2::error(
492 "totalCFM = {CFM}, Ci = {CI}, MaxCFM = {MAXCFM}, rpm = {RPM}, c1 = {C1}"
493 ", c2 = {C2}, max = {MAX}, min = {MIN}",
494 "CFM", totalCFM, "CI", ci, "MAXCFM", maxCFM, "RPM", rpm, "C1",
495 c1, "C2", c2, "MAX", tachMaxPercent, "MIN", tachMinPercent);
James Feista5e58722019-04-22 14:43:11 -0700496 }
James Feistb2eb3f52018-12-04 16:17:50 -0800497 }
498
499 // divide by 100 since rpm is in percent
500 value = totalCFM / 100;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700501 if constexpr (debug)
James Feista5e58722019-04-22 14:43:11 -0700502 {
George Liuce1abb62025-02-20 10:44:47 +0800503 lg2::error("cfm value = {VALUE}", "VALUE", value);
James Feista5e58722019-04-22 14:43:11 -0700504 }
James Feist9566bfa2019-01-29 15:31:23 -0800505 return true;
James Feistb2eb3f52018-12-04 16:17:50 -0800506}
507
508static constexpr double exitAirMaxReading = 127;
509static constexpr double exitAirMinReading = -128;
James Feistbc896df2018-11-26 16:28:17 -0800510ExitAirTempSensor::ExitAirTempSensor(
511 std::shared_ptr<sdbusplus::asio::connection>& conn,
James Feistb2eb3f52018-12-04 16:17:50 -0800512 const std::string& sensorName, const std::string& sensorConfiguration,
James Feistbc896df2018-11-26 16:28:17 -0800513 sdbusplus::asio::object_server& objectServer,
James Feistb839c052019-05-15 10:25:24 -0700514 std::vector<thresholds::Threshold>&& thresholdData) :
Zhikui Renda98f092021-11-01 09:41:08 -0700515 Sensor(escapeName(sensorName), std::move(thresholdData),
Zev Weiss054aad82022-08-18 01:37:34 -0700516 sensorConfiguration, "ExitAirTemp", false, false, exitAirMaxReading,
517 exitAirMinReading, conn, PowerState::on),
Ed Tanous2049bd22022-07-09 07:20:26 -0700518 objServer(objectServer)
James Feistbc896df2018-11-26 16:28:17 -0800519{
520 sensorInterface = objectServer.add_interface(
521 "/xyz/openbmc_project/sensors/temperature/" + name,
522 "xyz.openbmc_project.Sensor.Value");
523
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530524 for (const auto& threshold : thresholds)
James Feistbc896df2018-11-26 16:28:17 -0800525 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530526 std::string interface = thresholds::getInterface(threshold.level);
527 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
528 objectServer.add_interface(
529 "/xyz/openbmc_project/sensors/temperature/" + name, interface);
James Feistbc896df2018-11-26 16:28:17 -0800530 }
James Feist078f2322019-03-08 11:09:05 -0800531 association = objectServer.add_interface(
532 "/xyz/openbmc_project/sensors/temperature/" + name,
James Feist2adc95c2019-09-30 14:55:28 -0700533 association::interface);
Andrei Kartashev39287412022-02-04 16:04:47 +0300534 setInitialProperties(sensor_paths::unitDegreesC);
James Feistbc896df2018-11-26 16:28:17 -0800535}
536
537ExitAirTempSensor::~ExitAirTempSensor()
538{
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530539 for (const auto& iface : thresholdInterfaces)
540 {
541 objServer.remove_interface(iface);
542 }
James Feist523828e2019-03-04 14:38:37 -0800543 objServer.remove_interface(sensorInterface);
James Feist078f2322019-03-08 11:09:05 -0800544 objServer.remove_interface(association);
James Feistbc896df2018-11-26 16:28:17 -0800545}
546
Ed Tanous201a1012024-04-03 18:07:28 -0700547void ExitAirTempSensor::setupMatches()
James Feistbc896df2018-11-26 16:28:17 -0800548{
Brandon Kim66558232021-11-09 16:53:08 -0800549 constexpr const auto matchTypes{
550 std::to_array<const char*>({"power", inletTemperatureSensor})};
James Feistbc896df2018-11-26 16:28:17 -0800551
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700552 std::weak_ptr<ExitAirTempSensor> weakRef = weak_from_this();
Ed Tanous13b63f82021-05-11 16:12:52 -0700553 for (const std::string type : matchTypes)
James Feistbc896df2018-11-26 16:28:17 -0800554 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400555 setupSensorMatch(
556 matches, *dbusConnection, type,
557 [weakRef,
558 type](const double& value, sdbusplus::message_t& message) {
559 auto self = weakRef.lock();
560 if (!self)
561 {
562 return;
563 }
564 if (type == "power")
565 {
566 std::string path = message.get_path();
567 if (path.find("PS") != std::string::npos &&
568 path.ends_with("Input_Power"))
569 {
570 self->powerReadings[message.get_path()] = value;
571 }
572 }
573 else if (type == inletTemperatureSensor)
574 {
575 self->inletTemp = value;
576 }
577 self->updateReading();
578 });
579 }
580 dbusConnection->async_method_call(
581 [weakRef](boost::system::error_code ec,
582 const std::variant<double>& value) {
583 if (ec)
584 {
585 // sensor not ready yet
586 return;
587 }
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700588 auto self = weakRef.lock();
589 if (!self)
590 {
591 return;
592 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400593 self->inletTemp = std::visit(VariantToDoubleVisitor(), value);
594 },
James Feist9566bfa2019-01-29 15:31:23 -0800595 "xyz.openbmc_project.HwmonTempSensor",
596 std::string("/xyz/openbmc_project/sensors/") + inletTemperatureSensor,
James Feista5e58722019-04-22 14:43:11 -0700597 properties::interface, properties::get, sensorValueInterface, "Value");
598 dbusConnection->async_method_call(
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700599 [weakRef](boost::system::error_code ec, const GetSubTreeType& subtree) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400600 if (ec)
James Feista5e58722019-04-22 14:43:11 -0700601 {
George Liuce1abb62025-02-20 10:44:47 +0800602 lg2::error("Error contacting mapper");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400603 return;
James Feista5e58722019-04-22 14:43:11 -0700604 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400605 auto self = weakRef.lock();
606 if (!self)
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700607 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400608 return;
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700609 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400610 for (const auto& [path, matches] : subtree)
611 {
612 size_t lastSlash = path.rfind('/');
613 if (lastSlash == std::string::npos ||
614 lastSlash == path.size() || matches.empty())
615 {
616 continue;
617 }
618 std::string sensorName = path.substr(lastSlash + 1);
619 if (sensorName.starts_with("PS") &&
620 sensorName.ends_with("Input_Power"))
621 {
622 // lambda capture requires a proper variable (not a
623 // structured binding)
624 const std::string& cbPath = path;
625 self->dbusConnection->async_method_call(
626 [weakRef, cbPath](boost::system::error_code ec,
627 const std::variant<double>& value) {
628 if (ec)
629 {
George Liuce1abb62025-02-20 10:44:47 +0800630 lg2::error("Error getting value from '{PATH}'",
631 "PATH", cbPath);
Patrick Williams2aaf7172024-08-16 15:20:40 -0400632 }
633 auto self = weakRef.lock();
634 if (!self)
635 {
636 return;
637 }
638 double reading =
639 std::visit(VariantToDoubleVisitor(), value);
640 if constexpr (debug)
641 {
George Liuce1abb62025-02-20 10:44:47 +0800642 lg2::error("'{PATH}' reading '{VALUE}'", "PATH",
643 cbPath, "VALUE", reading);
Patrick Williams2aaf7172024-08-16 15:20:40 -0400644 }
645 self->powerReadings[cbPath] = reading;
646 },
647 matches[0].first, cbPath, properties::interface,
648 properties::get, sensorValueInterface, "Value");
649 }
650 }
651 },
James Feista5e58722019-04-22 14:43:11 -0700652 mapper::busName, mapper::path, mapper::interface, mapper::subtree,
653 "/xyz/openbmc_project/sensors/power", 0,
654 std::array<const char*, 1>{sensorValueInterface});
James Feistbc896df2018-11-26 16:28:17 -0800655}
656
Ed Tanous201a1012024-04-03 18:07:28 -0700657void ExitAirTempSensor::updateReading()
James Feistbc896df2018-11-26 16:28:17 -0800658{
James Feistbc896df2018-11-26 16:28:17 -0800659 double val = 0.0;
660 if (calculate(val))
661 {
James Feist18af4232019-03-13 11:14:00 -0700662 val = std::floor(val + 0.5);
James Feistbc896df2018-11-26 16:28:17 -0800663 updateValue(val);
664 }
665 else
666 {
667 updateValue(std::numeric_limits<double>::quiet_NaN());
668 }
669}
670
Ed Tanous201a1012024-04-03 18:07:28 -0700671double ExitAirTempSensor::getTotalCFM()
James Feistbc896df2018-11-26 16:28:17 -0800672{
James Feistb2eb3f52018-12-04 16:17:50 -0800673 double sum = 0;
674 for (auto& sensor : cfmSensors)
James Feistbc896df2018-11-26 16:28:17 -0800675 {
James Feistb2eb3f52018-12-04 16:17:50 -0800676 double reading = 0;
677 if (!sensor->calculate(reading))
James Feistbc896df2018-11-26 16:28:17 -0800678 {
James Feistbc896df2018-11-26 16:28:17 -0800679 return -1;
680 }
James Feistb2eb3f52018-12-04 16:17:50 -0800681 sum += reading;
James Feistbc896df2018-11-26 16:28:17 -0800682 }
James Feistb2eb3f52018-12-04 16:17:50 -0800683
684 return sum;
James Feistbc896df2018-11-26 16:28:17 -0800685}
686
687bool ExitAirTempSensor::calculate(double& val)
688{
Zhikui Ren12e3d672020-12-03 15:14:49 -0800689 constexpr size_t maxErrorPrint = 5;
James Feistbc896df2018-11-26 16:28:17 -0800690 static bool firstRead = false;
James Feistae11cfc2019-05-07 15:01:20 -0700691 static size_t errorPrint = maxErrorPrint;
692
James Feistbc896df2018-11-26 16:28:17 -0800693 double cfm = getTotalCFM();
694 if (cfm <= 0)
695 {
George Liuce1abb62025-02-20 10:44:47 +0800696 lg2::error("Error getting cfm");
James Feistbc896df2018-11-26 16:28:17 -0800697 return false;
698 }
699
Zhikui Ren12e3d672020-12-03 15:14:49 -0800700 // Though cfm is not expected to be less than qMin normally,
701 // it is not a hard limit for exit air temp calculation.
702 // 50% qMin is chosen as a generic limit between providing
703 // a valid derived exit air temp and reporting exit air temp not available.
704 constexpr const double cfmLimitFactor = 0.5;
705 if (cfm < (qMin * cfmLimitFactor))
706 {
707 if (errorPrint > 0)
708 {
709 errorPrint--;
George Liuce1abb62025-02-20 10:44:47 +0800710 lg2::error("cfm '{CFM}' is too low, expected qMin '{QMIN}'", "CFM",
711 cfm, "QMIN", qMin);
Zhikui Ren12e3d672020-12-03 15:14:49 -0800712 }
713 val = 0;
714 return false;
715 }
716
James Feistbc896df2018-11-26 16:28:17 -0800717 // if there is an error getting inlet temp, return error
718 if (std::isnan(inletTemp))
719 {
James Feistae11cfc2019-05-07 15:01:20 -0700720 if (errorPrint > 0)
721 {
722 errorPrint--;
George Liuce1abb62025-02-20 10:44:47 +0800723 lg2::error("Cannot get inlet temp");
James Feistae11cfc2019-05-07 15:01:20 -0700724 }
James Feistbc896df2018-11-26 16:28:17 -0800725 val = 0;
726 return false;
727 }
728
729 // if fans are off, just make the exit temp equal to inlet
James Feist71d31b22019-01-02 16:57:54 -0800730 if (!isPowerOn())
James Feistbc896df2018-11-26 16:28:17 -0800731 {
732 val = inletTemp;
733 return true;
734 }
735
736 double totalPower = 0;
Zev Weiss72f322f2022-08-12 18:21:01 -0700737 for (const auto& [path, reading] : powerReadings)
James Feistbc896df2018-11-26 16:28:17 -0800738 {
Zev Weiss72f322f2022-08-12 18:21:01 -0700739 if (std::isnan(reading))
James Feistbc896df2018-11-26 16:28:17 -0800740 {
741 continue;
742 }
Zev Weiss72f322f2022-08-12 18:21:01 -0700743 totalPower += reading;
James Feistbc896df2018-11-26 16:28:17 -0800744 }
745
746 // Calculate power correction factor
747 // Ci = CL + (CH - CL)/(QMax - QMin) * (CFM - QMin)
Ed Tanous8a57ec02020-10-09 12:46:52 -0700748 double powerFactor = 0.0;
James Feistbc896df2018-11-26 16:28:17 -0800749 if (cfm <= qMin)
750 {
751 powerFactor = powerFactorMin;
752 }
753 else if (cfm >= qMax)
754 {
755 powerFactor = powerFactorMax;
756 }
757 else
758 {
759 powerFactor = powerFactorMin + ((powerFactorMax - powerFactorMin) /
760 (qMax - qMin) * (cfm - qMin));
761 }
762
Ed Tanous8a57ec02020-10-09 12:46:52 -0700763 totalPower *= powerFactor;
James Feistbc896df2018-11-26 16:28:17 -0800764 totalPower += pOffset;
765
766 if (totalPower == 0)
767 {
James Feistae11cfc2019-05-07 15:01:20 -0700768 if (errorPrint > 0)
769 {
770 errorPrint--;
George Liuce1abb62025-02-20 10:44:47 +0800771 lg2::error("total power 0");
James Feistae11cfc2019-05-07 15:01:20 -0700772 }
James Feistbc896df2018-11-26 16:28:17 -0800773 val = 0;
774 return false;
775 }
776
Ed Tanous8a57ec02020-10-09 12:46:52 -0700777 if constexpr (debug)
James Feistbc896df2018-11-26 16:28:17 -0800778 {
George Liuce1abb62025-02-20 10:44:47 +0800779 lg2::info(
780 "Power Factor: {POWER_FACTOR}, Inlet Temp: {INLET_TEMP}, Total Power: {TOTAL_POWER}",
781 "POWER_FACTOR", powerFactor, "INLET_TEMP", inletTemp, "TOTAL_POWER",
782 totalPower);
783 };
James Feistbc896df2018-11-26 16:28:17 -0800784
785 // Calculate the exit air temp
786 // Texit = Tfp + (1.76 * TotalPower / CFM * Faltitude)
Ed Tanous8a57ec02020-10-09 12:46:52 -0700787 double reading = 1.76 * totalPower * altitudeFactor;
James Feistbc896df2018-11-26 16:28:17 -0800788 reading /= cfm;
789 reading += inletTemp;
790
Ed Tanous8a57ec02020-10-09 12:46:52 -0700791 if constexpr (debug)
James Feistbc896df2018-11-26 16:28:17 -0800792 {
George Liuce1abb62025-02-20 10:44:47 +0800793 lg2::info("Reading 1: '{VALUE}'", "VALUE", reading);
James Feistbc896df2018-11-26 16:28:17 -0800794 }
795
796 // Now perform the exponential average
797 // Calculate alpha based on SDR values and CFM
798 // Ai = As + (Af - As)/(QMax - QMin) * (CFM - QMin)
799
800 double alpha = 0.0;
801 if (cfm < qMin)
802 {
803 alpha = alphaS;
804 }
805 else if (cfm >= qMax)
806 {
807 alpha = alphaF;
808 }
809 else
810 {
811 alpha = alphaS + ((alphaF - alphaS) * (cfm - qMin) / (qMax - qMin));
812 }
813
Zhikui Ren12e3d672020-12-03 15:14:49 -0800814 auto time = std::chrono::steady_clock::now();
James Feistbc896df2018-11-26 16:28:17 -0800815 if (!firstRead)
816 {
817 firstRead = true;
818 lastTime = time;
819 lastReading = reading;
820 }
821 double alphaDT =
822 std::chrono::duration_cast<std::chrono::seconds>(time - lastTime)
823 .count() *
824 alpha;
825
826 // cap at 1.0 or the below fails
827 if (alphaDT > 1.0)
828 {
829 alphaDT = 1.0;
830 }
831
Ed Tanous8a57ec02020-10-09 12:46:52 -0700832 if constexpr (debug)
James Feistbc896df2018-11-26 16:28:17 -0800833 {
George Liuce1abb62025-02-20 10:44:47 +0800834 lg2::info("AlphaDT: '{ADT}'", "ADT", alphaDT);
James Feistbc896df2018-11-26 16:28:17 -0800835 }
836
837 reading = ((reading * alphaDT) + (lastReading * (1.0 - alphaDT)));
838
Ed Tanous8a57ec02020-10-09 12:46:52 -0700839 if constexpr (debug)
James Feistbc896df2018-11-26 16:28:17 -0800840 {
George Liuce1abb62025-02-20 10:44:47 +0800841 lg2::info("Reading 2: '{VALUE}'", "VALUE", reading);
James Feistbc896df2018-11-26 16:28:17 -0800842 }
843
844 val = reading;
845 lastReading = reading;
846 lastTime = time;
James Feistae11cfc2019-05-07 15:01:20 -0700847 errorPrint = maxErrorPrint;
James Feistbc896df2018-11-26 16:28:17 -0800848 return true;
849}
850
Ed Tanous201a1012024-04-03 18:07:28 -0700851void ExitAirTempSensor::checkThresholds()
James Feistbc896df2018-11-26 16:28:17 -0800852{
853 thresholds::checkThresholds(this);
854}
855
Zev Weissafd15042022-07-18 12:28:40 -0700856static void loadVariantPathArray(const SensorBaseConfigMap& data,
857 const std::string& key,
858 std::vector<std::string>& resp)
James Feistbc896df2018-11-26 16:28:17 -0800859{
860 auto it = data.find(key);
861 if (it == data.end())
862 {
George Liuce1abb62025-02-20 10:44:47 +0800863 lg2::error("Configuration missing '{KEY}'", "KEY", key);
James Feistbc896df2018-11-26 16:28:17 -0800864 throw std::invalid_argument("Key Missing");
865 }
866 BasicVariantType copy = it->second;
James Feist3eb82622019-02-08 13:10:22 -0800867 std::vector<std::string> config = std::get<std::vector<std::string>>(copy);
James Feistbc896df2018-11-26 16:28:17 -0800868 for (auto& str : config)
869 {
870 boost::replace_all(str, " ", "_");
871 }
872 resp = std::move(config);
873}
874
875void createSensor(sdbusplus::asio::object_server& objectServer,
James Feistb2eb3f52018-12-04 16:17:50 -0800876 std::shared_ptr<ExitAirTempSensor>& exitAirSensor,
James Feistbc896df2018-11-26 16:28:17 -0800877 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
878{
879 if (!dbusConnection)
880 {
George Liuce1abb62025-02-20 10:44:47 +0800881 lg2::error("Connection not created");
James Feistbc896df2018-11-26 16:28:17 -0800882 return;
883 }
James Feist655f3762020-10-05 15:28:15 -0700884 auto getter = std::make_shared<GetSensorConfiguration>(
Patrick Williams597e8422023-10-20 11:19:01 -0500885 dbusConnection, [&objectServer, &dbusConnection,
886 &exitAirSensor](const ManagedObjectType& resp) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400887 cfmSensors.clear();
888 for (const auto& [path, interfaces] : resp)
James Feistbc896df2018-11-26 16:28:17 -0800889 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400890 for (const auto& [intf, cfg] : interfaces)
James Feistbc896df2018-11-26 16:28:17 -0800891 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400892 if (intf == configInterfaceName(exitAirType))
893 {
894 // thresholds should be under the same path
895 std::vector<thresholds::Threshold> sensorThresholds;
896 parseThresholdsFromConfig(interfaces, sensorThresholds);
James Feistbc896df2018-11-26 16:28:17 -0800897
Patrick Williams2aaf7172024-08-16 15:20:40 -0400898 std::string name =
899 loadVariant<std::string>(cfg, "Name");
900 exitAirSensor = nullptr;
901 exitAirSensor = std::make_shared<ExitAirTempSensor>(
902 dbusConnection, name, path.str, objectServer,
903 std::move(sensorThresholds));
904 exitAirSensor->powerFactorMin =
905 loadVariant<double>(cfg, "PowerFactorMin");
906 exitAirSensor->powerFactorMax =
907 loadVariant<double>(cfg, "PowerFactorMax");
908 exitAirSensor->qMin = loadVariant<double>(cfg, "QMin");
909 exitAirSensor->qMax = loadVariant<double>(cfg, "QMax");
910 exitAirSensor->alphaS =
911 loadVariant<double>(cfg, "AlphaS");
912 exitAirSensor->alphaF =
913 loadVariant<double>(cfg, "AlphaF");
914 }
915 else if (intf == configInterfaceName(cfmType))
916 {
917 // thresholds should be under the same path
918 std::vector<thresholds::Threshold> sensorThresholds;
919 parseThresholdsFromConfig(interfaces, sensorThresholds);
920 std::string name =
921 loadVariant<std::string>(cfg, "Name");
922 auto sensor = std::make_shared<CFMSensor>(
923 dbusConnection, name, path.str, objectServer,
924 std::move(sensorThresholds), exitAirSensor);
925 loadVariantPathArray(cfg, "Tachs", sensor->tachs);
926 sensor->maxCFM = loadVariant<double>(cfg, "MaxCFM");
James Feistbc896df2018-11-26 16:28:17 -0800927
Patrick Williams2aaf7172024-08-16 15:20:40 -0400928 // change these into percent upon getting the data
929 sensor->c1 = loadVariant<double>(cfg, "C1") / 100;
930 sensor->c2 = loadVariant<double>(cfg, "C2") / 100;
931 sensor->tachMinPercent =
932 loadVariant<double>(cfg, "TachMinPercent");
933 sensor->tachMaxPercent =
934 loadVariant<double>(cfg, "TachMaxPercent");
935 sensor->createMaxCFMIface();
936 sensor->setupMatches();
James Feistbc896df2018-11-26 16:28:17 -0800937
Patrick Williams2aaf7172024-08-16 15:20:40 -0400938 cfmSensors.emplace_back(std::move(sensor));
939 }
James Feistbc896df2018-11-26 16:28:17 -0800940 }
941 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400942 if (exitAirSensor)
943 {
944 exitAirSensor->setupMatches();
945 exitAirSensor->updateReading();
946 }
947 });
James Feist655f3762020-10-05 15:28:15 -0700948 getter->getConfiguration(
Zev Weiss054aad82022-08-18 01:37:34 -0700949 std::vector<std::string>(monitorTypes.begin(), monitorTypes.end()));
James Feistbc896df2018-11-26 16:28:17 -0800950}
951
James Feistb6c0b912019-07-09 12:21:44 -0700952int main()
James Feistbc896df2018-11-26 16:28:17 -0800953{
Ed Tanous1f978632023-02-28 18:16:39 -0800954 boost::asio::io_context io;
James Feistbc896df2018-11-26 16:28:17 -0800955 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
Johnathan Mantey661d4372022-10-27 09:00:59 -0700956 sdbusplus::asio::object_server objectServer(systemBus, true);
957 objectServer.add_manager("/xyz/openbmc_project/sensors");
James Feistbc896df2018-11-26 16:28:17 -0800958 systemBus->request_name("xyz.openbmc_project.ExitAirTempSensor");
James Feistbc896df2018-11-26 16:28:17 -0800959 std::shared_ptr<ExitAirTempSensor> sensor =
960 nullptr; // wait until we find the config
James Feistbc896df2018-11-26 16:28:17 -0800961
Ed Tanous83db50c2023-03-01 10:20:24 -0800962 boost::asio::post(io,
963 [&]() { createSensor(objectServer, sensor, systemBus); });
James Feistbc896df2018-11-26 16:28:17 -0800964
Ed Tanous9b4a20e2022-09-06 08:47:11 -0700965 boost::asio::steady_timer configTimer(io);
James Feistbc896df2018-11-26 16:28:17 -0800966
Patrick Williams92f8f512022-07-22 19:26:55 -0500967 std::function<void(sdbusplus::message_t&)> eventHandler =
968 [&](sdbusplus::message_t&) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400969 configTimer.expires_after(std::chrono::seconds(1));
970 // create a timer because normally multiple properties change
971 configTimer.async_wait([&](const boost::system::error_code& ec) {
972 if (ec == boost::asio::error::operation_aborted)
973 {
974 return; // we're being canceled
975 }
976 createSensor(objectServer, sensor, systemBus);
977 if (!sensor)
978 {
George Liuce1abb62025-02-20 10:44:47 +0800979 lg2::info("Configuration not detected");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400980 }
981 });
982 };
Zev Weiss214d9712022-08-12 12:54:31 -0700983 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
Zev Weiss054aad82022-08-18 01:37:34 -0700984 setupPropertiesChangedMatches(*systemBus, monitorTypes, eventHandler);
James Feistbc896df2018-11-26 16:28:17 -0800985
Bruce Lee913d4d02021-07-22 10:18:42 +0800986 setupManufacturingModeMatch(*systemBus);
James Feistbc896df2018-11-26 16:28:17 -0800987 io.run();
Zhikui Ren8685b172021-06-29 15:16:52 -0700988 return 0;
James Feistbc896df2018-11-26 16:28:17 -0800989}