blob: 0797bf0dc7d4773cbd2bc4b98e009501f00ba96a [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
19#include "Utils.hpp"
20#include "VariantVisitors.hpp"
21
James Feistbc896df2018-11-26 16:28:17 -080022#include <boost/algorithm/string/replace.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070023#include <boost/container/flat_map.hpp>
James Feist38fb5982020-05-28 10:09:54 -070024#include <sdbusplus/asio/connection.hpp>
25#include <sdbusplus/asio/object_server.hpp>
26#include <sdbusplus/bus/match.hpp>
27
28#include <array>
James Feistbc896df2018-11-26 16:28:17 -080029#include <chrono>
Patrick Venture96e97db2019-10-31 13:44:38 -070030#include <cmath>
31#include <functional>
James Feistbc896df2018-11-26 16:28:17 -080032#include <iostream>
33#include <limits>
Patrick Venture96e97db2019-10-31 13:44:38 -070034#include <memory>
James Feistbc896df2018-11-26 16:28:17 -080035#include <numeric>
Patrick Venture96e97db2019-10-31 13:44:38 -070036#include <stdexcept>
37#include <utility>
38#include <variant>
James Feistbc896df2018-11-26 16:28:17 -080039#include <vector>
40
Ed Tanous8a57ec02020-10-09 12:46:52 -070041constexpr const double altitudeFactor = 1.14;
Zev Weiss054aad82022-08-18 01:37:34 -070042constexpr const char* exitAirType = "ExitAirTempSensor";
43constexpr const char* cfmType = "CFMSensor";
James Feistbc896df2018-11-26 16:28:17 -080044
45// todo: this *might* need to be configurable
46constexpr const char* inletTemperatureSensor = "temperature/Front_Panel_Temp";
James Feist13452092019-03-07 16:38:12 -080047constexpr const char* pidConfigurationType =
48 "xyz.openbmc_project.Configuration.Pid";
49constexpr const char* settingsDaemon = "xyz.openbmc_project.Settings";
50constexpr const char* cfmSettingPath = "/xyz/openbmc_project/control/cfm_limit";
51constexpr const char* cfmSettingIface = "xyz.openbmc_project.Control.CFMLimit";
James Feistbc896df2018-11-26 16:28:17 -080052
Ed Tanous8a57ec02020-10-09 12:46:52 -070053static constexpr bool debug = false;
James Feistbc896df2018-11-26 16:28:17 -080054
James Feistb2eb3f52018-12-04 16:17:50 -080055static constexpr double cfmMaxReading = 255;
56static constexpr double cfmMinReading = 0;
57
James Feist13452092019-03-07 16:38:12 -080058static constexpr size_t minSystemCfm = 50;
59
Zev Weiss054aad82022-08-18 01:37:34 -070060constexpr const auto monitorTypes{
61 std::to_array<const char*>({exitAirType, cfmType})};
James Feist655f3762020-10-05 15:28:15 -070062
James Feist9a25ed42019-10-15 15:43:44 -070063static std::vector<std::shared_ptr<CFMSensor>> cfmSensors;
64
James Feistb2eb3f52018-12-04 16:17:50 -080065static void setupSensorMatch(
Patrick Williams92f8f512022-07-22 19:26:55 -050066 std::vector<sdbusplus::bus::match_t>& matches, sdbusplus::bus_t& connection,
67 const std::string& type,
68 std::function<void(const double&, sdbusplus::message_t&)>&& callback)
James Feistb2eb3f52018-12-04 16:17:50 -080069{
Patrick Williams92f8f512022-07-22 19:26:55 -050070 std::function<void(sdbusplus::message_t & message)> eventHandler =
71 [callback{std::move(callback)}](sdbusplus::message_t& message) {
Ed Tanousbb679322022-05-16 16:10:00 -070072 std::string objectName;
73 boost::container::flat_map<std::string, std::variant<double, int64_t>>
74 values;
75 message.read(objectName, values);
76 auto findValue = values.find("Value");
77 if (findValue == values.end())
78 {
79 return;
80 }
81 double value = std::visit(VariantToDoubleVisitor(), findValue->second);
82 if (std::isnan(value))
83 {
84 return;
85 }
James Feist9566bfa2019-01-29 15:31:23 -080086
Ed Tanousbb679322022-05-16 16:10:00 -070087 callback(value, message);
88 };
James Feistb2eb3f52018-12-04 16:17:50 -080089 matches.emplace_back(connection,
90 "type='signal',"
91 "member='PropertiesChanged',interface='org."
92 "freedesktop.DBus.Properties',path_"
93 "namespace='/xyz/openbmc_project/sensors/" +
94 std::string(type) +
95 "',arg0='xyz.openbmc_project.Sensor.Value'",
96 std::move(eventHandler));
97}
98
James Feist13452092019-03-07 16:38:12 -080099static void setMaxPWM(const std::shared_ptr<sdbusplus::asio::connection>& conn,
100 double value)
101{
102 using GetSubTreeType = std::vector<std::pair<
103 std::string,
104 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
105
106 conn->async_method_call(
107 [conn, value](const boost::system::error_code ec,
108 const GetSubTreeType& ret) {
Ed Tanousbb679322022-05-16 16:10:00 -0700109 if (ec)
110 {
111 std::cerr << "Error calling mapper\n";
112 return;
113 }
114 for (const auto& [path, objDict] : ret)
115 {
116 if (objDict.empty())
James Feist13452092019-03-07 16:38:12 -0800117 {
James Feist13452092019-03-07 16:38:12 -0800118 return;
119 }
Ed Tanousbb679322022-05-16 16:10:00 -0700120 const std::string& owner = objDict.begin()->first;
121
122 conn->async_method_call(
123 [conn, value, owner,
124 path{path}](const boost::system::error_code ec,
125 const std::variant<std::string>& classType) {
126 if (ec)
127 {
128 std::cerr << "Error getting pid class\n";
129 return;
130 }
Ed Tanous2049bd22022-07-09 07:20:26 -0700131 const auto* classStr = std::get_if<std::string>(&classType);
Ed Tanousbb679322022-05-16 16:10:00 -0700132 if (classStr == nullptr || *classStr != "fan")
James Feist13452092019-03-07 16:38:12 -0800133 {
134 return;
135 }
James Feist13452092019-03-07 16:38:12 -0800136 conn->async_method_call(
Ed Tanousbb679322022-05-16 16:10:00 -0700137 [](boost::system::error_code& ec) {
138 if (ec)
139 {
140 std::cerr << "Error setting pid class\n";
141 return;
142 }
James Feist13452092019-03-07 16:38:12 -0800143 },
Ed Tanousbb679322022-05-16 16:10:00 -0700144 owner, path, "org.freedesktop.DBus.Properties", "Set",
145 pidConfigurationType, "OutLimitMax",
146 std::variant<double>(value));
147 },
148 owner, path, "org.freedesktop.DBus.Properties", "Get",
149 pidConfigurationType, "Class");
150 }
James Feist13452092019-03-07 16:38:12 -0800151 },
James Feista5e58722019-04-22 14:43:11 -0700152 mapper::busName, mapper::path, mapper::interface, mapper::subtree, "/",
153 0, std::array<std::string, 1>{pidConfigurationType});
James Feist13452092019-03-07 16:38:12 -0800154}
155
James Feistb2eb3f52018-12-04 16:17:50 -0800156CFMSensor::CFMSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
157 const std::string& sensorName,
158 const std::string& sensorConfiguration,
159 sdbusplus::asio::object_server& objectServer,
James Feistb839c052019-05-15 10:25:24 -0700160 std::vector<thresholds::Threshold>&& thresholdData,
James Feistb2eb3f52018-12-04 16:17:50 -0800161 std::shared_ptr<ExitAirTempSensor>& parent) :
Zhikui Renda98f092021-11-01 09:41:08 -0700162 Sensor(escapeName(sensorName), std::move(thresholdData),
Zev Weiss054aad82022-08-18 01:37:34 -0700163 sensorConfiguration, "CFMSensor", false, false, cfmMaxReading,
164 cfmMinReading, conn, PowerState::on),
Ed Tanous2049bd22022-07-09 07:20:26 -0700165 parent(parent), objServer(objectServer)
James Feistb2eb3f52018-12-04 16:17:50 -0800166{
Basheer Ahmed Muddebihale5b867b2021-07-26 08:32:19 -0700167 sensorInterface = objectServer.add_interface(
168 "/xyz/openbmc_project/sensors/airflow/" + name,
169 "xyz.openbmc_project.Sensor.Value");
James Feistb2eb3f52018-12-04 16:17:50 -0800170
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530171 for (const auto& threshold : thresholds)
James Feistb2eb3f52018-12-04 16:17:50 -0800172 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530173 std::string interface = thresholds::getInterface(threshold.level);
174 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
175 objectServer.add_interface(
176 "/xyz/openbmc_project/sensors/airflow/" + name, interface);
James Feistb2eb3f52018-12-04 16:17:50 -0800177 }
James Feist078f2322019-03-08 11:09:05 -0800178
179 association = objectServer.add_interface(
Basheer Ahmed Muddebihale5b867b2021-07-26 08:32:19 -0700180 "/xyz/openbmc_project/sensors/airflow/" + name, association::interface);
James Feist078f2322019-03-08 11:09:05 -0800181
Andrei Kartashev39287412022-02-04 16:04:47 +0300182 setInitialProperties(sensor_paths::unitCFM);
James Feist9a25ed42019-10-15 15:43:44 -0700183
James Feist13452092019-03-07 16:38:12 -0800184 pwmLimitIface =
185 objectServer.add_interface("/xyz/openbmc_project/control/pwm_limit",
186 "xyz.openbmc_project.Control.PWMLimit");
187 cfmLimitIface =
188 objectServer.add_interface("/xyz/openbmc_project/control/MaxCFM",
189 "xyz.openbmc_project.Control.CFMLimit");
James Feist9a25ed42019-10-15 15:43:44 -0700190}
James Feist13452092019-03-07 16:38:12 -0800191
James Feist9a25ed42019-10-15 15:43:44 -0700192void CFMSensor::setupMatches()
193{
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700194 std::weak_ptr<CFMSensor> weakRef = weak_from_this();
Ed Tanous8a17c302021-09-02 15:07:11 -0700195 setupSensorMatch(
196 matches, *dbusConnection, "fan_tach",
Patrick Williams92f8f512022-07-22 19:26:55 -0500197 [weakRef](const double& value, sdbusplus::message_t& message) {
Ed Tanousbb679322022-05-16 16:10:00 -0700198 auto self = weakRef.lock();
199 if (!self)
200 {
201 return;
202 }
203 self->tachReadings[message.get_path()] = value;
204 if (self->tachRanges.find(message.get_path()) == self->tachRanges.end())
205 {
206 // calls update reading after updating ranges
207 self->addTachRanges(message.get_sender(), message.get_path());
208 }
209 else
210 {
211 self->updateReading();
212 }
Ed Tanous8a17c302021-09-02 15:07:11 -0700213 });
James Feist9a25ed42019-10-15 15:43:44 -0700214
215 dbusConnection->async_method_call(
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700216 [weakRef](const boost::system::error_code ec,
217 const std::variant<double> cfmVariant) {
Ed Tanousbb679322022-05-16 16:10:00 -0700218 auto self = weakRef.lock();
219 if (!self)
220 {
221 return;
222 }
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700223
Ed Tanousbb679322022-05-16 16:10:00 -0700224 uint64_t maxRpm = 100;
225 if (!ec)
226 {
Ed Tanous2049bd22022-07-09 07:20:26 -0700227 const auto* cfm = std::get_if<double>(&cfmVariant);
Ed Tanousbb679322022-05-16 16:10:00 -0700228 if (cfm != nullptr && *cfm >= minSystemCfm)
229 {
230 maxRpm = self->getMaxRpm(*cfm);
James Feist13452092019-03-07 16:38:12 -0800231 }
Ed Tanousbb679322022-05-16 16:10:00 -0700232 }
233 self->pwmLimitIface->register_property("Limit", maxRpm);
234 self->pwmLimitIface->initialize();
235 setMaxPWM(self->dbusConnection, maxRpm);
James Feist13452092019-03-07 16:38:12 -0800236 },
237 settingsDaemon, cfmSettingPath, "org.freedesktop.DBus.Properties",
238 "Get", cfmSettingIface, "Limit");
239
Ed Tanousbb679322022-05-16 16:10:00 -0700240 matches.emplace_back(*dbusConnection,
241 "type='signal',"
242 "member='PropertiesChanged',interface='org."
243 "freedesktop.DBus.Properties',path='" +
244 std::string(cfmSettingPath) + "',arg0='" +
245 std::string(cfmSettingIface) + "'",
Patrick Williams92f8f512022-07-22 19:26:55 -0500246 [weakRef](sdbusplus::message_t& message) {
Ed Tanousbb679322022-05-16 16:10:00 -0700247 auto self = weakRef.lock();
248 if (!self)
249 {
250 return;
251 }
252 boost::container::flat_map<std::string, std::variant<double>> values;
253 std::string objectName;
254 message.read(objectName, values);
255 const auto findValue = values.find("Limit");
256 if (findValue == values.end())
257 {
258 return;
259 }
Ed Tanous2049bd22022-07-09 07:20:26 -0700260 auto* const reading = std::get_if<double>(&(findValue->second));
Ed Tanousbb679322022-05-16 16:10:00 -0700261 if (reading == nullptr)
262 {
263 std::cerr << "Got CFM Limit of wrong type\n";
264 return;
265 }
266 if (*reading < minSystemCfm && *reading != 0)
267 {
268 std::cerr << "Illegal CFM setting detected\n";
269 return;
270 }
271 uint64_t maxRpm = self->getMaxRpm(*reading);
272 self->pwmLimitIface->set_property("Limit", maxRpm);
273 setMaxPWM(self->dbusConnection, maxRpm);
274 });
James Feistb2eb3f52018-12-04 16:17:50 -0800275}
276
James Feist9566bfa2019-01-29 15:31:23 -0800277CFMSensor::~CFMSensor()
278{
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530279 for (const auto& iface : thresholdInterfaces)
280 {
281 objServer.remove_interface(iface);
282 }
James Feist9566bfa2019-01-29 15:31:23 -0800283 objServer.remove_interface(sensorInterface);
James Feist078f2322019-03-08 11:09:05 -0800284 objServer.remove_interface(association);
James Feist13452092019-03-07 16:38:12 -0800285 objServer.remove_interface(cfmLimitIface);
286 objServer.remove_interface(pwmLimitIface);
287}
288
289void CFMSensor::createMaxCFMIface(void)
290{
James Feistb6c0b912019-07-09 12:21:44 -0700291 cfmLimitIface->register_property("Limit", c2 * maxCFM * tachs.size());
James Feist13452092019-03-07 16:38:12 -0800292 cfmLimitIface->initialize();
James Feist9566bfa2019-01-29 15:31:23 -0800293}
294
James Feistb2eb3f52018-12-04 16:17:50 -0800295void CFMSensor::addTachRanges(const std::string& serviceName,
296 const std::string& path)
297{
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700298 std::weak_ptr<CFMSensor> weakRef = weak_from_this();
James Feistb2eb3f52018-12-04 16:17:50 -0800299 dbusConnection->async_method_call(
Zev Weissafd15042022-07-18 12:28:40 -0700300 [weakRef, path](const boost::system::error_code ec,
301 const SensorBaseConfigMap& data) {
Ed Tanousbb679322022-05-16 16:10:00 -0700302 if (ec)
303 {
304 std::cerr << "Error getting properties from " << path << "\n";
305 return;
306 }
307 auto self = weakRef.lock();
308 if (!self)
309 {
310 return;
311 }
312 double max = loadVariant<double>(data, "MaxValue");
313 double min = loadVariant<double>(data, "MinValue");
314 self->tachRanges[path] = std::make_pair(min, max);
315 self->updateReading();
James Feistb2eb3f52018-12-04 16:17:50 -0800316 },
317 serviceName, path, "org.freedesktop.DBus.Properties", "GetAll",
318 "xyz.openbmc_project.Sensor.Value");
319}
320
321void CFMSensor::checkThresholds(void)
322{
323 thresholds::checkThresholds(this);
324}
325
326void CFMSensor::updateReading(void)
327{
328 double val = 0.0;
329 if (calculate(val))
330 {
331 if (value != val && parent)
332 {
333 parent->updateReading();
334 }
335 updateValue(val);
336 }
337 else
338 {
339 updateValue(std::numeric_limits<double>::quiet_NaN());
340 }
341}
342
Ed Tanous2049bd22022-07-09 07:20:26 -0700343uint64_t CFMSensor::getMaxRpm(uint64_t cfmMaxSetting) const
James Feist13452092019-03-07 16:38:12 -0800344{
345 uint64_t pwmPercent = 100;
346 double totalCFM = std::numeric_limits<double>::max();
347 if (cfmMaxSetting == 0)
348 {
349 return pwmPercent;
350 }
351
James Feist52427952019-04-05 14:23:35 -0700352 bool firstLoop = true;
James Feist13452092019-03-07 16:38:12 -0800353 while (totalCFM > cfmMaxSetting)
354 {
James Feist52427952019-04-05 14:23:35 -0700355 if (firstLoop)
356 {
357 firstLoop = false;
358 }
359 else
360 {
361 pwmPercent--;
362 }
363
James Feist13452092019-03-07 16:38:12 -0800364 double ci = 0;
365 if (pwmPercent == 0)
366 {
367 ci = 0;
368 }
369 else if (pwmPercent < tachMinPercent)
370 {
371 ci = c1;
372 }
373 else if (pwmPercent > tachMaxPercent)
374 {
375 ci = c2;
376 }
377 else
378 {
379 ci = c1 + (((c2 - c1) * (pwmPercent - tachMinPercent)) /
380 (tachMaxPercent - tachMinPercent));
381 }
382
383 // Now calculate the CFM for this tach
384 // CFMi = Ci * Qmaxi * TACHi
385 totalCFM = ci * maxCFM * pwmPercent;
386 totalCFM *= tachs.size();
387 // divide by 100 since pwm is in percent
388 totalCFM /= 100;
389
James Feist13452092019-03-07 16:38:12 -0800390 if (pwmPercent <= 0)
391 {
392 break;
393 }
394 }
James Feist52427952019-04-05 14:23:35 -0700395
James Feist13452092019-03-07 16:38:12 -0800396 return pwmPercent;
397}
398
James Feistb2eb3f52018-12-04 16:17:50 -0800399bool CFMSensor::calculate(double& value)
400{
401 double totalCFM = 0;
402 for (const std::string& tachName : tachs)
403 {
404 auto findReading = std::find_if(
Zev Weiss6c106d62022-08-17 20:50:00 -0700405 tachReadings.begin(), tachReadings.end(),
406 [&](const auto& item) { return item.first.ends_with(tachName); });
Ed Tanousbb679322022-05-16 16:10:00 -0700407 auto findRange = std::find_if(tachRanges.begin(), tachRanges.end(),
408 [&](const auto& item) {
Zev Weiss6c106d62022-08-17 20:50:00 -0700409 return item.first.ends_with(tachName);
Ed Tanousbb679322022-05-16 16:10:00 -0700410 });
James Feistb2eb3f52018-12-04 16:17:50 -0800411 if (findReading == tachReadings.end())
412 {
Ed Tanous8a57ec02020-10-09 12:46:52 -0700413 if constexpr (debug)
James Feista96329f2019-01-24 10:08:27 -0800414 {
415 std::cerr << "Can't find " << tachName << "in readings\n";
416 }
James Feist9566bfa2019-01-29 15:31:23 -0800417 continue; // haven't gotten a reading
James Feistb2eb3f52018-12-04 16:17:50 -0800418 }
419
420 if (findRange == tachRanges.end())
421 {
James Feist523828e2019-03-04 14:38:37 -0800422 std::cerr << "Can't find " << tachName << " in ranges\n";
James Feistb2eb3f52018-12-04 16:17:50 -0800423 return false; // haven't gotten a max / min
424 }
425
426 // avoid divide by 0
427 if (findRange->second.second == 0)
428 {
429 std::cerr << "Tach Max Set to 0 " << tachName << "\n";
430 return false;
431 }
432
433 double rpm = findReading->second;
434
435 // for now assume the min for a fan is always 0, divide by max to get
436 // percent and mult by 100
437 rpm /= findRange->second.second;
438 rpm *= 100;
439
Ed Tanous8a57ec02020-10-09 12:46:52 -0700440 if constexpr (debug)
James Feistb2eb3f52018-12-04 16:17:50 -0800441 {
442 std::cout << "Tach " << tachName << "at " << rpm << "\n";
443 }
444
445 // Do a linear interpolation to get Ci
446 // Ci = C1 + (C2 - C1)/(RPM2 - RPM1) * (TACHi - TACH1)
447
448 double ci = 0;
449 if (rpm == 0)
450 {
451 ci = 0;
452 }
453 else if (rpm < tachMinPercent)
454 {
455 ci = c1;
456 }
457 else if (rpm > tachMaxPercent)
458 {
459 ci = c2;
460 }
461 else
462 {
463 ci = c1 + (((c2 - c1) * (rpm - tachMinPercent)) /
464 (tachMaxPercent - tachMinPercent));
465 }
466
467 // Now calculate the CFM for this tach
468 // CFMi = Ci * Qmaxi * TACHi
469 totalCFM += ci * maxCFM * rpm;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700470 if constexpr (debug)
James Feista5e58722019-04-22 14:43:11 -0700471 {
472 std::cerr << "totalCFM = " << totalCFM << "\n";
473 std::cerr << "Ci " << ci << " MaxCFM " << maxCFM << " rpm " << rpm
474 << "\n";
475 std::cerr << "c1 " << c1 << " c2 " << c2 << " max "
476 << tachMaxPercent << " min " << tachMinPercent << "\n";
477 }
James Feistb2eb3f52018-12-04 16:17:50 -0800478 }
479
480 // divide by 100 since rpm is in percent
481 value = totalCFM / 100;
Ed Tanous8a57ec02020-10-09 12:46:52 -0700482 if constexpr (debug)
James Feista5e58722019-04-22 14:43:11 -0700483 {
484 std::cerr << "cfm value = " << value << "\n";
485 }
James Feist9566bfa2019-01-29 15:31:23 -0800486 return true;
James Feistb2eb3f52018-12-04 16:17:50 -0800487}
488
489static constexpr double exitAirMaxReading = 127;
490static constexpr double exitAirMinReading = -128;
James Feistbc896df2018-11-26 16:28:17 -0800491ExitAirTempSensor::ExitAirTempSensor(
492 std::shared_ptr<sdbusplus::asio::connection>& conn,
James Feistb2eb3f52018-12-04 16:17:50 -0800493 const std::string& sensorName, const std::string& sensorConfiguration,
James Feistbc896df2018-11-26 16:28:17 -0800494 sdbusplus::asio::object_server& objectServer,
James Feistb839c052019-05-15 10:25:24 -0700495 std::vector<thresholds::Threshold>&& thresholdData) :
Zhikui Renda98f092021-11-01 09:41:08 -0700496 Sensor(escapeName(sensorName), std::move(thresholdData),
Zev Weiss054aad82022-08-18 01:37:34 -0700497 sensorConfiguration, "ExitAirTemp", false, false, exitAirMaxReading,
498 exitAirMinReading, conn, PowerState::on),
Ed Tanous2049bd22022-07-09 07:20:26 -0700499 objServer(objectServer)
James Feistbc896df2018-11-26 16:28:17 -0800500{
501 sensorInterface = objectServer.add_interface(
502 "/xyz/openbmc_project/sensors/temperature/" + name,
503 "xyz.openbmc_project.Sensor.Value");
504
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530505 for (const auto& threshold : thresholds)
James Feistbc896df2018-11-26 16:28:17 -0800506 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530507 std::string interface = thresholds::getInterface(threshold.level);
508 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
509 objectServer.add_interface(
510 "/xyz/openbmc_project/sensors/temperature/" + name, interface);
James Feistbc896df2018-11-26 16:28:17 -0800511 }
James Feist078f2322019-03-08 11:09:05 -0800512 association = objectServer.add_interface(
513 "/xyz/openbmc_project/sensors/temperature/" + name,
James Feist2adc95c2019-09-30 14:55:28 -0700514 association::interface);
Andrei Kartashev39287412022-02-04 16:04:47 +0300515 setInitialProperties(sensor_paths::unitDegreesC);
James Feistbc896df2018-11-26 16:28:17 -0800516}
517
518ExitAirTempSensor::~ExitAirTempSensor()
519{
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530520 for (const auto& iface : thresholdInterfaces)
521 {
522 objServer.remove_interface(iface);
523 }
James Feist523828e2019-03-04 14:38:37 -0800524 objServer.remove_interface(sensorInterface);
James Feist078f2322019-03-08 11:09:05 -0800525 objServer.remove_interface(association);
James Feistbc896df2018-11-26 16:28:17 -0800526}
527
528void ExitAirTempSensor::setupMatches(void)
529{
Brandon Kim66558232021-11-09 16:53:08 -0800530 constexpr const auto matchTypes{
531 std::to_array<const char*>({"power", inletTemperatureSensor})};
James Feistbc896df2018-11-26 16:28:17 -0800532
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700533 std::weak_ptr<ExitAirTempSensor> weakRef = weak_from_this();
Ed Tanous13b63f82021-05-11 16:12:52 -0700534 for (const std::string type : matchTypes)
James Feistbc896df2018-11-26 16:28:17 -0800535 {
James Feistb2eb3f52018-12-04 16:17:50 -0800536 setupSensorMatch(matches, *dbusConnection, type,
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700537 [weakRef, type](const double& value,
Patrick Williams92f8f512022-07-22 19:26:55 -0500538 sdbusplus::message_t& message) {
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700539 auto self = weakRef.lock();
540 if (!self)
541 {
542 return;
543 }
Ed Tanousbb679322022-05-16 16:10:00 -0700544 if (type == "power")
545 {
546 std::string path = message.get_path();
547 if (path.find("PS") != std::string::npos &&
Zev Weiss6c106d62022-08-17 20:50:00 -0700548 path.ends_with("Input_Power"))
Ed Tanousbb679322022-05-16 16:10:00 -0700549 {
550 self->powerReadings[message.get_path()] = value;
551 }
552 }
553 else if (type == inletTemperatureSensor)
554 {
555 self->inletTemp = value;
556 }
557 self->updateReading();
558 });
559 }
560 dbusConnection->async_method_call(
561 [weakRef](boost::system::error_code ec,
562 const std::variant<double>& value) {
563 if (ec)
564 {
565 // sensor not ready yet
566 return;
567 }
568 auto self = weakRef.lock();
569 if (!self)
570 {
571 return;
572 }
573 self->inletTemp = std::visit(VariantToDoubleVisitor(), value);
James Feist9566bfa2019-01-29 15:31:23 -0800574 },
575 "xyz.openbmc_project.HwmonTempSensor",
576 std::string("/xyz/openbmc_project/sensors/") + inletTemperatureSensor,
James Feista5e58722019-04-22 14:43:11 -0700577 properties::interface, properties::get, sensorValueInterface, "Value");
578 dbusConnection->async_method_call(
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700579 [weakRef](boost::system::error_code ec, const GetSubTreeType& subtree) {
Ed Tanousbb679322022-05-16 16:10:00 -0700580 if (ec)
581 {
582 std::cerr << "Error contacting mapper\n";
583 return;
584 }
585 auto self = weakRef.lock();
586 if (!self)
587 {
588 return;
589 }
Zev Weiss72f322f2022-08-12 18:21:01 -0700590 for (const auto& [path, matches] : subtree)
Ed Tanousbb679322022-05-16 16:10:00 -0700591 {
Zev Weiss72f322f2022-08-12 18:21:01 -0700592 size_t lastSlash = path.rfind('/');
593 if (lastSlash == std::string::npos || lastSlash == path.size() ||
594 matches.empty())
James Feista5e58722019-04-22 14:43:11 -0700595 {
Ed Tanousbb679322022-05-16 16:10:00 -0700596 continue;
James Feista5e58722019-04-22 14:43:11 -0700597 }
Zev Weiss72f322f2022-08-12 18:21:01 -0700598 std::string sensorName = path.substr(lastSlash + 1);
Zev Weiss6c106d62022-08-17 20:50:00 -0700599 if (sensorName.starts_with("PS") &&
600 sensorName.ends_with("Input_Power"))
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700601 {
Zev Weiss72f322f2022-08-12 18:21:01 -0700602 // lambda capture requires a proper variable (not a structured
603 // binding)
604 const std::string& cbPath = path;
Ed Tanousbb679322022-05-16 16:10:00 -0700605 self->dbusConnection->async_method_call(
Zev Weiss72f322f2022-08-12 18:21:01 -0700606 [weakRef, cbPath](boost::system::error_code ec,
607 const std::variant<double>& value) {
Ed Tanousbb679322022-05-16 16:10:00 -0700608 if (ec)
609 {
Zev Weiss72f322f2022-08-12 18:21:01 -0700610 std::cerr << "Error getting value from " << cbPath
Ed Tanousbb679322022-05-16 16:10:00 -0700611 << "\n";
612 }
613 auto self = weakRef.lock();
614 if (!self)
615 {
616 return;
617 }
Patrick Williams779c96a2023-05-10 07:50:42 -0500618 double reading = std::visit(VariantToDoubleVisitor(),
619 value);
Ed Tanousbb679322022-05-16 16:10:00 -0700620 if constexpr (debug)
621 {
Zev Weiss72f322f2022-08-12 18:21:01 -0700622 std::cerr << cbPath << "Reading " << reading << "\n";
Ed Tanousbb679322022-05-16 16:10:00 -0700623 }
Zev Weiss72f322f2022-08-12 18:21:01 -0700624 self->powerReadings[cbPath] = reading;
Ed Tanousbb679322022-05-16 16:10:00 -0700625 },
Zev Weiss72f322f2022-08-12 18:21:01 -0700626 matches[0].first, cbPath, properties::interface,
Ed Tanousbb679322022-05-16 16:10:00 -0700627 properties::get, sensorValueInterface, "Value");
Zhikui Rendbb73aa2021-04-02 13:39:04 -0700628 }
Ed Tanousbb679322022-05-16 16:10:00 -0700629 }
James Feista5e58722019-04-22 14:43:11 -0700630 },
631 mapper::busName, mapper::path, mapper::interface, mapper::subtree,
632 "/xyz/openbmc_project/sensors/power", 0,
633 std::array<const char*, 1>{sensorValueInterface});
James Feistbc896df2018-11-26 16:28:17 -0800634}
635
636void ExitAirTempSensor::updateReading(void)
637{
James Feistbc896df2018-11-26 16:28:17 -0800638 double val = 0.0;
639 if (calculate(val))
640 {
James Feist18af4232019-03-13 11:14:00 -0700641 val = std::floor(val + 0.5);
James Feistbc896df2018-11-26 16:28:17 -0800642 updateValue(val);
643 }
644 else
645 {
646 updateValue(std::numeric_limits<double>::quiet_NaN());
647 }
648}
649
James Feistb2eb3f52018-12-04 16:17:50 -0800650double ExitAirTempSensor::getTotalCFM(void)
James Feistbc896df2018-11-26 16:28:17 -0800651{
James Feistb2eb3f52018-12-04 16:17:50 -0800652 double sum = 0;
653 for (auto& sensor : cfmSensors)
James Feistbc896df2018-11-26 16:28:17 -0800654 {
James Feistb2eb3f52018-12-04 16:17:50 -0800655 double reading = 0;
656 if (!sensor->calculate(reading))
James Feistbc896df2018-11-26 16:28:17 -0800657 {
James Feistbc896df2018-11-26 16:28:17 -0800658 return -1;
659 }
James Feistb2eb3f52018-12-04 16:17:50 -0800660 sum += reading;
James Feistbc896df2018-11-26 16:28:17 -0800661 }
James Feistb2eb3f52018-12-04 16:17:50 -0800662
663 return sum;
James Feistbc896df2018-11-26 16:28:17 -0800664}
665
666bool ExitAirTempSensor::calculate(double& val)
667{
Zhikui Ren12e3d672020-12-03 15:14:49 -0800668 constexpr size_t maxErrorPrint = 5;
James Feistbc896df2018-11-26 16:28:17 -0800669 static bool firstRead = false;
James Feistae11cfc2019-05-07 15:01:20 -0700670 static size_t errorPrint = maxErrorPrint;
671
James Feistbc896df2018-11-26 16:28:17 -0800672 double cfm = getTotalCFM();
673 if (cfm <= 0)
674 {
675 std::cerr << "Error getting cfm\n";
676 return false;
677 }
678
Zhikui Ren12e3d672020-12-03 15:14:49 -0800679 // Though cfm is not expected to be less than qMin normally,
680 // it is not a hard limit for exit air temp calculation.
681 // 50% qMin is chosen as a generic limit between providing
682 // a valid derived exit air temp and reporting exit air temp not available.
683 constexpr const double cfmLimitFactor = 0.5;
684 if (cfm < (qMin * cfmLimitFactor))
685 {
686 if (errorPrint > 0)
687 {
688 errorPrint--;
689 std::cerr << "cfm " << cfm << " is too low, expected qMin " << qMin
690 << "\n";
691 }
692 val = 0;
693 return false;
694 }
695
James Feistbc896df2018-11-26 16:28:17 -0800696 // if there is an error getting inlet temp, return error
697 if (std::isnan(inletTemp))
698 {
James Feistae11cfc2019-05-07 15:01:20 -0700699 if (errorPrint > 0)
700 {
701 errorPrint--;
702 std::cerr << "Cannot get inlet temp\n";
703 }
James Feistbc896df2018-11-26 16:28:17 -0800704 val = 0;
705 return false;
706 }
707
708 // if fans are off, just make the exit temp equal to inlet
James Feist71d31b22019-01-02 16:57:54 -0800709 if (!isPowerOn())
James Feistbc896df2018-11-26 16:28:17 -0800710 {
711 val = inletTemp;
712 return true;
713 }
714
715 double totalPower = 0;
Zev Weiss72f322f2022-08-12 18:21:01 -0700716 for (const auto& [path, reading] : powerReadings)
James Feistbc896df2018-11-26 16:28:17 -0800717 {
Zev Weiss72f322f2022-08-12 18:21:01 -0700718 if (std::isnan(reading))
James Feistbc896df2018-11-26 16:28:17 -0800719 {
720 continue;
721 }
Zev Weiss72f322f2022-08-12 18:21:01 -0700722 totalPower += reading;
James Feistbc896df2018-11-26 16:28:17 -0800723 }
724
725 // Calculate power correction factor
726 // Ci = CL + (CH - CL)/(QMax - QMin) * (CFM - QMin)
Ed Tanous8a57ec02020-10-09 12:46:52 -0700727 double powerFactor = 0.0;
James Feistbc896df2018-11-26 16:28:17 -0800728 if (cfm <= qMin)
729 {
730 powerFactor = powerFactorMin;
731 }
732 else if (cfm >= qMax)
733 {
734 powerFactor = powerFactorMax;
735 }
736 else
737 {
738 powerFactor = powerFactorMin + ((powerFactorMax - powerFactorMin) /
739 (qMax - qMin) * (cfm - qMin));
740 }
741
Ed Tanous8a57ec02020-10-09 12:46:52 -0700742 totalPower *= powerFactor;
James Feistbc896df2018-11-26 16:28:17 -0800743 totalPower += pOffset;
744
745 if (totalPower == 0)
746 {
James Feistae11cfc2019-05-07 15:01:20 -0700747 if (errorPrint > 0)
748 {
749 errorPrint--;
750 std::cerr << "total power 0\n";
751 }
James Feistbc896df2018-11-26 16:28:17 -0800752 val = 0;
753 return false;
754 }
755
Ed Tanous8a57ec02020-10-09 12:46:52 -0700756 if constexpr (debug)
James Feistbc896df2018-11-26 16:28:17 -0800757 {
758 std::cout << "Power Factor " << powerFactor << "\n";
759 std::cout << "Inlet Temp " << inletTemp << "\n";
760 std::cout << "Total Power" << totalPower << "\n";
761 }
762
763 // Calculate the exit air temp
764 // Texit = Tfp + (1.76 * TotalPower / CFM * Faltitude)
Ed Tanous8a57ec02020-10-09 12:46:52 -0700765 double reading = 1.76 * totalPower * altitudeFactor;
James Feistbc896df2018-11-26 16:28:17 -0800766 reading /= cfm;
767 reading += inletTemp;
768
Ed Tanous8a57ec02020-10-09 12:46:52 -0700769 if constexpr (debug)
James Feistbc896df2018-11-26 16:28:17 -0800770 {
771 std::cout << "Reading 1: " << reading << "\n";
772 }
773
774 // Now perform the exponential average
775 // Calculate alpha based on SDR values and CFM
776 // Ai = As + (Af - As)/(QMax - QMin) * (CFM - QMin)
777
778 double alpha = 0.0;
779 if (cfm < qMin)
780 {
781 alpha = alphaS;
782 }
783 else if (cfm >= qMax)
784 {
785 alpha = alphaF;
786 }
787 else
788 {
789 alpha = alphaS + ((alphaF - alphaS) * (cfm - qMin) / (qMax - qMin));
790 }
791
Zhikui Ren12e3d672020-12-03 15:14:49 -0800792 auto time = std::chrono::steady_clock::now();
James Feistbc896df2018-11-26 16:28:17 -0800793 if (!firstRead)
794 {
795 firstRead = true;
796 lastTime = time;
797 lastReading = reading;
798 }
799 double alphaDT =
800 std::chrono::duration_cast<std::chrono::seconds>(time - lastTime)
801 .count() *
802 alpha;
803
804 // cap at 1.0 or the below fails
805 if (alphaDT > 1.0)
806 {
807 alphaDT = 1.0;
808 }
809
Ed Tanous8a57ec02020-10-09 12:46:52 -0700810 if constexpr (debug)
James Feistbc896df2018-11-26 16:28:17 -0800811 {
812 std::cout << "AlphaDT: " << alphaDT << "\n";
813 }
814
815 reading = ((reading * alphaDT) + (lastReading * (1.0 - alphaDT)));
816
Ed Tanous8a57ec02020-10-09 12:46:52 -0700817 if constexpr (debug)
James Feistbc896df2018-11-26 16:28:17 -0800818 {
819 std::cout << "Reading 2: " << reading << "\n";
820 }
821
822 val = reading;
823 lastReading = reading;
824 lastTime = time;
James Feistae11cfc2019-05-07 15:01:20 -0700825 errorPrint = maxErrorPrint;
James Feistbc896df2018-11-26 16:28:17 -0800826 return true;
827}
828
829void ExitAirTempSensor::checkThresholds(void)
830{
831 thresholds::checkThresholds(this);
832}
833
Zev Weissafd15042022-07-18 12:28:40 -0700834static void loadVariantPathArray(const SensorBaseConfigMap& data,
835 const std::string& key,
836 std::vector<std::string>& resp)
James Feistbc896df2018-11-26 16:28:17 -0800837{
838 auto it = data.find(key);
839 if (it == data.end())
840 {
841 std::cerr << "Configuration missing " << key << "\n";
842 throw std::invalid_argument("Key Missing");
843 }
844 BasicVariantType copy = it->second;
James Feist3eb82622019-02-08 13:10:22 -0800845 std::vector<std::string> config = std::get<std::vector<std::string>>(copy);
James Feistbc896df2018-11-26 16:28:17 -0800846 for (auto& str : config)
847 {
848 boost::replace_all(str, " ", "_");
849 }
850 resp = std::move(config);
851}
852
853void createSensor(sdbusplus::asio::object_server& objectServer,
James Feistb2eb3f52018-12-04 16:17:50 -0800854 std::shared_ptr<ExitAirTempSensor>& exitAirSensor,
James Feistbc896df2018-11-26 16:28:17 -0800855 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
856{
857 if (!dbusConnection)
858 {
859 std::cerr << "Connection not created\n";
860 return;
861 }
James Feist655f3762020-10-05 15:28:15 -0700862 auto getter = std::make_shared<GetSensorConfiguration>(
Ed Tanousbb679322022-05-16 16:10:00 -0700863 dbusConnection,
864 [&objectServer, &dbusConnection,
865 &exitAirSensor](const ManagedObjectType& resp) {
866 cfmSensors.clear();
Zev Weiss72f322f2022-08-12 18:21:01 -0700867 for (const auto& [path, interfaces] : resp)
Ed Tanousbb679322022-05-16 16:10:00 -0700868 {
Zev Weiss72f322f2022-08-12 18:21:01 -0700869 for (const auto& [intf, cfg] : interfaces)
James Feistbc896df2018-11-26 16:28:17 -0800870 {
Zev Weiss054aad82022-08-18 01:37:34 -0700871 if (intf == configInterfaceName(exitAirType))
James Feistbc896df2018-11-26 16:28:17 -0800872 {
Ed Tanousbb679322022-05-16 16:10:00 -0700873 // thresholds should be under the same path
874 std::vector<thresholds::Threshold> sensorThresholds;
Zev Weiss72f322f2022-08-12 18:21:01 -0700875 parseThresholdsFromConfig(interfaces, sensorThresholds);
James Feistbc896df2018-11-26 16:28:17 -0800876
Zev Weiss72f322f2022-08-12 18:21:01 -0700877 std::string name = loadVariant<std::string>(cfg, "Name");
Ed Tanousbb679322022-05-16 16:10:00 -0700878 exitAirSensor = std::make_shared<ExitAirTempSensor>(
Zev Weiss72f322f2022-08-12 18:21:01 -0700879 dbusConnection, name, path.str, objectServer,
Ed Tanousbb679322022-05-16 16:10:00 -0700880 std::move(sensorThresholds));
881 exitAirSensor->powerFactorMin =
Zev Weiss72f322f2022-08-12 18:21:01 -0700882 loadVariant<double>(cfg, "PowerFactorMin");
Ed Tanousbb679322022-05-16 16:10:00 -0700883 exitAirSensor->powerFactorMax =
Zev Weiss72f322f2022-08-12 18:21:01 -0700884 loadVariant<double>(cfg, "PowerFactorMax");
885 exitAirSensor->qMin = loadVariant<double>(cfg, "QMin");
886 exitAirSensor->qMax = loadVariant<double>(cfg, "QMax");
887 exitAirSensor->alphaS = loadVariant<double>(cfg, "AlphaS");
888 exitAirSensor->alphaF = loadVariant<double>(cfg, "AlphaF");
Ed Tanousbb679322022-05-16 16:10:00 -0700889 }
Zev Weiss054aad82022-08-18 01:37:34 -0700890 else if (intf == configInterfaceName(cfmType))
Ed Tanousbb679322022-05-16 16:10:00 -0700891 {
892 // thresholds should be under the same path
893 std::vector<thresholds::Threshold> sensorThresholds;
Zev Weiss72f322f2022-08-12 18:21:01 -0700894 parseThresholdsFromConfig(interfaces, sensorThresholds);
895 std::string name = loadVariant<std::string>(cfg, "Name");
Ed Tanousbb679322022-05-16 16:10:00 -0700896 auto sensor = std::make_shared<CFMSensor>(
Zev Weiss72f322f2022-08-12 18:21:01 -0700897 dbusConnection, name, path.str, objectServer,
Ed Tanousbb679322022-05-16 16:10:00 -0700898 std::move(sensorThresholds), exitAirSensor);
Zev Weiss72f322f2022-08-12 18:21:01 -0700899 loadVariantPathArray(cfg, "Tachs", sensor->tachs);
900 sensor->maxCFM = loadVariant<double>(cfg, "MaxCFM");
James Feistbc896df2018-11-26 16:28:17 -0800901
Ed Tanousbb679322022-05-16 16:10:00 -0700902 // change these into percent upon getting the data
Zev Weiss72f322f2022-08-12 18:21:01 -0700903 sensor->c1 = loadVariant<double>(cfg, "C1") / 100;
904 sensor->c2 = loadVariant<double>(cfg, "C2") / 100;
Ed Tanousbb679322022-05-16 16:10:00 -0700905 sensor->tachMinPercent =
Zev Weiss72f322f2022-08-12 18:21:01 -0700906 loadVariant<double>(cfg, "TachMinPercent");
Ed Tanousbb679322022-05-16 16:10:00 -0700907 sensor->tachMaxPercent =
Zev Weiss72f322f2022-08-12 18:21:01 -0700908 loadVariant<double>(cfg, "TachMaxPercent");
Ed Tanousbb679322022-05-16 16:10:00 -0700909 sensor->createMaxCFMIface();
910 sensor->setupMatches();
James Feistbc896df2018-11-26 16:28:17 -0800911
Ed Tanousbb679322022-05-16 16:10:00 -0700912 cfmSensors.emplace_back(std::move(sensor));
James Feistbc896df2018-11-26 16:28:17 -0800913 }
914 }
Ed Tanousbb679322022-05-16 16:10:00 -0700915 }
916 if (exitAirSensor)
917 {
918 exitAirSensor->setupMatches();
919 exitAirSensor->updateReading();
920 }
Ed Tanous8a17c302021-09-02 15:07:11 -0700921 });
James Feist655f3762020-10-05 15:28:15 -0700922 getter->getConfiguration(
Zev Weiss054aad82022-08-18 01:37:34 -0700923 std::vector<std::string>(monitorTypes.begin(), monitorTypes.end()));
James Feistbc896df2018-11-26 16:28:17 -0800924}
925
James Feistb6c0b912019-07-09 12:21:44 -0700926int main()
James Feistbc896df2018-11-26 16:28:17 -0800927{
Ed Tanous1f978632023-02-28 18:16:39 -0800928 boost::asio::io_context io;
James Feistbc896df2018-11-26 16:28:17 -0800929 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
Johnathan Mantey661d4372022-10-27 09:00:59 -0700930 sdbusplus::asio::object_server objectServer(systemBus, true);
931 objectServer.add_manager("/xyz/openbmc_project/sensors");
James Feistbc896df2018-11-26 16:28:17 -0800932 systemBus->request_name("xyz.openbmc_project.ExitAirTempSensor");
James Feistbc896df2018-11-26 16:28:17 -0800933 std::shared_ptr<ExitAirTempSensor> sensor =
934 nullptr; // wait until we find the config
James Feistbc896df2018-11-26 16:28:17 -0800935
Ed Tanous83db50c2023-03-01 10:20:24 -0800936 boost::asio::post(io,
937 [&]() { createSensor(objectServer, sensor, systemBus); });
James Feistbc896df2018-11-26 16:28:17 -0800938
Ed Tanous9b4a20e2022-09-06 08:47:11 -0700939 boost::asio::steady_timer configTimer(io);
James Feistbc896df2018-11-26 16:28:17 -0800940
Patrick Williams92f8f512022-07-22 19:26:55 -0500941 std::function<void(sdbusplus::message_t&)> eventHandler =
942 [&](sdbusplus::message_t&) {
Ed Tanous83db50c2023-03-01 10:20:24 -0800943 configTimer.expires_after(std::chrono::seconds(1));
Ed Tanousbb679322022-05-16 16:10:00 -0700944 // create a timer because normally multiple properties change
945 configTimer.async_wait([&](const boost::system::error_code& ec) {
946 if (ec == boost::asio::error::operation_aborted)
947 {
948 return; // we're being canceled
949 }
950 createSensor(objectServer, sensor, systemBus);
951 if (!sensor)
952 {
953 std::cout << "Configuration not detected\n";
954 }
955 });
956 };
Zev Weiss214d9712022-08-12 12:54:31 -0700957 std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
Zev Weiss054aad82022-08-18 01:37:34 -0700958 setupPropertiesChangedMatches(*systemBus, monitorTypes, eventHandler);
James Feistbc896df2018-11-26 16:28:17 -0800959
Bruce Lee913d4d02021-07-22 10:18:42 +0800960 setupManufacturingModeMatch(*systemBus);
James Feistbc896df2018-11-26 16:28:17 -0800961 io.run();
Zhikui Ren8685b172021-06-29 15:16:52 -0700962 return 0;
James Feistbc896df2018-11-26 16:28:17 -0800963}