blob: 13786d817de8ce86443e6731ffecd2a2a1639d95 [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
Tao Linf2e94222023-10-31 17:38:17 +080017namespace phosphor::virtual_sensor
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070018{
19
Lei YU0ab9d832022-07-19 07:12:50 +000020FuncMaxIgnoreNaN<double> VirtualSensor::funcMaxIgnoreNaN;
Lei YU87d35112022-10-24 05:54:25 +000021FuncSumIgnoreNaN<double> VirtualSensor::funcSumIgnoreNaN;
Lei YUc77b6b32023-06-08 12:02:05 +000022FuncIfNan<double> VirtualSensor::funcIfNan;
Lei YU0ab9d832022-07-19 07:12:50 +000023
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070024void printParams(const VirtualSensor::ParamMap& paramMap)
25{
26 for (const auto& p : paramMap)
27 {
28 const auto& p1 = p.first;
29 const auto& p2 = p.second;
30 auto val = p2->getParamValue();
Patrick Williamsfbd71452021-08-30 06:59:46 -050031 debug("Parameter: {PARAM} = {VALUE}", "PARAM", p1, "VALUE", val);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070032 }
33}
34
35double SensorParam::getParamValue()
36{
37 switch (paramType)
38 {
39 case constParam:
40 return value;
41 break;
Vijay Khemka7452a862020-08-11 16:01:23 -070042 case dbusParam:
43 return dbusSensor->getSensorValue();
44 break;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070045 default:
46 throw std::invalid_argument("param type not supported");
47 }
48}
49
Lei YU0fcf0e12021-06-04 11:14:17 +080050using AssociationList =
51 std::vector<std::tuple<std::string, std::string, std::string>>;
52
53AssociationList getAssociationsFromJson(const Json& j)
54{
55 AssociationList assocs{};
56 try
57 {
58 j.get_to(assocs);
59 }
60 catch (const std::exception& ex)
61 {
Patrick Williams82b39c62021-07-28 16:22:27 -050062 error("Failed to parse association: {ERROR}", "ERROR", ex);
Lei YU0fcf0e12021-06-04 11:14:17 +080063 }
64 return assocs;
65}
66
Rashmica Guptae7efe132021-07-27 19:42:11 +100067template <typename U>
68struct VariantToNumber
69{
70 template <typename T>
71 U operator()(const T& t) const
72 {
73 if constexpr (std::is_convertible<T, U>::value)
74 {
75 return static_cast<U>(t);
76 }
77 throw std::invalid_argument("Invalid number type in config\n");
78 }
79};
80
81template <typename U>
82U getNumberFromConfig(const PropertyMap& map, const std::string& name,
Jiaqing Zhao190f6d02022-05-21 23:26:15 +080083 bool required,
84 U defaultValue = std::numeric_limits<U>::quiet_NaN())
Rashmica Guptae7efe132021-07-27 19:42:11 +100085{
86 if (auto itr = map.find(name); itr != map.end())
87 {
88 return std::visit(VariantToNumber<U>(), itr->second);
89 }
90 else if (required)
91 {
Patrick Williams82b39c62021-07-28 16:22:27 -050092 error("Required field {NAME} missing in config", "NAME", name);
Rashmica Guptae7efe132021-07-27 19:42:11 +100093 throw std::invalid_argument("Required field missing in config");
94 }
Jiaqing Zhao190f6d02022-05-21 23:26:15 +080095 return defaultValue;
Rashmica Guptae7efe132021-07-27 19:42:11 +100096}
97
98bool isCalculationType(const std::string& interface)
99{
100 auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
101 interface);
102 if (itr != calculationIfaces.end())
103 {
104 return true;
105 }
106 return false;
107}
108
109const std::string getThresholdType(const std::string& direction,
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100110 const std::string& severity)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000111{
Rashmica Guptae7efe132021-07-27 19:42:11 +1000112 std::string suffix;
Rashmica Guptae7efe132021-07-27 19:42:11 +1000113
114 if (direction == "less than")
115 {
116 suffix = "Low";
117 }
118 else if (direction == "greater than")
119 {
120 suffix = "High";
121 }
122 else
123 {
124 throw std::invalid_argument(
125 "Invalid threshold direction specified in entity manager");
126 }
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100127 return severity + suffix;
128}
129
130std::string getSeverityField(const PropertyMap& propertyMap)
131{
Patrick Williams150d5f62024-08-16 15:21:45 -0400132 static const std::array thresholdTypes{
133 "Warning", "Critical", "PerformanceLoss", "SoftShutdown",
134 "HardShutdown"};
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100135
136 std::string severity;
137 if (auto itr = propertyMap.find("Severity"); itr != propertyMap.end())
138 {
139 /* Severity should be a string, but can be an unsigned int */
140 if (std::holds_alternative<std::string>(itr->second))
141 {
142 severity = std::get<std::string>(itr->second);
143 if (0 == std::ranges::count(thresholdTypes, severity))
144 {
145 throw std::invalid_argument(
146 "Invalid threshold severity specified in entity manager");
147 }
148 }
149 else
150 {
Patrick Williams150d5f62024-08-16 15:21:45 -0400151 auto sev =
152 getNumberFromConfig<uint64_t>(propertyMap, "Severity", true);
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100153 /* Checking bounds ourselves so we throw invalid argument on
154 * invalid user input */
155 if (sev >= thresholdTypes.size())
156 {
157 throw std::invalid_argument(
158 "Invalid threshold severity specified in entity manager");
159 }
160 severity = thresholdTypes.at(sev);
161 }
162 }
163 return severity;
Rashmica Guptae7efe132021-07-27 19:42:11 +1000164}
165
Tao Lin91799db2022-07-27 21:02:20 +0800166void parseThresholds(Json& thresholds, const PropertyMap& propertyMap,
167 const std::string& entityInterface = "")
Rashmica Guptae7efe132021-07-27 19:42:11 +1000168{
169 std::string direction;
170
Rashmica Guptae7efe132021-07-27 19:42:11 +1000171 auto value = getNumberFromConfig<double>(propertyMap, "Value", true);
172
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100173 auto severity = getSeverityField(propertyMap);
174
175 if (auto itr = propertyMap.find("Direction"); itr != propertyMap.end())
Rashmica Guptae7efe132021-07-27 19:42:11 +1000176 {
177 direction = std::get<std::string>(itr->second);
178 }
179
180 auto threshold = getThresholdType(direction, severity);
181 thresholds[threshold] = value;
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000182
Patrick Williams150d5f62024-08-16 15:21:45 -0400183 auto hysteresis =
184 getNumberFromConfig<double>(propertyMap, "Hysteresis", false);
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000185 if (hysteresis != std::numeric_limits<double>::quiet_NaN())
186 {
187 thresholds[threshold + "Hysteresis"] = hysteresis;
188 }
Tao Lin91799db2022-07-27 21:02:20 +0800189
190 if (!entityInterface.empty())
191 {
192 thresholds[threshold + "Direction"] = entityInterface;
193 }
Rashmica Guptae7efe132021-07-27 19:42:11 +1000194}
195
196void VirtualSensor::parseConfigInterface(const PropertyMap& propertyMap,
197 const std::string& sensorType,
198 const std::string& interface)
199{
200 /* Parse sensors / DBus params */
201 if (auto itr = propertyMap.find("Sensors"); itr != propertyMap.end())
202 {
203 auto sensors = std::get<std::vector<std::string>>(itr->second);
204 for (auto sensor : sensors)
205 {
206 std::replace(sensor.begin(), sensor.end(), ' ', '_');
207 auto sensorObjPath = sensorDbusPath + sensorType + "/" + sensor;
208
Patrick Williams150d5f62024-08-16 15:21:45 -0400209 auto paramPtr =
210 std::make_unique<SensorParam>(bus, sensorObjPath, *this);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000211 symbols.create_variable(sensor);
212 paramMap.emplace(std::move(sensor), std::move(paramPtr));
213 }
214 }
215 /* Get expression string */
216 if (!isCalculationType(interface))
217 {
218 throw std::invalid_argument("Invalid expression in interface");
219 }
220 exprStr = interface;
221
222 /* Get optional min and max input and output values */
223 ValueIface::maxValue(
224 getNumberFromConfig<double>(propertyMap, "MaxValue", false));
225 ValueIface::minValue(
226 getNumberFromConfig<double>(propertyMap, "MinValue", false));
227 maxValidInput =
Jiaqing Zhao190f6d02022-05-21 23:26:15 +0800228 getNumberFromConfig<double>(propertyMap, "MaxValidInput", false,
229 std::numeric_limits<double>::infinity());
Rashmica Guptae7efe132021-07-27 19:42:11 +1000230 minValidInput =
Jiaqing Zhao190f6d02022-05-21 23:26:15 +0800231 getNumberFromConfig<double>(propertyMap, "MinValidInput", false,
232 -std::numeric_limits<double>::infinity());
Rashmica Guptae7efe132021-07-27 19:42:11 +1000233}
234
Matt Spinlerce675222021-01-14 16:38:09 -0600235void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
236 const std::string& objPath)
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700237{
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700238 static const Json empty{};
239
240 /* Get threshold values if defined in config */
241 auto threshold = sensorConfig.value("Threshold", empty);
Matt Spinlerf15189e2021-01-15 10:13:28 -0600242
Rashmica Gupta3e999192021-06-09 16:17:04 +1000243 createThresholds(threshold, objPath);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700244
Harvey Wuf6443742021-04-09 16:47:36 +0800245 /* Get MaxValue, MinValue setting if defined in config */
246 auto confDesc = sensorConfig.value("Desc", empty);
247 if (auto maxConf = confDesc.find("MaxValue");
248 maxConf != confDesc.end() && maxConf->is_number())
249 {
250 ValueIface::maxValue(maxConf->get<double>());
251 }
252 if (auto minConf = confDesc.find("MinValue");
253 minConf != confDesc.end() && minConf->is_number())
254 {
255 ValueIface::minValue(minConf->get<double>());
256 }
257
Lei YU0fcf0e12021-06-04 11:14:17 +0800258 /* Get optional association */
259 auto assocJson = sensorConfig.value("Associations", empty);
260 if (!assocJson.empty())
261 {
262 auto assocs = getAssociationsFromJson(assocJson);
263 if (!assocs.empty())
264 {
265 associationIface =
266 std::make_unique<AssociationObject>(bus, objPath.c_str());
267 associationIface->associations(assocs);
268 }
269 }
270
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700271 /* Get expression string */
Patrick Williams03c4c8e2022-04-14 22:19:44 -0500272 static constexpr auto exprKey = "Expression";
273 if (sensorConfig.contains(exprKey))
274 {
Patrick Williamsa9596782022-04-15 10:20:07 -0500275 auto& ref = sensorConfig.at(exprKey);
Patrick Williams03c4c8e2022-04-14 22:19:44 -0500276 if (ref.is_array())
277 {
278 exprStr = std::string{};
279 for (auto& s : ref)
280 {
281 exprStr += s;
282 }
283 }
284 else if (ref.is_string())
285 {
286 exprStr = std::string{ref};
287 }
288 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700289
290 /* Get all the parameter listed in configuration */
291 auto params = sensorConfig.value("Params", empty);
292
293 /* Check for constant parameter */
294 const auto& consParams = params.value("ConstParam", empty);
295 if (!consParams.empty())
296 {
297 for (auto& j : consParams)
298 {
299 if (j.find("ParamName") != j.end())
300 {
301 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700302 std::string name = j["ParamName"];
303 symbols.create_variable(name);
304 paramMap.emplace(std::move(name), std::move(paramPtr));
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700305 }
306 else
307 {
308 /* Invalid configuration */
309 throw std::invalid_argument(
310 "ParamName not found in configuration");
311 }
312 }
313 }
314
Vijay Khemka7452a862020-08-11 16:01:23 -0700315 /* Check for dbus parameter */
316 auto dbusParams = params.value("DbusParam", empty);
317 if (!dbusParams.empty())
318 {
319 for (auto& j : dbusParams)
320 {
321 /* Get parameter dbus sensor descriptor */
322 auto desc = j.value("Desc", empty);
323 if ((!desc.empty()) && (j.find("ParamName") != j.end()))
324 {
325 std::string sensorType = desc.value("SensorType", "");
326 std::string name = desc.value("Name", "");
327
328 if (!sensorType.empty() && !name.empty())
329 {
George Liu1204b432021-12-29 17:24:48 +0800330 auto path = sensorDbusPath + sensorType + "/" + name;
Vijay Khemka7452a862020-08-11 16:01:23 -0700331
Patrick Williams150d5f62024-08-16 15:21:45 -0400332 auto paramPtr =
333 std::make_unique<SensorParam>(bus, path, *this);
George Liu1204b432021-12-29 17:24:48 +0800334 std::string paramName = j["ParamName"];
335 symbols.create_variable(paramName);
336 paramMap.emplace(std::move(paramName), std::move(paramPtr));
Vijay Khemka7452a862020-08-11 16:01:23 -0700337 }
338 }
339 }
340 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700341
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700342 symbols.add_constants();
Matt Spinler9f1ef4f2020-11-09 15:59:11 -0600343 symbols.add_package(vecopsPackage);
Lei YU0ab9d832022-07-19 07:12:50 +0000344 symbols.add_function("maxIgnoreNaN", funcMaxIgnoreNaN);
Lei YU87d35112022-10-24 05:54:25 +0000345 symbols.add_function("sumIgnoreNaN", funcSumIgnoreNaN);
Lei YUc77b6b32023-06-08 12:02:05 +0000346 symbols.add_function("ifNan", funcIfNan);
Lei YU0ab9d832022-07-19 07:12:50 +0000347
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700348 expression.register_symbol_table(symbols);
349
350 /* parser from exprtk */
351 exprtk::parser<double> parser{};
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600352 if (!parser.compile(exprStr, expression))
353 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500354 error("Expression compilation failed");
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600355
356 for (std::size_t i = 0; i < parser.error_count(); ++i)
357 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500358 auto err = parser.get_error(i);
359 error("Error parsing token at {POSITION}: {ERROR}", "POSITION",
360 err.token.position, "TYPE",
361 exprtk::parser_error::to_str(err.mode), "ERROR",
362 err.diagnostic);
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600363 }
364 throw std::runtime_error("Expression compilation failed");
365 }
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700366
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700367 /* Print all parameters for debug purpose only */
368 if (DEBUG)
369 printParams(paramMap);
370}
371
Tao Lindc777012022-07-27 20:41:46 +0800372void VirtualSensor::createAssociation(const std::string& objPath,
373 const std::string& entityPath)
374{
375 if (objPath.empty() || entityPath.empty())
376 {
377 return;
378 }
379
380 std::filesystem::path p(entityPath);
381 auto assocsDbus =
382 AssociationList{{"chassis", "all_sensors", p.parent_path().string()}};
Patrick Williams150d5f62024-08-16 15:21:45 -0400383 associationIface =
384 std::make_unique<AssociationObject>(bus, objPath.c_str());
Tao Lindc777012022-07-27 20:41:46 +0800385 associationIface->associations(assocsDbus);
386}
387
Patrick Williams150d5f62024-08-16 15:21:45 -0400388void VirtualSensor::initVirtualSensor(
389 const InterfaceMap& interfaceMap, const std::string& objPath,
390 const std::string& sensorType, const std::string& calculationIface)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000391{
392 Json thresholds;
Patrick Williams150d5f62024-08-16 15:21:45 -0400393 const std::string vsThresholdsIntf =
394 calculationIface + vsThresholdsIfaceSuffix;
Rashmica Guptae7efe132021-07-27 19:42:11 +1000395
396 for (const auto& [interface, propertyMap] : interfaceMap)
397 {
398 /* Each threshold is on it's own interface with a number as a suffix
399 * eg xyz.openbmc_project.Configuration.ModifiedMedian.Thresholds1 */
400 if (interface.find(vsThresholdsIntf) != std::string::npos)
401 {
Tao Lin91799db2022-07-27 21:02:20 +0800402 parseThresholds(thresholds, propertyMap, interface);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000403 }
404 else if (interface == calculationIface)
405 {
406 parseConfigInterface(propertyMap, sensorType, interface);
407 }
408 }
409
410 createThresholds(thresholds, objPath);
411 symbols.add_constants();
412 symbols.add_package(vecopsPackage);
413 expression.register_symbol_table(symbols);
414
Tao Lindc777012022-07-27 20:41:46 +0800415 createAssociation(objPath, entityPath);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000416 /* Print all parameters for debug purpose only */
417 if (DEBUG)
418 {
419 printParams(paramMap);
420 }
421}
422
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700423void VirtualSensor::setSensorValue(double value)
424{
Patrick Williams543bf662021-04-29 09:03:53 -0500425 value = std::clamp(value, ValueIface::minValue(), ValueIface::maxValue());
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700426 ValueIface::value(value);
427}
428
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000429double VirtualSensor::calculateValue(const std::string& calculation,
430 const VirtualSensor::ParamMap& paramMap)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000431{
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000432 auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
433 calculation);
434 if (itr == calculationIfaces.end())
435 {
436 return std::numeric_limits<double>::quiet_NaN();
437 }
438 else if (calculation == "xyz.openbmc_project.Configuration.ModifiedMedian")
439 {
440 return calculateModifiedMedianValue(paramMap);
441 }
Tao Linf6b7e0a2022-10-09 09:35:44 +0800442 else if (calculation == "xyz.openbmc_project.Configuration.Maximum")
443 {
444 return calculateMaximumValue(paramMap);
445 }
Rashmica Guptae7efe132021-07-27 19:42:11 +1000446 return std::numeric_limits<double>::quiet_NaN();
447}
448
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000449bool VirtualSensor::sensorInRange(double value)
450{
451 if (value <= this->maxValidInput && value >= this->minValidInput)
452 {
453 return true;
454 }
455 return false;
456}
457
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700458void VirtualSensor::updateVirtualSensor()
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700459{
460 for (auto& param : paramMap)
461 {
462 auto& name = param.first;
463 auto& data = param.second;
464 if (auto var = symbols.get_variable(name))
465 {
466 var->ref() = data->getParamValue();
467 }
468 else
469 {
470 /* Invalid parameter */
471 throw std::invalid_argument("ParamName not found in symbols");
472 }
473 }
Patrick Williams150d5f62024-08-16 15:21:45 -0400474 auto itr =
475 std::find(calculationIfaces.begin(), calculationIfaces.end(), exprStr);
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000476 auto val = (itr == calculationIfaces.end())
477 ? expression.value()
478 : calculateValue(exprStr, paramMap);
Vijay Khemka32a71562020-09-10 15:29:18 -0700479
480 /* Set sensor value to dbus interface */
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700481 setSensorValue(val);
Vijay Khemka32a71562020-09-10 15:29:18 -0700482
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700483 if (DEBUG)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000484 {
Patrick Williamsfbd71452021-08-30 06:59:46 -0500485 debug("Sensor {NAME} = {VALUE}", "NAME", this->name, "VALUE", val);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000486 }
Vijay Khemka32a71562020-09-10 15:29:18 -0700487
Matt Spinler8f5e6112021-01-15 10:44:32 -0600488 /* Check sensor thresholds and log required message */
Matt Spinlerb306b032021-02-01 10:05:46 -0600489 checkThresholds(val, perfLossIface);
Patrick Williamsfdb826d2021-01-20 14:37:53 -0600490 checkThresholds(val, warningIface);
491 checkThresholds(val, criticalIface);
492 checkThresholds(val, softShutdownIface);
493 checkThresholds(val, hardShutdownIface);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700494}
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700495
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000496double VirtualSensor::calculateModifiedMedianValue(
497 const VirtualSensor::ParamMap& paramMap)
498{
499 std::vector<double> values;
500
501 for (auto& param : paramMap)
502 {
503 auto& name = param.first;
504 if (auto var = symbols.get_variable(name))
505 {
506 if (!sensorInRange(var->ref()))
507 {
508 continue;
509 }
510 values.push_back(var->ref());
511 }
512 }
513
514 size_t size = values.size();
515 std::sort(values.begin(), values.end());
516 switch (size)
517 {
518 case 2:
519 /* Choose biggest value */
520 return values.at(1);
521 case 0:
522 return std::numeric_limits<double>::quiet_NaN();
523 default:
524 /* Choose median value */
525 if (size % 2 == 0)
526 {
527 // Average of the two middle values
528 return (values.at(size / 2) + values.at(size / 2 - 1)) / 2;
529 }
530 else
531 {
532 return values.at((size - 1) / 2);
533 }
534 }
535}
536
Tao Linf6b7e0a2022-10-09 09:35:44 +0800537double VirtualSensor::calculateMaximumValue(
538 const VirtualSensor::ParamMap& paramMap)
539{
540 std::vector<double> values;
541
542 for (auto& param : paramMap)
543 {
544 auto& name = param.first;
545 if (auto var = symbols.get_variable(name))
546 {
547 if (!sensorInRange(var->ref()))
548 {
549 continue;
550 }
551 values.push_back(var->ref());
552 }
553 }
554 auto maxIt = std::max_element(values.begin(), values.end());
555 if (maxIt == values.end())
556 {
557 return std::numeric_limits<double>::quiet_NaN();
558 }
559 return *maxIt;
560}
561
Rashmica Gupta3e999192021-06-09 16:17:04 +1000562void VirtualSensor::createThresholds(const Json& threshold,
563 const std::string& objPath)
564{
565 if (threshold.empty())
566 {
567 return;
568 }
569 // Only create the threshold interfaces if
570 // at least one of their values is present.
571 if (threshold.contains("CriticalHigh") || threshold.contains("CriticalLow"))
572 {
573 criticalIface =
574 std::make_unique<Threshold<CriticalObject>>(bus, objPath.c_str());
575
Tao Lin91799db2022-07-27 21:02:20 +0800576 if (threshold.contains("CriticalHigh"))
577 {
578 criticalIface->setEntityInterfaceHigh(
579 threshold.value("CriticalHighDirection", ""));
580 if (DEBUG)
581 {
582 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
583 "INTF", threshold.value("CriticalHighDirection", ""));
584 }
585 }
586 if (threshold.contains("CriticalLow"))
587 {
588 criticalIface->setEntityInterfaceLow(
589 threshold.value("CriticalLowDirection", ""));
590 if (DEBUG)
591 {
592 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
593 "INTF", threshold.value("CriticalLowDirection", ""));
594 }
595 }
596
597 criticalIface->setEntityPath(entityPath);
598 if (DEBUG)
599 {
600 debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath,
601 "PATH", entityPath);
602 }
Matt Spinlera291ce12023-02-06 15:12:44 -0600603
604 criticalIface->criticalHigh(threshold.value(
605 "CriticalHigh", std::numeric_limits<double>::quiet_NaN()));
606 criticalIface->criticalLow(threshold.value(
607 "CriticalLow", std::numeric_limits<double>::quiet_NaN()));
608 criticalIface->setHighHysteresis(
609 threshold.value("CriticalHighHysteresis", defaultHysteresis));
610 criticalIface->setLowHysteresis(
611 threshold.value("CriticalLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000612 }
613
614 if (threshold.contains("WarningHigh") || threshold.contains("WarningLow"))
615 {
616 warningIface =
617 std::make_unique<Threshold<WarningObject>>(bus, objPath.c_str());
618
Tao Lin91799db2022-07-27 21:02:20 +0800619 if (threshold.contains("WarningHigh"))
620 {
621 warningIface->setEntityInterfaceHigh(
622 threshold.value("WarningHighDirection", ""));
623 if (DEBUG)
624 {
625 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
626 "INTF", threshold.value("WarningHighDirection", ""));
627 }
628 }
629 if (threshold.contains("WarningLow"))
630 {
631 warningIface->setEntityInterfaceLow(
632 threshold.value("WarningLowDirection", ""));
633 if (DEBUG)
634 {
635 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
636 "INTF", threshold.value("WarningLowDirection", ""));
637 }
638 }
639
640 warningIface->setEntityPath(entityPath);
641 if (DEBUG)
642 {
643 debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath,
644 "PATH", entityPath);
645 }
Matt Spinlera291ce12023-02-06 15:12:44 -0600646
647 warningIface->warningHigh(threshold.value(
648 "WarningHigh", std::numeric_limits<double>::quiet_NaN()));
649 warningIface->warningLow(threshold.value(
650 "WarningLow", std::numeric_limits<double>::quiet_NaN()));
651 warningIface->setHighHysteresis(
652 threshold.value("WarningHighHysteresis", defaultHysteresis));
653 warningIface->setLowHysteresis(
654 threshold.value("WarningLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000655 }
656
657 if (threshold.contains("HardShutdownHigh") ||
658 threshold.contains("HardShutdownLow"))
659 {
660 hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>(
661 bus, objPath.c_str());
662
663 hardShutdownIface->hardShutdownHigh(threshold.value(
664 "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
665 hardShutdownIface->hardShutdownLow(threshold.value(
666 "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000667 hardShutdownIface->setHighHysteresis(
668 threshold.value("HardShutdownHighHysteresis", defaultHysteresis));
669 hardShutdownIface->setLowHysteresis(
670 threshold.value("HardShutdownLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000671 }
672
673 if (threshold.contains("SoftShutdownHigh") ||
674 threshold.contains("SoftShutdownLow"))
675 {
676 softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>(
677 bus, objPath.c_str());
678
679 softShutdownIface->softShutdownHigh(threshold.value(
680 "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
681 softShutdownIface->softShutdownLow(threshold.value(
682 "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000683 softShutdownIface->setHighHysteresis(
684 threshold.value("SoftShutdownHighHysteresis", defaultHysteresis));
685 softShutdownIface->setLowHysteresis(
686 threshold.value("SoftShutdownLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000687 }
688
689 if (threshold.contains("PerformanceLossHigh") ||
690 threshold.contains("PerformanceLossLow"))
691 {
692 perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>(
693 bus, objPath.c_str());
694
695 perfLossIface->performanceLossHigh(threshold.value(
696 "PerformanceLossHigh", std::numeric_limits<double>::quiet_NaN()));
697 perfLossIface->performanceLossLow(threshold.value(
698 "PerformanceLossLow", std::numeric_limits<double>::quiet_NaN()));
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000699 perfLossIface->setHighHysteresis(threshold.value(
700 "PerformanceLossHighHysteresis", defaultHysteresis));
701 perfLossIface->setLowHysteresis(
702 threshold.value("PerformanceLossLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000703 }
704}
705
Rashmica Guptae7efe132021-07-27 19:42:11 +1000706ManagedObjectType VirtualSensors::getObjectsFromDBus()
707{
708 ManagedObjectType objects;
709
710 try
711 {
Patrick Williams150d5f62024-08-16 15:21:45 -0400712 auto method = bus.new_method_call(
713 "xyz.openbmc_project.EntityManager",
714 "/xyz/openbmc_project/inventory",
715 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rashmica Guptae7efe132021-07-27 19:42:11 +1000716 auto reply = bus.call(method);
717 reply.read(objects);
718 }
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500719 catch (const sdbusplus::exception_t& ex)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000720 {
721 // If entity manager isn't running yet, keep going.
722 if (std::string("org.freedesktop.DBus.Error.ServiceUnknown") !=
723 ex.name())
724 {
Matt Spinler71b9c112022-10-18 09:14:45 -0500725 error("Could not reach entity-manager: {ERROR}", "ERROR", ex);
726 throw;
Rashmica Guptae7efe132021-07-27 19:42:11 +1000727 }
728 }
729
730 return objects;
731}
732
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500733void VirtualSensors::propertiesChanged(sdbusplus::message_t& msg)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000734{
735 std::string path;
736 PropertyMap properties;
737
738 msg.read(path, properties);
739
740 /* We get multiple callbacks for one sensor. 'Type' is a required field and
741 * is a unique label so use to to only proceed once per sensor */
742 if (properties.contains("Type"))
743 {
744 if (isCalculationType(path))
745 {
746 createVirtualSensorsFromDBus(path);
747 }
748 }
749}
750
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700751/** @brief Parsing Virtual Sensor config JSON file */
Patrick Williams32dff212023-02-09 13:54:18 -0600752Json VirtualSensors::parseConfigFile()
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700753{
Patrick Williams32dff212023-02-09 13:54:18 -0600754 using path = std::filesystem::path;
755 auto configFile = []() -> path {
756 static constexpr auto name = "virtual_sensor_config.json";
757
758 for (auto pathSeg : {std::filesystem::current_path(),
759 path{"/var/lib/phosphor-virtual-sensor"},
760 path{"/usr/share/phosphor-virtual-sensor"}})
761 {
762 auto file = pathSeg / name;
763 if (std::filesystem::exists(file))
764 {
765 return file;
766 }
767 }
768 return name;
769 }();
770
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700771 std::ifstream jsonFile(configFile);
772 if (!jsonFile.is_open())
773 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500774 error("config JSON file {FILENAME} not found", "FILENAME", configFile);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000775 return {};
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700776 }
777
778 auto data = Json::parse(jsonFile, nullptr, false);
779 if (data.is_discarded())
780 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500781 error("config readings JSON parser failure with {FILENAME}", "FILENAME",
782 configFile);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700783 throw std::exception{};
784 }
785
786 return data;
787}
788
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700789std::map<std::string, ValueIface::Unit> unitMap = {
790 {"temperature", ValueIface::Unit::DegreesC},
791 {"fan_tach", ValueIface::Unit::RPMS},
Konstantin Aladyshev9358f6b2024-03-14 14:23:40 +0300792 {"fan_pwm", ValueIface::Unit::Percent},
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700793 {"voltage", ValueIface::Unit::Volts},
794 {"altitude", ValueIface::Unit::Meters},
795 {"current", ValueIface::Unit::Amperes},
796 {"power", ValueIface::Unit::Watts},
797 {"energy", ValueIface::Unit::Joules},
Kumar Thangavel2b56ddb2021-01-13 20:16:11 +0530798 {"utilization", ValueIface::Unit::Percent},
Rashmica Gupta4ac7a7f2021-07-08 12:30:50 +1000799 {"airflow", ValueIface::Unit::CFM},
800 {"pressure", ValueIface::Unit::Pascals}};
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700801
Rashmica Guptae7efe132021-07-27 19:42:11 +1000802const std::string getSensorTypeFromUnit(const std::string& unit)
803{
804 std::string unitPrefix = "xyz.openbmc_project.Sensor.Value.Unit.";
805 for (auto [type, unitObj] : unitMap)
806 {
807 auto unitPath = ValueIface::convertUnitToString(unitObj);
808 if (unitPath == (unitPrefix + unit))
809 {
810 return type;
811 }
812 }
813 return "";
814}
815
816void VirtualSensors::setupMatches()
817{
818 /* Already setup */
819 if (!this->matches.empty())
820 {
821 return;
822 }
823
824 /* Setup matches */
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500825 auto eventHandler = [this](sdbusplus::message_t& message) {
Rashmica Guptae7efe132021-07-27 19:42:11 +1000826 if (message.is_method_error())
827 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500828 error("Callback method error");
Rashmica Guptae7efe132021-07-27 19:42:11 +1000829 return;
830 }
831 this->propertiesChanged(message);
832 };
833
834 for (const char* iface : calculationIfaces)
835 {
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500836 auto match = std::make_unique<sdbusplus::bus::match_t>(
Rashmica Guptae7efe132021-07-27 19:42:11 +1000837 bus,
838 sdbusplus::bus::match::rules::propertiesChangedNamespace(
839 "/xyz/openbmc_project/inventory", iface),
840 eventHandler);
841 this->matches.emplace_back(std::move(match));
842 }
843}
844
845void VirtualSensors::createVirtualSensorsFromDBus(
846 const std::string& calculationIface)
847{
848 if (calculationIface.empty())
849 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500850 error("No calculation type supplied");
Rashmica Guptae7efe132021-07-27 19:42:11 +1000851 return;
852 }
853 auto objects = getObjectsFromDBus();
854
855 /* Get virtual sensors config data */
856 for (const auto& [path, interfaceMap] : objects)
857 {
Rashmica Guptae7efe132021-07-27 19:42:11 +1000858 /* Find Virtual Sensor interfaces */
George Liu2db8d412023-08-21 16:41:04 +0800859 auto intfIter = interfaceMap.find(calculationIface);
860 if (intfIter == interfaceMap.end())
Rashmica Guptae7efe132021-07-27 19:42:11 +1000861 {
862 continue;
863 }
George Liu2db8d412023-08-21 16:41:04 +0800864
865 std::string name = path.filename();
Rashmica Guptae7efe132021-07-27 19:42:11 +1000866 if (name.empty())
867 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500868 error("Virtual Sensor name not found in entity manager config");
Rashmica Guptae7efe132021-07-27 19:42:11 +1000869 continue;
870 }
871 if (virtualSensorsMap.contains(name))
872 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500873 error("A virtual sensor named {NAME} already exists", "NAME", name);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000874 continue;
875 }
876
877 /* Extract the virtual sensor type as we need this to initialize the
878 * sensor */
George Liu2db8d412023-08-21 16:41:04 +0800879 std::string sensorType, sensorUnit;
880 auto propertyMap = intfIter->second;
881 auto proIter = propertyMap.find("Units");
882 if (proIter != propertyMap.end())
Rashmica Guptae7efe132021-07-27 19:42:11 +1000883 {
George Liu2db8d412023-08-21 16:41:04 +0800884 sensorUnit = std::get<std::string>(proIter->second);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000885 }
886 sensorType = getSensorTypeFromUnit(sensorUnit);
887 if (sensorType.empty())
888 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500889 error("Sensor unit type {TYPE} is not supported", "TYPE",
890 sensorUnit);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000891 continue;
892 }
893
894 try
895 {
George Liu2db8d412023-08-21 16:41:04 +0800896 auto objpath = static_cast<std::string>(path);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000897 auto virtObjPath = sensorDbusPath + sensorType + "/" + name;
898
899 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
900 bus, virtObjPath.c_str(), interfaceMap, name, sensorType,
Tao Lindc777012022-07-27 20:41:46 +0800901 calculationIface, objpath);
Patrick Williams82b39c62021-07-28 16:22:27 -0500902 info("Added a new virtual sensor: {NAME} {TYPE}", "NAME", name,
903 "TYPE", sensorType);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000904 virtualSensorPtr->updateVirtualSensor();
905
906 /* Initialize unit value for virtual sensor */
907 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
908 virtualSensorPtr->emit_object_added();
909
910 virtualSensorsMap.emplace(name, std::move(virtualSensorPtr));
911
912 /* Setup match for interfaces removed */
Patrick Williamsae10c522023-10-20 11:19:44 -0500913 auto intfRemoved = [this, objpath,
914 name](sdbusplus::message_t& message) {
Rashmica Guptae7efe132021-07-27 19:42:11 +1000915 if (!virtualSensorsMap.contains(name))
916 {
917 return;
918 }
919 sdbusplus::message::object_path path;
920 message.read(path);
921 if (static_cast<const std::string&>(path) == objpath)
922 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500923 info("Removed a virtual sensor: {NAME}", "NAME", name);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000924 virtualSensorsMap.erase(name);
925 }
926 };
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500927 auto matchOnRemove = std::make_unique<sdbusplus::bus::match_t>(
Rashmica Guptae7efe132021-07-27 19:42:11 +1000928 bus,
929 sdbusplus::bus::match::rules::interfacesRemoved() +
930 sdbusplus::bus::match::rules::argNpath(0, objpath),
931 intfRemoved);
932 /* TODO: slight race condition here. Check that the config still
933 * exists */
934 this->matches.emplace_back(std::move(matchOnRemove));
935 }
Patrick Williamsdac26632021-10-06 14:38:47 -0500936 catch (const std::invalid_argument& ia)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000937 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500938 error("Failed to set up virtual sensor: {ERROR}", "ERROR", ia);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000939 }
940 }
941}
942
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700943void VirtualSensors::createVirtualSensors()
944{
945 static const Json empty{};
946
Patrick Williams32dff212023-02-09 13:54:18 -0600947 auto data = parseConfigFile();
Rashmica Guptae7efe132021-07-27 19:42:11 +1000948
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700949 // print values
950 if (DEBUG)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000951 {
Patrick Williamsfbd71452021-08-30 06:59:46 -0500952 debug("JSON: {JSON}", "JSON", data.dump());
Rashmica Guptae7efe132021-07-27 19:42:11 +1000953 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700954
955 /* Get virtual sensors config data */
956 for (const auto& j : data)
957 {
958 auto desc = j.value("Desc", empty);
959 if (!desc.empty())
960 {
Rashmica Guptae7efe132021-07-27 19:42:11 +1000961 if (desc.value("Config", "") == "D-Bus")
962 {
963 /* Look on D-Bus for a virtual sensor config. Set up matches
964 * first because the configs may not be on D-Bus yet and we
965 * don't want to miss them */
966 setupMatches();
967
968 if (desc.contains("Type"))
969 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500970 auto type = desc.value("Type", "");
971 auto path = "xyz.openbmc_project.Configuration." + type;
972
Rashmica Guptae7efe132021-07-27 19:42:11 +1000973 if (!isCalculationType(path))
974 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500975 error("Invalid calculation type {TYPE} supplied.",
976 "TYPE", type);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000977 continue;
978 }
979 createVirtualSensorsFromDBus(path);
980 }
981 continue;
982 }
983
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700984 std::string sensorType = desc.value("SensorType", "");
985 std::string name = desc.value("Name", "");
Rashmica Gupta665a0a22021-06-30 11:35:28 +1000986 std::replace(name.begin(), name.end(), ' ', '_');
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700987
988 if (!name.empty() && !sensorType.empty())
989 {
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700990 if (unitMap.find(sensorType) == unitMap.end())
991 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500992 error("Sensor type {TYPE} is not supported", "TYPE",
993 sensorType);
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700994 }
995 else
996 {
Rashmica Gupta67d8b9d2021-06-30 11:41:14 +1000997 if (virtualSensorsMap.find(name) != virtualSensorsMap.end())
998 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500999 error("A virtual sensor named {NAME} already exists",
1000 "NAME", name);
Rashmica Gupta67d8b9d2021-06-30 11:41:14 +10001001 continue;
1002 }
Rashmica Gupta862c3d12021-08-06 12:19:31 +10001003 auto objPath = sensorDbusPath + sensorType + "/" + name;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001004
Vijay Khemkae0d371e2020-09-21 18:35:52 -07001005 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
1006 bus, objPath.c_str(), j, name);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001007
Patrick Williams82b39c62021-07-28 16:22:27 -05001008 info("Added a new virtual sensor: {NAME}", "NAME", name);
Vijay Khemkae0d371e2020-09-21 18:35:52 -07001009 virtualSensorPtr->updateVirtualSensor();
1010
1011 /* Initialize unit value for virtual sensor */
1012 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
Rashmica Guptaa2fa63a2021-08-06 12:21:13 +10001013 virtualSensorPtr->emit_object_added();
Vijay Khemkae0d371e2020-09-21 18:35:52 -07001014
1015 virtualSensorsMap.emplace(std::move(name),
1016 std::move(virtualSensorPtr));
1017 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001018 }
1019 else
1020 {
Patrick Williams82b39c62021-07-28 16:22:27 -05001021 error(
1022 "Sensor type ({TYPE}) or name ({NAME}) not found in config file",
1023 "NAME", name, "TYPE", sensorType);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001024 }
1025 }
1026 else
1027 {
Patrick Williams82b39c62021-07-28 16:22:27 -05001028 error("Descriptor for new virtual sensor not found in config file");
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001029 }
1030 }
1031}
1032
Tao Linf2e94222023-10-31 17:38:17 +08001033} // namespace phosphor::virtual_sensor
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001034
1035/**
1036 * @brief Main
1037 */
1038int main()
1039{
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001040 // Get a handle to system dbus
1041 auto bus = sdbusplus::bus::new_default();
1042
Matt Spinler6c19e7d2021-01-12 16:26:45 -06001043 // Add the ObjectManager interface
Ed Tanousf7ec40a2022-10-04 17:39:40 -07001044 sdbusplus::server::manager_t objManager(bus,
1045 "/xyz/openbmc_project/sensors");
Matt Spinler6c19e7d2021-01-12 16:26:45 -06001046
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001047 // Create an virtual sensors object
Tao Linf2e94222023-10-31 17:38:17 +08001048 phosphor::virtual_sensor::VirtualSensors virtualSensors(bus);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001049
1050 // Request service bus name
George Liu94921492023-08-21 16:18:14 +08001051 bus.request_name("xyz.openbmc_project.VirtualSensor");
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001052
Patrick Williamse6672392022-09-02 09:03:24 -05001053 // Run the dbus loop.
1054 bus.process_loop();
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001055
1056 return 0;
1057}