blob: 09613b1b2d917191ff90296762e13e60fd2888f1 [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
James Feistb2eb3f52018-12-04 16:17:50 -080065static constexpr double cfmMaxReading = 255;
66static constexpr double cfmMinReading = 0;
67
James Feist13452092019-03-07 16:38:12 -080068static constexpr size_t minSystemCfm = 50;
69
Zev Weiss054aad82022-08-18 01:37:34 -070070constexpr const auto monitorTypes{
71 std::to_array<const char*>({exitAirType, cfmType})};
James Feist655f3762020-10-05 15:28:15 -070072
James Feist9a25ed42019-10-15 15:43:44 -070073static std::vector<std::shared_ptr<CFMSensor>> cfmSensors;
74
James Feistb2eb3f52018-12-04 16:17:50 -080075static void setupSensorMatch(
Patrick Williams92f8f512022-07-22 19:26:55 -050076 std::vector<sdbusplus::bus::match_t>& matches, sdbusplus::bus_t& connection,
77 const std::string& type,
78 std::function<void(const double&, sdbusplus::message_t&)>&& callback)
James Feistb2eb3f52018-12-04 16:17:50 -080079{
Patrick Williams92f8f512022-07-22 19:26:55 -050080 std::function<void(sdbusplus::message_t & message)> eventHandler =
81 [callback{std::move(callback)}](sdbusplus::message_t& message) {
Patrick Williams2aaf7172024-08-16 15:20:40 -040082 std::string objectName;
83 boost::container::flat_map<std::string,
84 std::variant<double, int64_t>>
85 values;
86 message.read(objectName, values);
87 auto findValue = values.find("Value");
88 if (findValue == values.end())
89 {
90 return;
91 }
92 double value =
93 std::visit(VariantToDoubleVisitor(), findValue->second);
94 if (std::isnan(value))
95 {
96 return;
97 }
James Feist9566bfa2019-01-29 15:31:23 -080098
Patrick Williams2aaf7172024-08-16 15:20:40 -040099 callback(value, message);
100 };
101 matches.emplace_back(
102 connection,
103 "type='signal',"
104 "member='PropertiesChanged',interface='org."
105 "freedesktop.DBus.Properties',path_"
106 "namespace='/xyz/openbmc_project/sensors/" +
107 std::string(type) + "',arg0='xyz.openbmc_project.Sensor.Value'",
108 std::move(eventHandler));
James Feistb2eb3f52018-12-04 16:17:50 -0800109}
110
James Feist13452092019-03-07 16:38:12 -0800111static void setMaxPWM(const std::shared_ptr<sdbusplus::asio::connection>& conn,
112 double value)
113{
114 using GetSubTreeType = std::vector<std::pair<
115 std::string,
116 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
117
118 conn->async_method_call(
Patrick Williams2aaf7172024-08-16 15:20:40 -0400119 [conn,
120 value](const boost::system::error_code ec, const GetSubTreeType& ret) {
121 if (ec)
James Feist13452092019-03-07 16:38:12 -0800122 {
George Liuce1abb62025-02-20 10:44:47 +0800123 lg2::error("Error calling mapper");
James Feist13452092019-03-07 16:38:12 -0800124 return;
125 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400126 for (const auto& [path, objDict] : ret)
127 {
128 if (objDict.empty())
129 {
130 return;
131 }
132 const std::string& owner = objDict.begin()->first;
Ed Tanousbb679322022-05-16 16:10:00 -0700133
James Feist13452092019-03-07 16:38:12 -0800134 conn->async_method_call(
Patrick Williams2aaf7172024-08-16 15:20:40 -0400135 [conn, value, owner,
136 path{path}](const boost::system::error_code ec,
137 const std::variant<std::string>& classType) {
138 if (ec)
139 {
George Liuce1abb62025-02-20 10:44:47 +0800140 lg2::error("Error getting pid class");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400141 return;
142 }
143 const auto* classStr =
144 std::get_if<std::string>(&classType);
145 if (classStr == nullptr || *classStr != "fan")
146 {
147 return;
148 }
149 conn->async_method_call(
150 [](boost::system::error_code& ec) {
151 if (ec)
152 {
George Liuce1abb62025-02-20 10:44:47 +0800153 lg2::error("Error setting pid class");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400154 return;
155 }
156 },
157 owner, path, "org.freedesktop.DBus.Properties",
158 "Set", pidConfigurationType, "OutLimitMax",
159 std::variant<double>(value));
160 },
161 owner, path, "org.freedesktop.DBus.Properties", "Get",
162 pidConfigurationType, "Class");
163 }
164 },
James Feista5e58722019-04-22 14:43:11 -0700165 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
166 0, std::array<std::string, 1>{pidConfigurationType});
James Feist13452092019-03-07 16:38:12 -0800167}
168
James Feistb2eb3f52018-12-04 16:17:50 -0800169CFMSensor::CFMSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
170 const std::string& sensorName,
171 const std::string& sensorConfiguration,
172 sdbusplus::asio::object_server& objectServer,
James Feistb839c052019-05-15 10:25:24 -0700173 std::vector<thresholds::Threshold>&& thresholdData,
James Feistb2eb3f52018-12-04 16:17:50 -0800174 std::shared_ptr<ExitAirTempSensor>& parent) :
Zhikui Renda98f092021-11-01 09:41:08 -0700175 Sensor(escapeName(sensorName), std::move(thresholdData),
Zev Weiss054aad82022-08-18 01:37:34 -0700176 sensorConfiguration, "CFMSensor", false, false, cfmMaxReading,
177 cfmMinReading, conn, PowerState::on),
Ed Tanous2049bd22022-07-09 07:20:26 -0700178 parent(parent), objServer(objectServer)
James Feistb2eb3f52018-12-04 16:17:50 -0800179{
Basheer Ahmed Muddebihale5b867b2021-07-26 08:32:19 -0700180 sensorInterface = objectServer.add_interface(
181 "/xyz/openbmc_project/sensors/airflow/" + name,
182 "xyz.openbmc_project.Sensor.Value");
James Feistb2eb3f52018-12-04 16:17:50 -0800183
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530184 for (const auto& threshold : thresholds)
James Feistb2eb3f52018-12-04 16:17:50 -0800185 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530186 std::string interface = thresholds::getInterface(threshold.level);
187 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
188 objectServer.add_interface(
189 "/xyz/openbmc_project/sensors/airflow/" + name, interface);
James Feistb2eb3f52018-12-04 16:17:50 -0800190 }
James Feist078f2322019-03-08 11:09:05 -0800191
192 association = objectServer.add_interface(
Basheer Ahmed Muddebihale5b867b2021-07-26 08:32:19 -0700193 "/xyz/openbmc_project/sensors/airflow/" + name, association::interface);
James Feist078f2322019-03-08 11:09:05 -0800194
Andrei Kartashev39287412022-02-04 16:04:47 +0300195 setInitialProperties(sensor_paths::unitCFM);
James Feist9a25ed42019-10-15 15:43:44 -0700196
James Feist13452092019-03-07 16:38:12 -0800197 pwmLimitIface =
198 objectServer.add_interface("/xyz/openbmc_project/control/pwm_limit",
199 "xyz.openbmc_project.Control.PWMLimit");
200 cfmLimitIface =
201 objectServer.add_interface("/xyz/openbmc_project/control/MaxCFM",
202 "xyz.openbmc_project.Control.CFMLimit");
James Feist9a25ed42019-10-15 15:43:44 -0700203}
James Feist13452092019-03-07 16:38:12 -0800204
James Feist9a25ed42019-10-15 15:43:44 -0700205void CFMSensor::setupMatches()
206{
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700207 std::weak_ptr<CFMSensor> weakRef = weak_from_this();
Ed Tanous8a17c302021-09-02 15:07:11 -0700208 setupSensorMatch(
209 matches, *dbusConnection, "fan_tach",
Patrick Williams92f8f512022-07-22 19:26:55 -0500210 [weakRef](const double& value, sdbusplus::message_t& message) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400211 auto self = weakRef.lock();
212 if (!self)
213 {
214 return;
215 }
216 self->tachReadings[message.get_path()] = value;
217 if (self->tachRanges.find(message.get_path()) ==
218 self->tachRanges.end())
219 {
220 // calls update reading after updating ranges
221 self->addTachRanges(message.get_sender(), message.get_path());
222 }
223 else
224 {
225 self->updateReading();
226 }
227 });
James Feist9a25ed42019-10-15 15:43:44 -0700228
229 dbusConnection->async_method_call(
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700230 [weakRef](const boost::system::error_code ec,
231 const std::variant<double> cfmVariant) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400232 auto self = weakRef.lock();
233 if (!self)
Ed Tanousbb679322022-05-16 16:10:00 -0700234 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400235 return;
James Feist13452092019-03-07 16:38:12 -0800236 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400237
238 uint64_t maxRpm = 100;
239 if (!ec)
240 {
241 const auto* cfm = std::get_if<double>(&cfmVariant);
242 if (cfm != nullptr && *cfm >= minSystemCfm)
243 {
244 maxRpm = self->getMaxRpm(*cfm);
245 }
246 }
247 self->pwmLimitIface->register_property("Limit", maxRpm);
248 self->pwmLimitIface->initialize();
249 setMaxPWM(self->dbusConnection, maxRpm);
250 },
James Feist13452092019-03-07 16:38:12 -0800251 settingsDaemon, cfmSettingPath, "org.freedesktop.DBus.Properties",
252 "Get", cfmSettingIface, "Limit");
253
Patrick Williams2aaf7172024-08-16 15:20:40 -0400254 matches.emplace_back(
255 *dbusConnection,
256 "type='signal',"
257 "member='PropertiesChanged',interface='org."
258 "freedesktop.DBus.Properties',path='" +
259 std::string(cfmSettingPath) + "',arg0='" +
260 std::string(cfmSettingIface) + "'",
261 [weakRef](sdbusplus::message_t& message) {
262 auto self = weakRef.lock();
263 if (!self)
264 {
265 return;
266 }
267 boost::container::flat_map<std::string, std::variant<double>>
268 values;
269 std::string objectName;
270 message.read(objectName, values);
271 const auto findValue = values.find("Limit");
272 if (findValue == values.end())
273 {
274 return;
275 }
276 auto* const reading = std::get_if<double>(&(findValue->second));
277 if (reading == nullptr)
278 {
George Liuce1abb62025-02-20 10:44:47 +0800279 lg2::error("Got CFM Limit of wrong type");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400280 return;
281 }
282 if (*reading < minSystemCfm && *reading != 0)
283 {
George Liuce1abb62025-02-20 10:44:47 +0800284 lg2::error("Illegal CFM setting detected");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400285 return;
286 }
287 uint64_t maxRpm = self->getMaxRpm(*reading);
288 self->pwmLimitIface->set_property("Limit", maxRpm);
289 setMaxPWM(self->dbusConnection, maxRpm);
290 });
James Feistb2eb3f52018-12-04 16:17:50 -0800291}
292
James Feist9566bfa2019-01-29 15:31:23 -0800293CFMSensor::~CFMSensor()
294{
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530295 for (const auto& iface : thresholdInterfaces)
296 {
297 objServer.remove_interface(iface);
298 }
James Feist9566bfa2019-01-29 15:31:23 -0800299 objServer.remove_interface(sensorInterface);
James Feist078f2322019-03-08 11:09:05 -0800300 objServer.remove_interface(association);
James Feist13452092019-03-07 16:38:12 -0800301 objServer.remove_interface(cfmLimitIface);
302 objServer.remove_interface(pwmLimitIface);
303}
304
Ed Tanous201a1012024-04-03 18:07:28 -0700305void CFMSensor::createMaxCFMIface()
James Feist13452092019-03-07 16:38:12 -0800306{
James Feistb6c0b912019-07-09 12:21:44 -0700307 cfmLimitIface->register_property("Limit", c2 * maxCFM * tachs.size());
James Feist13452092019-03-07 16:38:12 -0800308 cfmLimitIface->initialize();
James Feist9566bfa2019-01-29 15:31:23 -0800309}
310
James Feistb2eb3f52018-12-04 16:17:50 -0800311void CFMSensor::addTachRanges(const std::string& serviceName,
312 const std::string& path)
313{
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700314 std::weak_ptr<CFMSensor> weakRef = weak_from_this();
James Feistb2eb3f52018-12-04 16:17:50 -0800315 dbusConnection->async_method_call(
Zev Weissafd15042022-07-18 12:28:40 -0700316 [weakRef, path](const boost::system::error_code ec,
317 const SensorBaseConfigMap& data) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400318 if (ec)
319 {
George Liuce1abb62025-02-20 10:44:47 +0800320 lg2::error("Error getting properties from '{PATH}'", "PATH",
321 path);
Patrick Williams2aaf7172024-08-16 15:20:40 -0400322 return;
323 }
324 auto self = weakRef.lock();
325 if (!self)
326 {
327 return;
328 }
329 double max = loadVariant<double>(data, "MaxValue");
330 double min = loadVariant<double>(data, "MinValue");
331 self->tachRanges[path] = std::make_pair(min, max);
332 self->updateReading();
333 },
James Feistb2eb3f52018-12-04 16:17:50 -0800334 serviceName, path, "org.freedesktop.DBus.Properties", "GetAll",
335 "xyz.openbmc_project.Sensor.Value");
336}
337
Ed Tanous201a1012024-04-03 18:07:28 -0700338void CFMSensor::checkThresholds()
James Feistb2eb3f52018-12-04 16:17:50 -0800339{
340 thresholds::checkThresholds(this);
341}
342
Ed Tanous201a1012024-04-03 18:07:28 -0700343void CFMSensor::updateReading()
James Feistb2eb3f52018-12-04 16:17:50 -0800344{
345 double val = 0.0;
346 if (calculate(val))
347 {
348 if (value != val && parent)
349 {
350 parent->updateReading();
351 }
352 updateValue(val);
353 }
354 else
355 {
356 updateValue(std::numeric_limits<double>::quiet_NaN());
357 }
358}
359
Ed Tanous2049bd22022-07-09 07:20:26 -0700360uint64_t CFMSensor::getMaxRpm(uint64_t cfmMaxSetting) const
James Feist13452092019-03-07 16:38:12 -0800361{
362 uint64_t pwmPercent = 100;
363 double totalCFM = std::numeric_limits<double>::max();
364 if (cfmMaxSetting == 0)
365 {
366 return pwmPercent;
367 }
368
James Feist52427952019-04-05 14:23:35 -0700369 bool firstLoop = true;
James Feist13452092019-03-07 16:38:12 -0800370 while (totalCFM > cfmMaxSetting)
371 {
James Feist52427952019-04-05 14:23:35 -0700372 if (firstLoop)
373 {
374 firstLoop = false;
375 }
376 else
377 {
378 pwmPercent--;
379 }
380
James Feist13452092019-03-07 16:38:12 -0800381 double ci = 0;
382 if (pwmPercent == 0)
383 {
384 ci = 0;
385 }
386 else if (pwmPercent < tachMinPercent)
387 {
388 ci = c1;
389 }
390 else if (pwmPercent > tachMaxPercent)
391 {
392 ci = c2;
393 }
394 else
395 {
396 ci = c1 + (((c2 - c1) * (pwmPercent - tachMinPercent)) /
397 (tachMaxPercent - tachMinPercent));
398 }
399
400 // Now calculate the CFM for this tach
401 // CFMi = Ci * Qmaxi * TACHi
402 totalCFM = ci * maxCFM * pwmPercent;
403 totalCFM *= tachs.size();
404 // divide by 100 since pwm is in percent
405 totalCFM /= 100;
406
James Feist13452092019-03-07 16:38:12 -0800407 if (pwmPercent <= 0)
408 {
409 break;
410 }
411 }
James Feist52427952019-04-05 14:23:35 -0700412
James Feist13452092019-03-07 16:38:12 -0800413 return pwmPercent;
414}
415
James Feistb2eb3f52018-12-04 16:17:50 -0800416bool CFMSensor::calculate(double& value)
417{
418 double totalCFM = 0;
419 for (const std::string& tachName : tachs)
420 {
421 auto findReading = std::find_if(
Zev Weiss6c106d62022-08-17 20:50:00 -0700422 tachReadings.begin(), tachReadings.end(),
423 [&](const auto& item) { return item.first.ends_with(tachName); });
Patrick Williams597e8422023-10-20 11:19:01 -0500424 auto findRange = std::find_if(
425 tachRanges.begin(), tachRanges.end(),
426 [&](const auto& item) { return item.first.ends_with(tachName); });
James Feistb2eb3f52018-12-04 16:17:50 -0800427 if (findReading == tachReadings.end())
428 {
Alexander Hansen89be6142025-09-18 15:36:16 +0200429 lg2::debug("Can't find '{NAME}' in readings", "NAME", tachName);
James Feist9566bfa2019-01-29 15:31:23 -0800430 continue; // haven't gotten a reading
James Feistb2eb3f52018-12-04 16:17:50 -0800431 }
432
433 if (findRange == tachRanges.end())
434 {
George Liuce1abb62025-02-20 10:44:47 +0800435 lg2::error("Can't find '{NAME}' in ranges", "NAME", tachName);
James Feistb2eb3f52018-12-04 16:17:50 -0800436 return false; // haven't gotten a max / min
437 }
438
439 // avoid divide by 0
440 if (findRange->second.second == 0)
441 {
George Liuce1abb62025-02-20 10:44:47 +0800442 lg2::error("Tach Max Set to 0, tachName: '{NAME}'", "NAME",
443 tachName);
James Feistb2eb3f52018-12-04 16:17:50 -0800444 return false;
445 }
446
447 double rpm = findReading->second;
448
449 // for now assume the min for a fan is always 0, divide by max to get
450 // percent and mult by 100
451 rpm /= findRange->second.second;
452 rpm *= 100;
453
Alexander Hansen89be6142025-09-18 15:36:16 +0200454 lg2::debug("Tach '{NAME}' at '{RPM}'", "NAME", tachName, "RPM", rpm);
James Feistb2eb3f52018-12-04 16:17:50 -0800455
456 // Do a linear interpolation to get Ci
457 // Ci = C1 + (C2 - C1)/(RPM2 - RPM1) * (TACHi - TACH1)
458
459 double ci = 0;
460 if (rpm == 0)
461 {
462 ci = 0;
463 }
464 else if (rpm < tachMinPercent)
465 {
466 ci = c1;
467 }
468 else if (rpm > tachMaxPercent)
469 {
470 ci = c2;
471 }
472 else
473 {
474 ci = c1 + (((c2 - c1) * (rpm - tachMinPercent)) /
475 (tachMaxPercent - tachMinPercent));
476 }
477
478 // Now calculate the CFM for this tach
479 // CFMi = Ci * Qmaxi * TACHi
480 totalCFM += ci * maxCFM * rpm;
Alexander Hansen89be6142025-09-18 15:36:16 +0200481 lg2::debug(
482 "totalCFM = {CFM}, Ci = {CI}, MaxCFM = {MAXCFM}, rpm = {RPM}, c1 = {C1}"
483 ", c2 = {C2}, max = {MAX}, min = {MIN}",
484 "CFM", totalCFM, "CI", ci, "MAXCFM", maxCFM, "RPM", rpm, "C1", c1,
485 "C2", c2, "MAX", tachMaxPercent, "MIN", tachMinPercent);
James Feistb2eb3f52018-12-04 16:17:50 -0800486 }
487
488 // divide by 100 since rpm is in percent
489 value = totalCFM / 100;
Alexander Hansen89be6142025-09-18 15:36:16 +0200490 lg2::debug("cfm value = {VALUE}", "VALUE", value);
James Feist9566bfa2019-01-29 15:31:23 -0800491 return true;
James Feistb2eb3f52018-12-04 16:17:50 -0800492}
493
494static constexpr double exitAirMaxReading = 127;
495static constexpr double exitAirMinReading = -128;
James Feistbc896df2018-11-26 16:28:17 -0800496ExitAirTempSensor::ExitAirTempSensor(
497 std::shared_ptr<sdbusplus::asio::connection>& conn,
James Feistb2eb3f52018-12-04 16:17:50 -0800498 const std::string& sensorName, const std::string& sensorConfiguration,
James Feistbc896df2018-11-26 16:28:17 -0800499 sdbusplus::asio::object_server& objectServer,
James Feistb839c052019-05-15 10:25:24 -0700500 std::vector<thresholds::Threshold>&& thresholdData) :
Zhikui Renda98f092021-11-01 09:41:08 -0700501 Sensor(escapeName(sensorName), std::move(thresholdData),
Zev Weiss054aad82022-08-18 01:37:34 -0700502 sensorConfiguration, "ExitAirTemp", false, false, exitAirMaxReading,
503 exitAirMinReading, conn, PowerState::on),
Ed Tanous2049bd22022-07-09 07:20:26 -0700504 objServer(objectServer)
James Feistbc896df2018-11-26 16:28:17 -0800505{
506 sensorInterface = objectServer.add_interface(
507 "/xyz/openbmc_project/sensors/temperature/" + name,
508 "xyz.openbmc_project.Sensor.Value");
509
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530510 for (const auto& threshold : thresholds)
James Feistbc896df2018-11-26 16:28:17 -0800511 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530512 std::string interface = thresholds::getInterface(threshold.level);
513 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
514 objectServer.add_interface(
515 "/xyz/openbmc_project/sensors/temperature/" + name, interface);
James Feistbc896df2018-11-26 16:28:17 -0800516 }
James Feist078f2322019-03-08 11:09:05 -0800517 association = objectServer.add_interface(
518 "/xyz/openbmc_project/sensors/temperature/" + name,
James Feist2adc95c2019-09-30 14:55:28 -0700519 association::interface);
Andrei Kartashev39287412022-02-04 16:04:47 +0300520 setInitialProperties(sensor_paths::unitDegreesC);
James Feistbc896df2018-11-26 16:28:17 -0800521}
522
523ExitAirTempSensor::~ExitAirTempSensor()
524{
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530525 for (const auto& iface : thresholdInterfaces)
526 {
527 objServer.remove_interface(iface);
528 }
James Feist523828e2019-03-04 14:38:37 -0800529 objServer.remove_interface(sensorInterface);
James Feist078f2322019-03-08 11:09:05 -0800530 objServer.remove_interface(association);
James Feistbc896df2018-11-26 16:28:17 -0800531}
532
Ed Tanous201a1012024-04-03 18:07:28 -0700533void ExitAirTempSensor::setupMatches()
James Feistbc896df2018-11-26 16:28:17 -0800534{
Brandon Kim66558232021-11-09 16:53:08 -0800535 constexpr const auto matchTypes{
536 std::to_array<const char*>({"power", inletTemperatureSensor})};
James Feistbc896df2018-11-26 16:28:17 -0800537
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700538 std::weak_ptr<ExitAirTempSensor> weakRef = weak_from_this();
Ed Tanous13b63f82021-05-11 16:12:52 -0700539 for (const std::string type : matchTypes)
James Feistbc896df2018-11-26 16:28:17 -0800540 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400541 setupSensorMatch(
542 matches, *dbusConnection, type,
543 [weakRef,
544 type](const double& value, sdbusplus::message_t& message) {
545 auto self = weakRef.lock();
546 if (!self)
547 {
548 return;
549 }
550 if (type == "power")
551 {
552 std::string path = message.get_path();
553 if (path.find("PS") != std::string::npos &&
554 path.ends_with("Input_Power"))
555 {
556 self->powerReadings[message.get_path()] = value;
557 }
558 }
559 else if (type == inletTemperatureSensor)
560 {
561 self->inletTemp = value;
562 }
563 self->updateReading();
564 });
565 }
566 dbusConnection->async_method_call(
567 [weakRef](boost::system::error_code ec,
568 const std::variant<double>& value) {
569 if (ec)
570 {
571 // sensor not ready yet
572 return;
573 }
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700574 auto self = weakRef.lock();
575 if (!self)
576 {
577 return;
578 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400579 self->inletTemp = std::visit(VariantToDoubleVisitor(), value);
580 },
James Feist9566bfa2019-01-29 15:31:23 -0800581 "xyz.openbmc_project.HwmonTempSensor",
582 std::string("/xyz/openbmc_project/sensors/") + inletTemperatureSensor,
James Feista5e58722019-04-22 14:43:11 -0700583 properties::interface, properties::get, sensorValueInterface, "Value");
584 dbusConnection->async_method_call(
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700585 [weakRef](boost::system::error_code ec, const GetSubTreeType& subtree) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400586 if (ec)
James Feista5e58722019-04-22 14:43:11 -0700587 {
George Liuce1abb62025-02-20 10:44:47 +0800588 lg2::error("Error contacting mapper");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400589 return;
James Feista5e58722019-04-22 14:43:11 -0700590 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400591 auto self = weakRef.lock();
592 if (!self)
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700593 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400594 return;
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700595 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400596 for (const auto& [path, matches] : subtree)
597 {
598 size_t lastSlash = path.rfind('/');
599 if (lastSlash == std::string::npos ||
600 lastSlash == path.size() || matches.empty())
601 {
602 continue;
603 }
604 std::string sensorName = path.substr(lastSlash + 1);
605 if (sensorName.starts_with("PS") &&
606 sensorName.ends_with("Input_Power"))
607 {
608 // lambda capture requires a proper variable (not a
609 // structured binding)
610 const std::string& cbPath = path;
611 self->dbusConnection->async_method_call(
612 [weakRef, cbPath](boost::system::error_code ec,
613 const std::variant<double>& value) {
614 if (ec)
615 {
George Liuce1abb62025-02-20 10:44:47 +0800616 lg2::error("Error getting value from '{PATH}'",
617 "PATH", cbPath);
Patrick Williams2aaf7172024-08-16 15:20:40 -0400618 }
619 auto self = weakRef.lock();
620 if (!self)
621 {
622 return;
623 }
624 double reading =
625 std::visit(VariantToDoubleVisitor(), value);
Alexander Hansen89be6142025-09-18 15:36:16 +0200626 lg2::debug("'{PATH}' reading '{VALUE}'", "PATH",
627 cbPath, "VALUE", reading);
Patrick Williams2aaf7172024-08-16 15:20:40 -0400628 self->powerReadings[cbPath] = reading;
629 },
630 matches[0].first, cbPath, properties::interface,
631 properties::get, sensorValueInterface, "Value");
632 }
633 }
634 },
James Feista5e58722019-04-22 14:43:11 -0700635 mapper::busName, mapper::path, mapper::interface, mapper::subtree,
636 "/xyz/openbmc_project/sensors/power", 0,
637 std::array<const char*, 1>{sensorValueInterface});
James Feistbc896df2018-11-26 16:28:17 -0800638}
639
Ed Tanous201a1012024-04-03 18:07:28 -0700640void ExitAirTempSensor::updateReading()
James Feistbc896df2018-11-26 16:28:17 -0800641{
James Feistbc896df2018-11-26 16:28:17 -0800642 double val = 0.0;
643 if (calculate(val))
644 {
James Feist18af4232019-03-13 11:14:00 -0700645 val = std::floor(val + 0.5);
James Feistbc896df2018-11-26 16:28:17 -0800646 updateValue(val);
647 }
648 else
649 {
650 updateValue(std::numeric_limits<double>::quiet_NaN());
651 }
652}
653
Ed Tanous201a1012024-04-03 18:07:28 -0700654double ExitAirTempSensor::getTotalCFM()
James Feistbc896df2018-11-26 16:28:17 -0800655{
James Feistb2eb3f52018-12-04 16:17:50 -0800656 double sum = 0;
657 for (auto& sensor : cfmSensors)
James Feistbc896df2018-11-26 16:28:17 -0800658 {
James Feistb2eb3f52018-12-04 16:17:50 -0800659 double reading = 0;
660 if (!sensor->calculate(reading))
James Feistbc896df2018-11-26 16:28:17 -0800661 {
James Feistbc896df2018-11-26 16:28:17 -0800662 return -1;
663 }
James Feistb2eb3f52018-12-04 16:17:50 -0800664 sum += reading;
James Feistbc896df2018-11-26 16:28:17 -0800665 }
James Feistb2eb3f52018-12-04 16:17:50 -0800666
667 return sum;
James Feistbc896df2018-11-26 16:28:17 -0800668}
669
670bool ExitAirTempSensor::calculate(double& val)
671{
Zhikui Ren12e3d672020-12-03 15:14:49 -0800672 constexpr size_t maxErrorPrint = 5;
James Feistbc896df2018-11-26 16:28:17 -0800673 static bool firstRead = false;
James Feistae11cfc2019-05-07 15:01:20 -0700674 static size_t errorPrint = maxErrorPrint;
675
James Feistbc896df2018-11-26 16:28:17 -0800676 double cfm = getTotalCFM();
677 if (cfm <= 0)
678 {
George Liuce1abb62025-02-20 10:44:47 +0800679 lg2::error("Error getting cfm");
James Feistbc896df2018-11-26 16:28:17 -0800680 return false;
681 }
682
Zhikui Ren12e3d672020-12-03 15:14:49 -0800683 // Though cfm is not expected to be less than qMin normally,
684 // it is not a hard limit for exit air temp calculation.
685 // 50% qMin is chosen as a generic limit between providing
686 // a valid derived exit air temp and reporting exit air temp not available.
687 constexpr const double cfmLimitFactor = 0.5;
688 if (cfm < (qMin * cfmLimitFactor))
689 {
690 if (errorPrint > 0)
691 {
692 errorPrint--;
George Liuce1abb62025-02-20 10:44:47 +0800693 lg2::error("cfm '{CFM}' is too low, expected qMin '{QMIN}'", "CFM",
694 cfm, "QMIN", qMin);
Zhikui Ren12e3d672020-12-03 15:14:49 -0800695 }
696 val = 0;
697 return false;
698 }
699
James Feistbc896df2018-11-26 16:28:17 -0800700 // if there is an error getting inlet temp, return error
701 if (std::isnan(inletTemp))
702 {
James Feistae11cfc2019-05-07 15:01:20 -0700703 if (errorPrint > 0)
704 {
705 errorPrint--;
George Liuce1abb62025-02-20 10:44:47 +0800706 lg2::error("Cannot get inlet temp");
James Feistae11cfc2019-05-07 15:01:20 -0700707 }
James Feistbc896df2018-11-26 16:28:17 -0800708 val = 0;
709 return false;
710 }
711
712 // if fans are off, just make the exit temp equal to inlet
James Feist71d31b22019-01-02 16:57:54 -0800713 if (!isPowerOn())
James Feistbc896df2018-11-26 16:28:17 -0800714 {
715 val = inletTemp;
716 return true;
717 }
718
719 double totalPower = 0;
Zev Weiss72f322f2022-08-12 18:21:01 -0700720 for (const auto& [path, reading] : powerReadings)
James Feistbc896df2018-11-26 16:28:17 -0800721 {
Zev Weiss72f322f2022-08-12 18:21:01 -0700722 if (std::isnan(reading))
James Feistbc896df2018-11-26 16:28:17 -0800723 {
724 continue;
725 }
Zev Weiss72f322f2022-08-12 18:21:01 -0700726 totalPower += reading;
James Feistbc896df2018-11-26 16:28:17 -0800727 }
728
729 // Calculate power correction factor
730 // Ci = CL + (CH - CL)/(QMax - QMin) * (CFM - QMin)
Ed Tanous8a57ec02020-10-09 12:46:52 -0700731 double powerFactor = 0.0;
James Feistbc896df2018-11-26 16:28:17 -0800732 if (cfm <= qMin)
733 {
734 powerFactor = powerFactorMin;
735 }
736 else if (cfm >= qMax)
737 {
738 powerFactor = powerFactorMax;
739 }
740 else
741 {
742 powerFactor = powerFactorMin + ((powerFactorMax - powerFactorMin) /
743 (qMax - qMin) * (cfm - qMin));
744 }
745
Ed Tanous8a57ec02020-10-09 12:46:52 -0700746 totalPower *= powerFactor;
James Feistbc896df2018-11-26 16:28:17 -0800747 totalPower += pOffset;
748
749 if (totalPower == 0)
750 {
James Feistae11cfc2019-05-07 15:01:20 -0700751 if (errorPrint > 0)
752 {
753 errorPrint--;
George Liuce1abb62025-02-20 10:44:47 +0800754 lg2::error("total power 0");
James Feistae11cfc2019-05-07 15:01:20 -0700755 }
James Feistbc896df2018-11-26 16:28:17 -0800756 val = 0;
757 return false;
758 }
759
Alexander Hansen89be6142025-09-18 15:36:16 +0200760 lg2::debug(
761 "Power Factor: {POWER_FACTOR}, Inlet Temp: {INLET_TEMP}, Total Power: {TOTAL_POWER}",
762 "POWER_FACTOR", powerFactor, "INLET_TEMP", inletTemp, "TOTAL_POWER",
763 totalPower);
James Feistbc896df2018-11-26 16:28:17 -0800764
765 // Calculate the exit air temp
766 // Texit = Tfp + (1.76 * TotalPower / CFM * Faltitude)
Ed Tanous8a57ec02020-10-09 12:46:52 -0700767 double reading = 1.76 * totalPower * altitudeFactor;
James Feistbc896df2018-11-26 16:28:17 -0800768 reading /= cfm;
769 reading += inletTemp;
770
Alexander Hansen89be6142025-09-18 15:36:16 +0200771 lg2::debug("Reading 1: '{VALUE}'", "VALUE", reading);
James Feistbc896df2018-11-26 16:28:17 -0800772
773 // Now perform the exponential average
774 // Calculate alpha based on SDR values and CFM
775 // Ai = As + (Af - As)/(QMax - QMin) * (CFM - QMin)
776
777 double alpha = 0.0;
778 if (cfm < qMin)
779 {
780 alpha = alphaS;
781 }
782 else if (cfm >= qMax)
783 {
784 alpha = alphaF;
785 }
786 else
787 {
788 alpha = alphaS + ((alphaF - alphaS) * (cfm - qMin) / (qMax - qMin));
789 }
790
Zhikui Ren12e3d672020-12-03 15:14:49 -0800791 auto time = std::chrono::steady_clock::now();
James Feistbc896df2018-11-26 16:28:17 -0800792 if (!firstRead)
793 {
794 firstRead = true;
795 lastTime = time;
796 lastReading = reading;
797 }
798 double alphaDT =
799 std::chrono::duration_cast<std::chrono::seconds>(time - lastTime)
800 .count() *
801 alpha;
802
803 // cap at 1.0 or the below fails
804 if (alphaDT > 1.0)
805 {
806 alphaDT = 1.0;
807 }
808
Alexander Hansen89be6142025-09-18 15:36:16 +0200809 lg2::debug("AlphaDT: '{ADT}'", "ADT", alphaDT);
James Feistbc896df2018-11-26 16:28:17 -0800810
811 reading = ((reading * alphaDT) + (lastReading * (1.0 - alphaDT)));
812
Alexander Hansen89be6142025-09-18 15:36:16 +0200813 lg2::debug("Reading 2: '{VALUE}'", "VALUE", reading);
James Feistbc896df2018-11-26 16:28:17 -0800814
815 val = reading;
816 lastReading = reading;
817 lastTime = time;
James Feistae11cfc2019-05-07 15:01:20 -0700818 errorPrint = maxErrorPrint;
James Feistbc896df2018-11-26 16:28:17 -0800819 return true;
820}
821
Ed Tanous201a1012024-04-03 18:07:28 -0700822void ExitAirTempSensor::checkThresholds()
James Feistbc896df2018-11-26 16:28:17 -0800823{
824 thresholds::checkThresholds(this);
825}
826
Zev Weissafd15042022-07-18 12:28:40 -0700827static void loadVariantPathArray(const SensorBaseConfigMap& data,
828 const std::string& key,
829 std::vector<std::string>& resp)
James Feistbc896df2018-11-26 16:28:17 -0800830{
831 auto it = data.find(key);
832 if (it == data.end())
833 {
George Liuce1abb62025-02-20 10:44:47 +0800834 lg2::error("Configuration missing '{KEY}'", "KEY", key);
James Feistbc896df2018-11-26 16:28:17 -0800835 throw std::invalid_argument("Key Missing");
836 }
837 BasicVariantType copy = it->second;
James Feist3eb82622019-02-08 13:10:22 -0800838 std::vector<std::string> config = std::get<std::vector<std::string>>(copy);
James Feistbc896df2018-11-26 16:28:17 -0800839 for (auto& str : config)
840 {
841 boost::replace_all(str, " ", "_");
842 }
843 resp = std::move(config);
844}
845
846void createSensor(sdbusplus::asio::object_server& objectServer,
James Feistb2eb3f52018-12-04 16:17:50 -0800847 std::shared_ptr<ExitAirTempSensor>& exitAirSensor,
James Feistbc896df2018-11-26 16:28:17 -0800848 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
849{
850 if (!dbusConnection)
851 {
George Liuce1abb62025-02-20 10:44:47 +0800852 lg2::error("Connection not created");
James Feistbc896df2018-11-26 16:28:17 -0800853 return;
854 }
James Feist655f3762020-10-05 15:28:15 -0700855 auto getter = std::make_shared<GetSensorConfiguration>(
Patrick Williams597e8422023-10-20 11:19:01 -0500856 dbusConnection, [&objectServer, &dbusConnection,
857 &exitAirSensor](const ManagedObjectType& resp) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400858 cfmSensors.clear();
859 for (const auto& [path, interfaces] : resp)
James Feistbc896df2018-11-26 16:28:17 -0800860 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400861 for (const auto& [intf, cfg] : interfaces)
James Feistbc896df2018-11-26 16:28:17 -0800862 {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400863 if (intf == configInterfaceName(exitAirType))
864 {
865 // thresholds should be under the same path
866 std::vector<thresholds::Threshold> sensorThresholds;
867 parseThresholdsFromConfig(interfaces, sensorThresholds);
James Feistbc896df2018-11-26 16:28:17 -0800868
Patrick Williams2aaf7172024-08-16 15:20:40 -0400869 std::string name =
870 loadVariant<std::string>(cfg, "Name");
871 exitAirSensor = nullptr;
872 exitAirSensor = std::make_shared<ExitAirTempSensor>(
873 dbusConnection, name, path.str, objectServer,
874 std::move(sensorThresholds));
875 exitAirSensor->powerFactorMin =
876 loadVariant<double>(cfg, "PowerFactorMin");
877 exitAirSensor->powerFactorMax =
878 loadVariant<double>(cfg, "PowerFactorMax");
879 exitAirSensor->qMin = loadVariant<double>(cfg, "QMin");
880 exitAirSensor->qMax = loadVariant<double>(cfg, "QMax");
881 exitAirSensor->alphaS =
882 loadVariant<double>(cfg, "AlphaS");
883 exitAirSensor->alphaF =
884 loadVariant<double>(cfg, "AlphaF");
885 }
886 else if (intf == configInterfaceName(cfmType))
887 {
888 // thresholds should be under the same path
889 std::vector<thresholds::Threshold> sensorThresholds;
890 parseThresholdsFromConfig(interfaces, sensorThresholds);
891 std::string name =
892 loadVariant<std::string>(cfg, "Name");
893 auto sensor = std::make_shared<CFMSensor>(
894 dbusConnection, name, path.str, objectServer,
895 std::move(sensorThresholds), exitAirSensor);
896 loadVariantPathArray(cfg, "Tachs", sensor->tachs);
897 sensor->maxCFM = loadVariant<double>(cfg, "MaxCFM");
James Feistbc896df2018-11-26 16:28:17 -0800898
Patrick Williams2aaf7172024-08-16 15:20:40 -0400899 // change these into percent upon getting the data
900 sensor->c1 = loadVariant<double>(cfg, "C1") / 100;
901 sensor->c2 = loadVariant<double>(cfg, "C2") / 100;
902 sensor->tachMinPercent =
903 loadVariant<double>(cfg, "TachMinPercent");
904 sensor->tachMaxPercent =
905 loadVariant<double>(cfg, "TachMaxPercent");
906 sensor->createMaxCFMIface();
907 sensor->setupMatches();
James Feistbc896df2018-11-26 16:28:17 -0800908
Patrick Williams2aaf7172024-08-16 15:20:40 -0400909 cfmSensors.emplace_back(std::move(sensor));
910 }
James Feistbc896df2018-11-26 16:28:17 -0800911 }
912 }
Patrick Williams2aaf7172024-08-16 15:20:40 -0400913 if (exitAirSensor)
914 {
915 exitAirSensor->setupMatches();
916 exitAirSensor->updateReading();
917 }
918 });
James Feist655f3762020-10-05 15:28:15 -0700919 getter->getConfiguration(
Zev Weiss054aad82022-08-18 01:37:34 -0700920 std::vector<std::string>(monitorTypes.begin(), monitorTypes.end()));
James Feistbc896df2018-11-26 16:28:17 -0800921}
922
James Feistb6c0b912019-07-09 12:21:44 -0700923int main()
James Feistbc896df2018-11-26 16:28:17 -0800924{
Ed Tanous1f978632023-02-28 18:16:39 -0800925 boost::asio::io_context io;
James Feistbc896df2018-11-26 16:28:17 -0800926 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
Johnathan Mantey661d4372022-10-27 09:00:59 -0700927 sdbusplus::asio::object_server objectServer(systemBus, true);
928 objectServer.add_manager("/xyz/openbmc_project/sensors");
James Feistbc896df2018-11-26 16:28:17 -0800929 systemBus->request_name("xyz.openbmc_project.ExitAirTempSensor");
James Feistbc896df2018-11-26 16:28:17 -0800930 std::shared_ptr<ExitAirTempSensor> sensor =
931 nullptr; // wait until we find the config
James Feistbc896df2018-11-26 16:28:17 -0800932
Ed Tanous83db50c2023-03-01 10:20:24 -0800933 boost::asio::post(io,
934 [&]() { createSensor(objectServer, sensor, systemBus); });
James Feistbc896df2018-11-26 16:28:17 -0800935
Ed Tanous9b4a20e2022-09-06 08:47:11 -0700936 boost::asio::steady_timer configTimer(io);
James Feistbc896df2018-11-26 16:28:17 -0800937
Patrick Williams92f8f512022-07-22 19:26:55 -0500938 std::function<void(sdbusplus::message_t&)> eventHandler =
939 [&](sdbusplus::message_t&) {
Patrick Williams2aaf7172024-08-16 15:20:40 -0400940 configTimer.expires_after(std::chrono::seconds(1));
941 // create a timer because normally multiple properties change
942 configTimer.async_wait([&](const boost::system::error_code& ec) {
943 if (ec == boost::asio::error::operation_aborted)
944 {
945 return; // we're being canceled
946 }
947 createSensor(objectServer, sensor, systemBus);
948 if (!sensor)
949 {
George Liuce1abb62025-02-20 10:44:47 +0800950 lg2::info("Configuration not detected");
Patrick Williams2aaf7172024-08-16 15:20:40 -0400951 }
952 });
953 };
Zev Weiss214d9712022-08-12 12:54:31 -0700954 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
Zev Weiss054aad82022-08-18 01:37:34 -0700955 setupPropertiesChangedMatches(*systemBus, monitorTypes, eventHandler);
James Feistbc896df2018-11-26 16:28:17 -0800956
Bruce Lee913d4d02021-07-22 10:18:42 +0800957 setupManufacturingModeMatch(*systemBus);
James Feistbc896df2018-11-26 16:28:17 -0800958 io.run();
Zhikui Ren8685b172021-06-29 15:16:52 -0700959 return 0;
James Feistbc896df2018-11-26 16:28:17 -0800960}