blob: 72681565513ec05cb924d3613be33297fbfdce6c [file] [log] [blame]
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001#include "virtualSensor.hpp"
2
Patrick Williams82b39c62021-07-28 16:22:27 -05003#include <phosphor-logging/lg2.hpp>
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07004
5#include <fstream>
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07006
7static constexpr bool DEBUG = false;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07008static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/";
Rashmica Guptae7efe132021-07-27 19:42:11 +10009static constexpr auto vsThresholdsIfaceSuffix = ".Thresholds";
Tao Linf6b7e0a2022-10-09 09:35:44 +080010static constexpr std::array<const char*, 2> calculationIfaces = {
11 "xyz.openbmc_project.Configuration.ModifiedMedian",
12 "xyz.openbmc_project.Configuration.Maximum"};
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +100013static constexpr auto defaultHysteresis = 0;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070014
Patrick Williams82b39c62021-07-28 16:22:27 -050015PHOSPHOR_LOG2_USING_WITH_FLAGS;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070016
Vijay Khemka51f898e2020-09-09 22:24:18 -070017int handleDbusSignal(sd_bus_message* msg, void* usrData, sd_bus_error*)
18{
19 if (usrData == nullptr)
20 {
21 throw std::runtime_error("Invalid match");
22 }
23
Patrick Williams8e11ccc2022-07-22 19:26:57 -050024 auto sdbpMsg = sdbusplus::message_t(msg);
Vijay Khemka51f898e2020-09-09 22:24:18 -070025 std::string msgIfce;
26 std::map<std::string, std::variant<int64_t, double, bool>> msgData;
27
28 sdbpMsg.read(msgIfce, msgData);
29
30 if (msgData.find("Value") != msgData.end())
31 {
32 using namespace phosphor::virtualSensor;
33 VirtualSensor* obj = static_cast<VirtualSensor*>(usrData);
34 // TODO(openbmc/phosphor-virtual-sensor#1): updateVirtualSensor should
35 // be changed to take the information we got from the signal, to avoid
36 // having to do numerous dbus queries.
37 obj->updateVirtualSensor();
38 }
39 return 0;
40}
41
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070042namespace phosphor
43{
44namespace virtualSensor
45{
46
47void printParams(const VirtualSensor::ParamMap& paramMap)
48{
49 for (const auto& p : paramMap)
50 {
51 const auto& p1 = p.first;
52 const auto& p2 = p.second;
53 auto val = p2->getParamValue();
Patrick Williamsfbd71452021-08-30 06:59:46 -050054 debug("Parameter: {PARAM} = {VALUE}", "PARAM", p1, "VALUE", val);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070055 }
56}
57
58double SensorParam::getParamValue()
59{
60 switch (paramType)
61 {
62 case constParam:
63 return value;
64 break;
Vijay Khemka7452a862020-08-11 16:01:23 -070065 case dbusParam:
66 return dbusSensor->getSensorValue();
67 break;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070068 default:
69 throw std::invalid_argument("param type not supported");
70 }
71}
72
Lei YU0fcf0e12021-06-04 11:14:17 +080073using AssociationList =
74 std::vector<std::tuple<std::string, std::string, std::string>>;
75
76AssociationList getAssociationsFromJson(const Json& j)
77{
78 AssociationList assocs{};
79 try
80 {
81 j.get_to(assocs);
82 }
83 catch (const std::exception& ex)
84 {
Patrick Williams82b39c62021-07-28 16:22:27 -050085 error("Failed to parse association: {ERROR}", "ERROR", ex);
Lei YU0fcf0e12021-06-04 11:14:17 +080086 }
87 return assocs;
88}
89
Rashmica Guptae7efe132021-07-27 19:42:11 +100090template <typename U>
91struct VariantToNumber
92{
93 template <typename T>
94 U operator()(const T& t) const
95 {
96 if constexpr (std::is_convertible<T, U>::value)
97 {
98 return static_cast<U>(t);
99 }
100 throw std::invalid_argument("Invalid number type in config\n");
101 }
102};
103
104template <typename U>
105U getNumberFromConfig(const PropertyMap& map, const std::string& name,
Jiaqing Zhao190f6d02022-05-21 23:26:15 +0800106 bool required,
107 U defaultValue = std::numeric_limits<U>::quiet_NaN())
Rashmica Guptae7efe132021-07-27 19:42:11 +1000108{
109 if (auto itr = map.find(name); itr != map.end())
110 {
111 return std::visit(VariantToNumber<U>(), itr->second);
112 }
113 else if (required)
114 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500115 error("Required field {NAME} missing in config", "NAME", name);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000116 throw std::invalid_argument("Required field missing in config");
117 }
Jiaqing Zhao190f6d02022-05-21 23:26:15 +0800118 return defaultValue;
Rashmica Guptae7efe132021-07-27 19:42:11 +1000119}
120
121bool isCalculationType(const std::string& interface)
122{
123 auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
124 interface);
125 if (itr != calculationIfaces.end())
126 {
127 return true;
128 }
129 return false;
130}
131
132const std::string getThresholdType(const std::string& direction,
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100133 const std::string& severity)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000134{
Rashmica Guptae7efe132021-07-27 19:42:11 +1000135 std::string suffix;
Rashmica Guptae7efe132021-07-27 19:42:11 +1000136
137 if (direction == "less than")
138 {
139 suffix = "Low";
140 }
141 else if (direction == "greater than")
142 {
143 suffix = "High";
144 }
145 else
146 {
147 throw std::invalid_argument(
148 "Invalid threshold direction specified in entity manager");
149 }
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100150 return severity + suffix;
151}
152
153std::string getSeverityField(const PropertyMap& propertyMap)
154{
155 static const std::array thresholdTypes{"Warning", "Critical",
156 "PerformanceLoss", "SoftShutdown",
157 "HardShutdown"};
158
159 std::string severity;
160 if (auto itr = propertyMap.find("Severity"); itr != propertyMap.end())
161 {
162 /* Severity should be a string, but can be an unsigned int */
163 if (std::holds_alternative<std::string>(itr->second))
164 {
165 severity = std::get<std::string>(itr->second);
166 if (0 == std::ranges::count(thresholdTypes, severity))
167 {
168 throw std::invalid_argument(
169 "Invalid threshold severity specified in entity manager");
170 }
171 }
172 else
173 {
Patrick Williams1226f202023-05-10 07:51:16 -0500174 auto sev = getNumberFromConfig<uint64_t>(propertyMap, "Severity",
175 true);
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100176 /* Checking bounds ourselves so we throw invalid argument on
177 * invalid user input */
178 if (sev >= thresholdTypes.size())
179 {
180 throw std::invalid_argument(
181 "Invalid threshold severity specified in entity manager");
182 }
183 severity = thresholdTypes.at(sev);
184 }
185 }
186 return severity;
Rashmica Guptae7efe132021-07-27 19:42:11 +1000187}
188
Tao Lin91799db2022-07-27 21:02:20 +0800189void parseThresholds(Json& thresholds, const PropertyMap& propertyMap,
190 const std::string& entityInterface = "")
Rashmica Guptae7efe132021-07-27 19:42:11 +1000191{
192 std::string direction;
193
Rashmica Guptae7efe132021-07-27 19:42:11 +1000194 auto value = getNumberFromConfig<double>(propertyMap, "Value", true);
195
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100196 auto severity = getSeverityField(propertyMap);
197
198 if (auto itr = propertyMap.find("Direction"); itr != propertyMap.end())
Rashmica Guptae7efe132021-07-27 19:42:11 +1000199 {
200 direction = std::get<std::string>(itr->second);
201 }
202
203 auto threshold = getThresholdType(direction, severity);
204 thresholds[threshold] = value;
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000205
Patrick Williams1226f202023-05-10 07:51:16 -0500206 auto hysteresis = getNumberFromConfig<double>(propertyMap, "Hysteresis",
207 false);
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000208 if (hysteresis != std::numeric_limits<double>::quiet_NaN())
209 {
210 thresholds[threshold + "Hysteresis"] = hysteresis;
211 }
Tao Lin91799db2022-07-27 21:02:20 +0800212
213 if (!entityInterface.empty())
214 {
215 thresholds[threshold + "Direction"] = entityInterface;
216 }
Rashmica Guptae7efe132021-07-27 19:42:11 +1000217}
218
219void VirtualSensor::parseConfigInterface(const PropertyMap& propertyMap,
220 const std::string& sensorType,
221 const std::string& interface)
222{
223 /* Parse sensors / DBus params */
224 if (auto itr = propertyMap.find("Sensors"); itr != propertyMap.end())
225 {
226 auto sensors = std::get<std::vector<std::string>>(itr->second);
227 for (auto sensor : sensors)
228 {
229 std::replace(sensor.begin(), sensor.end(), ' ', '_');
230 auto sensorObjPath = sensorDbusPath + sensorType + "/" + sensor;
231
Patrick Williams1226f202023-05-10 07:51:16 -0500232 auto paramPtr = std::make_unique<SensorParam>(bus, sensorObjPath,
233 this);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000234 symbols.create_variable(sensor);
235 paramMap.emplace(std::move(sensor), std::move(paramPtr));
236 }
237 }
238 /* Get expression string */
239 if (!isCalculationType(interface))
240 {
241 throw std::invalid_argument("Invalid expression in interface");
242 }
243 exprStr = interface;
244
245 /* Get optional min and max input and output values */
246 ValueIface::maxValue(
247 getNumberFromConfig<double>(propertyMap, "MaxValue", false));
248 ValueIface::minValue(
249 getNumberFromConfig<double>(propertyMap, "MinValue", false));
250 maxValidInput =
Jiaqing Zhao190f6d02022-05-21 23:26:15 +0800251 getNumberFromConfig<double>(propertyMap, "MaxValidInput", false,
252 std::numeric_limits<double>::infinity());
Rashmica Guptae7efe132021-07-27 19:42:11 +1000253 minValidInput =
Jiaqing Zhao190f6d02022-05-21 23:26:15 +0800254 getNumberFromConfig<double>(propertyMap, "MinValidInput", false,
255 -std::numeric_limits<double>::infinity());
Rashmica Guptae7efe132021-07-27 19:42:11 +1000256}
257
Matt Spinlerce675222021-01-14 16:38:09 -0600258void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
259 const std::string& objPath)
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700260{
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700261 static const Json empty{};
262
263 /* Get threshold values if defined in config */
264 auto threshold = sensorConfig.value("Threshold", empty);
Matt Spinlerf15189e2021-01-15 10:13:28 -0600265
Rashmica Gupta3e999192021-06-09 16:17:04 +1000266 createThresholds(threshold, objPath);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700267
Harvey Wuf6443742021-04-09 16:47:36 +0800268 /* Get MaxValue, MinValue setting if defined in config */
269 auto confDesc = sensorConfig.value("Desc", empty);
270 if (auto maxConf = confDesc.find("MaxValue");
271 maxConf != confDesc.end() && maxConf->is_number())
272 {
273 ValueIface::maxValue(maxConf->get<double>());
274 }
275 if (auto minConf = confDesc.find("MinValue");
276 minConf != confDesc.end() && minConf->is_number())
277 {
278 ValueIface::minValue(minConf->get<double>());
279 }
280
Lei YU0fcf0e12021-06-04 11:14:17 +0800281 /* Get optional association */
282 auto assocJson = sensorConfig.value("Associations", empty);
283 if (!assocJson.empty())
284 {
285 auto assocs = getAssociationsFromJson(assocJson);
286 if (!assocs.empty())
287 {
288 associationIface =
289 std::make_unique<AssociationObject>(bus, objPath.c_str());
290 associationIface->associations(assocs);
291 }
292 }
293
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700294 /* Get expression string */
Patrick Williams03c4c8e2022-04-14 22:19:44 -0500295 static constexpr auto exprKey = "Expression";
296 if (sensorConfig.contains(exprKey))
297 {
Patrick Williamsa9596782022-04-15 10:20:07 -0500298 auto& ref = sensorConfig.at(exprKey);
Patrick Williams03c4c8e2022-04-14 22:19:44 -0500299 if (ref.is_array())
300 {
301 exprStr = std::string{};
302 for (auto& s : ref)
303 {
304 exprStr += s;
305 }
306 }
307 else if (ref.is_string())
308 {
309 exprStr = std::string{ref};
310 }
311 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700312
313 /* Get all the parameter listed in configuration */
314 auto params = sensorConfig.value("Params", empty);
315
316 /* Check for constant parameter */
317 const auto& consParams = params.value("ConstParam", empty);
318 if (!consParams.empty())
319 {
320 for (auto& j : consParams)
321 {
322 if (j.find("ParamName") != j.end())
323 {
324 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700325 std::string name = j["ParamName"];
326 symbols.create_variable(name);
327 paramMap.emplace(std::move(name), std::move(paramPtr));
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700328 }
329 else
330 {
331 /* Invalid configuration */
332 throw std::invalid_argument(
333 "ParamName not found in configuration");
334 }
335 }
336 }
337
Vijay Khemka7452a862020-08-11 16:01:23 -0700338 /* Check for dbus parameter */
339 auto dbusParams = params.value("DbusParam", empty);
340 if (!dbusParams.empty())
341 {
342 for (auto& j : dbusParams)
343 {
344 /* Get parameter dbus sensor descriptor */
345 auto desc = j.value("Desc", empty);
346 if ((!desc.empty()) && (j.find("ParamName") != j.end()))
347 {
348 std::string sensorType = desc.value("SensorType", "");
349 std::string name = desc.value("Name", "");
350
351 if (!sensorType.empty() && !name.empty())
352 {
George Liu1204b432021-12-29 17:24:48 +0800353 auto path = sensorDbusPath + sensorType + "/" + name;
Vijay Khemka7452a862020-08-11 16:01:23 -0700354
Patrick Williams1226f202023-05-10 07:51:16 -0500355 auto paramPtr = std::make_unique<SensorParam>(bus, path,
356 this);
George Liu1204b432021-12-29 17:24:48 +0800357 std::string paramName = j["ParamName"];
358 symbols.create_variable(paramName);
359 paramMap.emplace(std::move(paramName), std::move(paramPtr));
Vijay Khemka7452a862020-08-11 16:01:23 -0700360 }
361 }
362 }
363 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700364
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700365 symbols.add_constants();
Matt Spinler9f1ef4f2020-11-09 15:59:11 -0600366 symbols.add_package(vecopsPackage);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700367 expression.register_symbol_table(symbols);
368
369 /* parser from exprtk */
370 exprtk::parser<double> parser{};
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600371 if (!parser.compile(exprStr, expression))
372 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500373 error("Expression compilation failed");
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600374
375 for (std::size_t i = 0; i < parser.error_count(); ++i)
376 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500377 auto err = parser.get_error(i);
378 error("Error parsing token at {POSITION}: {ERROR}", "POSITION",
379 err.token.position, "TYPE",
380 exprtk::parser_error::to_str(err.mode), "ERROR",
381 err.diagnostic);
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600382 }
383 throw std::runtime_error("Expression compilation failed");
384 }
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700385
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700386 /* Print all parameters for debug purpose only */
387 if (DEBUG)
388 printParams(paramMap);
389}
390
Tao Lindc777012022-07-27 20:41:46 +0800391void VirtualSensor::createAssociation(const std::string& objPath,
392 const std::string& entityPath)
393{
394 if (objPath.empty() || entityPath.empty())
395 {
396 return;
397 }
398
399 std::filesystem::path p(entityPath);
400 auto assocsDbus =
401 AssociationList{{"chassis", "all_sensors", p.parent_path().string()}};
Patrick Williams1226f202023-05-10 07:51:16 -0500402 associationIface = std::make_unique<AssociationObject>(bus,
403 objPath.c_str());
Tao Lindc777012022-07-27 20:41:46 +0800404 associationIface->associations(assocsDbus);
405}
406
Rashmica Guptae7efe132021-07-27 19:42:11 +1000407void VirtualSensor::initVirtualSensor(const InterfaceMap& interfaceMap,
408 const std::string& objPath,
409 const std::string& sensorType,
410 const std::string& calculationIface)
411{
412 Json thresholds;
Patrick Williams1226f202023-05-10 07:51:16 -0500413 const std::string vsThresholdsIntf = calculationIface +
414 vsThresholdsIfaceSuffix;
Rashmica Guptae7efe132021-07-27 19:42:11 +1000415
416 for (const auto& [interface, propertyMap] : interfaceMap)
417 {
418 /* Each threshold is on it's own interface with a number as a suffix
419 * eg xyz.openbmc_project.Configuration.ModifiedMedian.Thresholds1 */
420 if (interface.find(vsThresholdsIntf) != std::string::npos)
421 {
Tao Lin91799db2022-07-27 21:02:20 +0800422 parseThresholds(thresholds, propertyMap, interface);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000423 }
424 else if (interface == calculationIface)
425 {
426 parseConfigInterface(propertyMap, sensorType, interface);
427 }
428 }
429
430 createThresholds(thresholds, objPath);
431 symbols.add_constants();
432 symbols.add_package(vecopsPackage);
433 expression.register_symbol_table(symbols);
434
Tao Lindc777012022-07-27 20:41:46 +0800435 createAssociation(objPath, entityPath);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000436 /* Print all parameters for debug purpose only */
437 if (DEBUG)
438 {
439 printParams(paramMap);
440 }
441}
442
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700443void VirtualSensor::setSensorValue(double value)
444{
Patrick Williams543bf662021-04-29 09:03:53 -0500445 value = std::clamp(value, ValueIface::minValue(), ValueIface::maxValue());
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700446 ValueIface::value(value);
447}
448
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000449double VirtualSensor::calculateValue(const std::string& calculation,
450 const VirtualSensor::ParamMap& paramMap)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000451{
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000452 auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
453 calculation);
454 if (itr == calculationIfaces.end())
455 {
456 return std::numeric_limits<double>::quiet_NaN();
457 }
458 else if (calculation == "xyz.openbmc_project.Configuration.ModifiedMedian")
459 {
460 return calculateModifiedMedianValue(paramMap);
461 }
Tao Linf6b7e0a2022-10-09 09:35:44 +0800462 else if (calculation == "xyz.openbmc_project.Configuration.Maximum")
463 {
464 return calculateMaximumValue(paramMap);
465 }
Rashmica Guptae7efe132021-07-27 19:42:11 +1000466 return std::numeric_limits<double>::quiet_NaN();
467}
468
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000469bool VirtualSensor::sensorInRange(double value)
470{
471 if (value <= this->maxValidInput && value >= this->minValidInput)
472 {
473 return true;
474 }
475 return false;
476}
477
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700478void VirtualSensor::updateVirtualSensor()
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700479{
480 for (auto& param : paramMap)
481 {
482 auto& name = param.first;
483 auto& data = param.second;
484 if (auto var = symbols.get_variable(name))
485 {
486 var->ref() = data->getParamValue();
487 }
488 else
489 {
490 /* Invalid parameter */
491 throw std::invalid_argument("ParamName not found in symbols");
492 }
493 }
Patrick Williams1226f202023-05-10 07:51:16 -0500494 auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
495 exprStr);
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000496 auto val = (itr == calculationIfaces.end())
497 ? expression.value()
498 : calculateValue(exprStr, paramMap);
Vijay Khemka32a71562020-09-10 15:29:18 -0700499
500 /* Set sensor value to dbus interface */
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700501 setSensorValue(val);
Vijay Khemka32a71562020-09-10 15:29:18 -0700502
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700503 if (DEBUG)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000504 {
Patrick Williamsfbd71452021-08-30 06:59:46 -0500505 debug("Sensor {NAME} = {VALUE}", "NAME", this->name, "VALUE", val);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000506 }
Vijay Khemka32a71562020-09-10 15:29:18 -0700507
Matt Spinler8f5e6112021-01-15 10:44:32 -0600508 /* Check sensor thresholds and log required message */
Matt Spinlerb306b032021-02-01 10:05:46 -0600509 checkThresholds(val, perfLossIface);
Patrick Williamsfdb826d2021-01-20 14:37:53 -0600510 checkThresholds(val, warningIface);
511 checkThresholds(val, criticalIface);
512 checkThresholds(val, softShutdownIface);
513 checkThresholds(val, hardShutdownIface);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700514}
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700515
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000516double VirtualSensor::calculateModifiedMedianValue(
517 const VirtualSensor::ParamMap& paramMap)
518{
519 std::vector<double> values;
520
521 for (auto& param : paramMap)
522 {
523 auto& name = param.first;
524 if (auto var = symbols.get_variable(name))
525 {
526 if (!sensorInRange(var->ref()))
527 {
528 continue;
529 }
530 values.push_back(var->ref());
531 }
532 }
533
534 size_t size = values.size();
535 std::sort(values.begin(), values.end());
536 switch (size)
537 {
538 case 2:
539 /* Choose biggest value */
540 return values.at(1);
541 case 0:
542 return std::numeric_limits<double>::quiet_NaN();
543 default:
544 /* Choose median value */
545 if (size % 2 == 0)
546 {
547 // Average of the two middle values
548 return (values.at(size / 2) + values.at(size / 2 - 1)) / 2;
549 }
550 else
551 {
552 return values.at((size - 1) / 2);
553 }
554 }
555}
556
Tao Linf6b7e0a2022-10-09 09:35:44 +0800557double VirtualSensor::calculateMaximumValue(
558 const VirtualSensor::ParamMap& paramMap)
559{
560 std::vector<double> values;
561
562 for (auto& param : paramMap)
563 {
564 auto& name = param.first;
565 if (auto var = symbols.get_variable(name))
566 {
567 if (!sensorInRange(var->ref()))
568 {
569 continue;
570 }
571 values.push_back(var->ref());
572 }
573 }
574 auto maxIt = std::max_element(values.begin(), values.end());
575 if (maxIt == values.end())
576 {
577 return std::numeric_limits<double>::quiet_NaN();
578 }
579 return *maxIt;
580}
581
Rashmica Gupta3e999192021-06-09 16:17:04 +1000582void VirtualSensor::createThresholds(const Json& threshold,
583 const std::string& objPath)
584{
585 if (threshold.empty())
586 {
587 return;
588 }
589 // Only create the threshold interfaces if
590 // at least one of their values is present.
591 if (threshold.contains("CriticalHigh") || threshold.contains("CriticalLow"))
592 {
593 criticalIface =
594 std::make_unique<Threshold<CriticalObject>>(bus, objPath.c_str());
595
Tao Lin91799db2022-07-27 21:02:20 +0800596 if (threshold.contains("CriticalHigh"))
597 {
598 criticalIface->setEntityInterfaceHigh(
599 threshold.value("CriticalHighDirection", ""));
600 if (DEBUG)
601 {
602 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
603 "INTF", threshold.value("CriticalHighDirection", ""));
604 }
605 }
606 if (threshold.contains("CriticalLow"))
607 {
608 criticalIface->setEntityInterfaceLow(
609 threshold.value("CriticalLowDirection", ""));
610 if (DEBUG)
611 {
612 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
613 "INTF", threshold.value("CriticalLowDirection", ""));
614 }
615 }
616
617 criticalIface->setEntityPath(entityPath);
618 if (DEBUG)
619 {
620 debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath,
621 "PATH", entityPath);
622 }
Matt Spinlera291ce12023-02-06 15:12:44 -0600623
624 criticalIface->criticalHigh(threshold.value(
625 "CriticalHigh", std::numeric_limits<double>::quiet_NaN()));
626 criticalIface->criticalLow(threshold.value(
627 "CriticalLow", std::numeric_limits<double>::quiet_NaN()));
628 criticalIface->setHighHysteresis(
629 threshold.value("CriticalHighHysteresis", defaultHysteresis));
630 criticalIface->setLowHysteresis(
631 threshold.value("CriticalLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000632 }
633
634 if (threshold.contains("WarningHigh") || threshold.contains("WarningLow"))
635 {
636 warningIface =
637 std::make_unique<Threshold<WarningObject>>(bus, objPath.c_str());
638
Tao Lin91799db2022-07-27 21:02:20 +0800639 if (threshold.contains("WarningHigh"))
640 {
641 warningIface->setEntityInterfaceHigh(
642 threshold.value("WarningHighDirection", ""));
643 if (DEBUG)
644 {
645 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
646 "INTF", threshold.value("WarningHighDirection", ""));
647 }
648 }
649 if (threshold.contains("WarningLow"))
650 {
651 warningIface->setEntityInterfaceLow(
652 threshold.value("WarningLowDirection", ""));
653 if (DEBUG)
654 {
655 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
656 "INTF", threshold.value("WarningLowDirection", ""));
657 }
658 }
659
660 warningIface->setEntityPath(entityPath);
661 if (DEBUG)
662 {
663 debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath,
664 "PATH", entityPath);
665 }
Matt Spinlera291ce12023-02-06 15:12:44 -0600666
667 warningIface->warningHigh(threshold.value(
668 "WarningHigh", std::numeric_limits<double>::quiet_NaN()));
669 warningIface->warningLow(threshold.value(
670 "WarningLow", std::numeric_limits<double>::quiet_NaN()));
671 warningIface->setHighHysteresis(
672 threshold.value("WarningHighHysteresis", defaultHysteresis));
673 warningIface->setLowHysteresis(
674 threshold.value("WarningLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000675 }
676
677 if (threshold.contains("HardShutdownHigh") ||
678 threshold.contains("HardShutdownLow"))
679 {
680 hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>(
681 bus, objPath.c_str());
682
683 hardShutdownIface->hardShutdownHigh(threshold.value(
684 "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
685 hardShutdownIface->hardShutdownLow(threshold.value(
686 "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000687 hardShutdownIface->setHighHysteresis(
688 threshold.value("HardShutdownHighHysteresis", defaultHysteresis));
689 hardShutdownIface->setLowHysteresis(
690 threshold.value("HardShutdownLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000691 }
692
693 if (threshold.contains("SoftShutdownHigh") ||
694 threshold.contains("SoftShutdownLow"))
695 {
696 softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>(
697 bus, objPath.c_str());
698
699 softShutdownIface->softShutdownHigh(threshold.value(
700 "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
701 softShutdownIface->softShutdownLow(threshold.value(
702 "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000703 softShutdownIface->setHighHysteresis(
704 threshold.value("SoftShutdownHighHysteresis", defaultHysteresis));
705 softShutdownIface->setLowHysteresis(
706 threshold.value("SoftShutdownLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000707 }
708
709 if (threshold.contains("PerformanceLossHigh") ||
710 threshold.contains("PerformanceLossLow"))
711 {
712 perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>(
713 bus, objPath.c_str());
714
715 perfLossIface->performanceLossHigh(threshold.value(
716 "PerformanceLossHigh", std::numeric_limits<double>::quiet_NaN()));
717 perfLossIface->performanceLossLow(threshold.value(
718 "PerformanceLossLow", std::numeric_limits<double>::quiet_NaN()));
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000719 perfLossIface->setHighHysteresis(threshold.value(
720 "PerformanceLossHighHysteresis", defaultHysteresis));
721 perfLossIface->setLowHysteresis(
722 threshold.value("PerformanceLossLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000723 }
724}
725
Rashmica Guptae7efe132021-07-27 19:42:11 +1000726ManagedObjectType VirtualSensors::getObjectsFromDBus()
727{
728 ManagedObjectType objects;
729
730 try
731 {
Nan Zhouf6825b92022-09-20 20:52:43 +0000732 auto method = bus.new_method_call("xyz.openbmc_project.EntityManager",
733 "/xyz/openbmc_project/inventory",
Rashmica Guptae7efe132021-07-27 19:42:11 +1000734 "org.freedesktop.DBus.ObjectManager",
735 "GetManagedObjects");
736 auto reply = bus.call(method);
737 reply.read(objects);
738 }
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500739 catch (const sdbusplus::exception_t& ex)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000740 {
741 // If entity manager isn't running yet, keep going.
742 if (std::string("org.freedesktop.DBus.Error.ServiceUnknown") !=
743 ex.name())
744 {
Matt Spinler71b9c112022-10-18 09:14:45 -0500745 error("Could not reach entity-manager: {ERROR}", "ERROR", ex);
746 throw;
Rashmica Guptae7efe132021-07-27 19:42:11 +1000747 }
748 }
749
750 return objects;
751}
752
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500753void VirtualSensors::propertiesChanged(sdbusplus::message_t& msg)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000754{
755 std::string path;
756 PropertyMap properties;
757
758 msg.read(path, properties);
759
760 /* We get multiple callbacks for one sensor. 'Type' is a required field and
761 * is a unique label so use to to only proceed once per sensor */
762 if (properties.contains("Type"))
763 {
764 if (isCalculationType(path))
765 {
766 createVirtualSensorsFromDBus(path);
767 }
768 }
769}
770
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700771/** @brief Parsing Virtual Sensor config JSON file */
Patrick Williams32dff212023-02-09 13:54:18 -0600772Json VirtualSensors::parseConfigFile()
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700773{
Patrick Williams32dff212023-02-09 13:54:18 -0600774 using path = std::filesystem::path;
775 auto configFile = []() -> path {
776 static constexpr auto name = "virtual_sensor_config.json";
777
778 for (auto pathSeg : {std::filesystem::current_path(),
779 path{"/var/lib/phosphor-virtual-sensor"},
780 path{"/usr/share/phosphor-virtual-sensor"}})
781 {
782 auto file = pathSeg / name;
783 if (std::filesystem::exists(file))
784 {
785 return file;
786 }
787 }
788 return name;
789 }();
790
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700791 std::ifstream jsonFile(configFile);
792 if (!jsonFile.is_open())
793 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500794 error("config JSON file {FILENAME} not found", "FILENAME", configFile);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000795 return {};
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700796 }
797
798 auto data = Json::parse(jsonFile, nullptr, false);
799 if (data.is_discarded())
800 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500801 error("config readings JSON parser failure with {FILENAME}", "FILENAME",
802 configFile);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700803 throw std::exception{};
804 }
805
806 return data;
807}
808
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700809std::map<std::string, ValueIface::Unit> unitMap = {
810 {"temperature", ValueIface::Unit::DegreesC},
811 {"fan_tach", ValueIface::Unit::RPMS},
812 {"voltage", ValueIface::Unit::Volts},
813 {"altitude", ValueIface::Unit::Meters},
814 {"current", ValueIface::Unit::Amperes},
815 {"power", ValueIface::Unit::Watts},
816 {"energy", ValueIface::Unit::Joules},
Kumar Thangavel2b56ddb2021-01-13 20:16:11 +0530817 {"utilization", ValueIface::Unit::Percent},
Rashmica Gupta4ac7a7f2021-07-08 12:30:50 +1000818 {"airflow", ValueIface::Unit::CFM},
819 {"pressure", ValueIface::Unit::Pascals}};
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700820
Rashmica Guptae7efe132021-07-27 19:42:11 +1000821const std::string getSensorTypeFromUnit(const std::string& unit)
822{
823 std::string unitPrefix = "xyz.openbmc_project.Sensor.Value.Unit.";
824 for (auto [type, unitObj] : unitMap)
825 {
826 auto unitPath = ValueIface::convertUnitToString(unitObj);
827 if (unitPath == (unitPrefix + unit))
828 {
829 return type;
830 }
831 }
832 return "";
833}
834
835void VirtualSensors::setupMatches()
836{
837 /* Already setup */
838 if (!this->matches.empty())
839 {
840 return;
841 }
842
843 /* Setup matches */
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500844 auto eventHandler = [this](sdbusplus::message_t& message) {
Rashmica Guptae7efe132021-07-27 19:42:11 +1000845 if (message.is_method_error())
846 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500847 error("Callback method error");
Rashmica Guptae7efe132021-07-27 19:42:11 +1000848 return;
849 }
850 this->propertiesChanged(message);
851 };
852
853 for (const char* iface : calculationIfaces)
854 {
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500855 auto match = std::make_unique<sdbusplus::bus::match_t>(
Rashmica Guptae7efe132021-07-27 19:42:11 +1000856 bus,
857 sdbusplus::bus::match::rules::propertiesChangedNamespace(
858 "/xyz/openbmc_project/inventory", iface),
859 eventHandler);
860 this->matches.emplace_back(std::move(match));
861 }
862}
863
864void VirtualSensors::createVirtualSensorsFromDBus(
865 const std::string& calculationIface)
866{
867 if (calculationIface.empty())
868 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500869 error("No calculation type supplied");
Rashmica Guptae7efe132021-07-27 19:42:11 +1000870 return;
871 }
872 auto objects = getObjectsFromDBus();
873
874 /* Get virtual sensors config data */
875 for (const auto& [path, interfaceMap] : objects)
876 {
Rashmica Guptae7efe132021-07-27 19:42:11 +1000877 /* Find Virtual Sensor interfaces */
George Liu2db8d412023-08-21 16:41:04 +0800878 auto intfIter = interfaceMap.find(calculationIface);
879 if (intfIter == interfaceMap.end())
Rashmica Guptae7efe132021-07-27 19:42:11 +1000880 {
881 continue;
882 }
George Liu2db8d412023-08-21 16:41:04 +0800883
884 std::string name = path.filename();
Rashmica Guptae7efe132021-07-27 19:42:11 +1000885 if (name.empty())
886 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500887 error("Virtual Sensor name not found in entity manager config");
Rashmica Guptae7efe132021-07-27 19:42:11 +1000888 continue;
889 }
890 if (virtualSensorsMap.contains(name))
891 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500892 error("A virtual sensor named {NAME} already exists", "NAME", name);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000893 continue;
894 }
895
896 /* Extract the virtual sensor type as we need this to initialize the
897 * sensor */
George Liu2db8d412023-08-21 16:41:04 +0800898 std::string sensorType, sensorUnit;
899 auto propertyMap = intfIter->second;
900 auto proIter = propertyMap.find("Units");
901 if (proIter != propertyMap.end())
Rashmica Guptae7efe132021-07-27 19:42:11 +1000902 {
George Liu2db8d412023-08-21 16:41:04 +0800903 sensorUnit = std::get<std::string>(proIter->second);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000904 }
905 sensorType = getSensorTypeFromUnit(sensorUnit);
906 if (sensorType.empty())
907 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500908 error("Sensor unit type {TYPE} is not supported", "TYPE",
909 sensorUnit);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000910 continue;
911 }
912
913 try
914 {
George Liu2db8d412023-08-21 16:41:04 +0800915 auto objpath = static_cast<std::string>(path);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000916 auto virtObjPath = sensorDbusPath + sensorType + "/" + name;
917
918 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
919 bus, virtObjPath.c_str(), interfaceMap, name, sensorType,
Tao Lindc777012022-07-27 20:41:46 +0800920 calculationIface, objpath);
Patrick Williams82b39c62021-07-28 16:22:27 -0500921 info("Added a new virtual sensor: {NAME} {TYPE}", "NAME", name,
922 "TYPE", sensorType);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000923 virtualSensorPtr->updateVirtualSensor();
924
925 /* Initialize unit value for virtual sensor */
926 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
927 virtualSensorPtr->emit_object_added();
928
929 virtualSensorsMap.emplace(name, std::move(virtualSensorPtr));
930
931 /* Setup match for interfaces removed */
Patrick Williams1226f202023-05-10 07:51:16 -0500932 auto intfRemoved =
933 [this, objpath, name](sdbusplus::message_t& message) {
Rashmica Guptae7efe132021-07-27 19:42:11 +1000934 if (!virtualSensorsMap.contains(name))
935 {
936 return;
937 }
938 sdbusplus::message::object_path path;
939 message.read(path);
940 if (static_cast<const std::string&>(path) == objpath)
941 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500942 info("Removed a virtual sensor: {NAME}", "NAME", name);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000943 virtualSensorsMap.erase(name);
944 }
945 };
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500946 auto matchOnRemove = std::make_unique<sdbusplus::bus::match_t>(
Rashmica Guptae7efe132021-07-27 19:42:11 +1000947 bus,
948 sdbusplus::bus::match::rules::interfacesRemoved() +
949 sdbusplus::bus::match::rules::argNpath(0, objpath),
950 intfRemoved);
951 /* TODO: slight race condition here. Check that the config still
952 * exists */
953 this->matches.emplace_back(std::move(matchOnRemove));
954 }
Patrick Williamsdac26632021-10-06 14:38:47 -0500955 catch (const std::invalid_argument& ia)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000956 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500957 error("Failed to set up virtual sensor: {ERROR}", "ERROR", ia);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000958 }
959 }
960}
961
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700962void VirtualSensors::createVirtualSensors()
963{
964 static const Json empty{};
965
Patrick Williams32dff212023-02-09 13:54:18 -0600966 auto data = parseConfigFile();
Rashmica Guptae7efe132021-07-27 19:42:11 +1000967
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700968 // print values
969 if (DEBUG)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000970 {
Patrick Williamsfbd71452021-08-30 06:59:46 -0500971 debug("JSON: {JSON}", "JSON", data.dump());
Rashmica Guptae7efe132021-07-27 19:42:11 +1000972 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700973
974 /* Get virtual sensors config data */
975 for (const auto& j : data)
976 {
977 auto desc = j.value("Desc", empty);
978 if (!desc.empty())
979 {
Rashmica Guptae7efe132021-07-27 19:42:11 +1000980 if (desc.value("Config", "") == "D-Bus")
981 {
982 /* Look on D-Bus for a virtual sensor config. Set up matches
983 * first because the configs may not be on D-Bus yet and we
984 * don't want to miss them */
985 setupMatches();
986
987 if (desc.contains("Type"))
988 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500989 auto type = desc.value("Type", "");
990 auto path = "xyz.openbmc_project.Configuration." + type;
991
Rashmica Guptae7efe132021-07-27 19:42:11 +1000992 if (!isCalculationType(path))
993 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500994 error("Invalid calculation type {TYPE} supplied.",
995 "TYPE", type);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000996 continue;
997 }
998 createVirtualSensorsFromDBus(path);
999 }
1000 continue;
1001 }
1002
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001003 std::string sensorType = desc.value("SensorType", "");
1004 std::string name = desc.value("Name", "");
Rashmica Gupta665a0a22021-06-30 11:35:28 +10001005 std::replace(name.begin(), name.end(), ' ', '_');
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001006
1007 if (!name.empty() && !sensorType.empty())
1008 {
Vijay Khemkae0d371e2020-09-21 18:35:52 -07001009 if (unitMap.find(sensorType) == unitMap.end())
1010 {
Patrick Williams82b39c62021-07-28 16:22:27 -05001011 error("Sensor type {TYPE} is not supported", "TYPE",
1012 sensorType);
Vijay Khemkae0d371e2020-09-21 18:35:52 -07001013 }
1014 else
1015 {
Rashmica Gupta67d8b9d2021-06-30 11:41:14 +10001016 if (virtualSensorsMap.find(name) != virtualSensorsMap.end())
1017 {
Patrick Williams82b39c62021-07-28 16:22:27 -05001018 error("A virtual sensor named {NAME} already exists",
1019 "NAME", name);
Rashmica Gupta67d8b9d2021-06-30 11:41:14 +10001020 continue;
1021 }
Rashmica Gupta862c3d12021-08-06 12:19:31 +10001022 auto objPath = sensorDbusPath + sensorType + "/" + name;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001023
Vijay Khemkae0d371e2020-09-21 18:35:52 -07001024 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
1025 bus, objPath.c_str(), j, name);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001026
Patrick Williams82b39c62021-07-28 16:22:27 -05001027 info("Added a new virtual sensor: {NAME}", "NAME", name);
Vijay Khemkae0d371e2020-09-21 18:35:52 -07001028 virtualSensorPtr->updateVirtualSensor();
1029
1030 /* Initialize unit value for virtual sensor */
1031 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
Rashmica Guptaa2fa63a2021-08-06 12:21:13 +10001032 virtualSensorPtr->emit_object_added();
Vijay Khemkae0d371e2020-09-21 18:35:52 -07001033
1034 virtualSensorsMap.emplace(std::move(name),
1035 std::move(virtualSensorPtr));
1036 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001037 }
1038 else
1039 {
Patrick Williams82b39c62021-07-28 16:22:27 -05001040 error(
1041 "Sensor type ({TYPE}) or name ({NAME}) not found in config file",
1042 "NAME", name, "TYPE", sensorType);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001043 }
1044 }
1045 else
1046 {
Patrick Williams82b39c62021-07-28 16:22:27 -05001047 error("Descriptor for new virtual sensor not found in config file");
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001048 }
1049 }
1050}
1051
1052} // namespace virtualSensor
1053} // namespace phosphor
1054
1055/**
1056 * @brief Main
1057 */
1058int main()
1059{
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001060 // Get a handle to system dbus
1061 auto bus = sdbusplus::bus::new_default();
1062
Matt Spinler6c19e7d2021-01-12 16:26:45 -06001063 // Add the ObjectManager interface
Ed Tanousf7ec40a2022-10-04 17:39:40 -07001064 sdbusplus::server::manager_t objManager(bus,
1065 "/xyz/openbmc_project/sensors");
Matt Spinler6c19e7d2021-01-12 16:26:45 -06001066
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001067 // Create an virtual sensors object
1068 phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
1069
1070 // Request service bus name
George Liu94921492023-08-21 16:18:14 +08001071 bus.request_name("xyz.openbmc_project.VirtualSensor");
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001072
Patrick Williamse6672392022-09-02 09:03:24 -05001073 // Run the dbus loop.
1074 bus.process_loop();
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001075
1076 return 0;
1077}