blob: de8ddc024f8e30817ff01dfbb5c674c01697ef9e [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
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07007static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/";
Rashmica Guptae7efe132021-07-27 19:42:11 +10008static constexpr auto vsThresholdsIfaceSuffix = ".Thresholds";
Tao Linf6b7e0a2022-10-09 09:35:44 +08009static constexpr std::array<const char*, 2> calculationIfaces = {
10 "xyz.openbmc_project.Configuration.ModifiedMedian",
11 "xyz.openbmc_project.Configuration.Maximum"};
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +100012static constexpr auto defaultHysteresis = 0;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070013
Patrick Williams82b39c62021-07-28 16:22:27 -050014PHOSPHOR_LOG2_USING_WITH_FLAGS;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070015
Tao Linf2e94222023-10-31 17:38:17 +080016namespace phosphor::virtual_sensor
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070017{
18
Lei YU0ab9d832022-07-19 07:12:50 +000019FuncMaxIgnoreNaN<double> VirtualSensor::funcMaxIgnoreNaN;
Lei YU87d35112022-10-24 05:54:25 +000020FuncSumIgnoreNaN<double> VirtualSensor::funcSumIgnoreNaN;
Lei YUc77b6b32023-06-08 12:02:05 +000021FuncIfNan<double> VirtualSensor::funcIfNan;
Lei YU0ab9d832022-07-19 07:12:50 +000022
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070023void printParams(const VirtualSensor::ParamMap& paramMap)
24{
25 for (const auto& p : paramMap)
26 {
27 const auto& p1 = p.first;
28 const auto& p2 = p.second;
29 auto val = p2->getParamValue();
Patrick Williamsfbd71452021-08-30 06:59:46 -050030 debug("Parameter: {PARAM} = {VALUE}", "PARAM", p1, "VALUE", val);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070031 }
32}
33
34double SensorParam::getParamValue()
35{
36 switch (paramType)
37 {
38 case constParam:
39 return value;
40 break;
Vijay Khemka7452a862020-08-11 16:01:23 -070041 case dbusParam:
42 return dbusSensor->getSensorValue();
43 break;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070044 default:
45 throw std::invalid_argument("param type not supported");
46 }
47}
48
Lei YU0fcf0e12021-06-04 11:14:17 +080049using AssociationList =
50 std::vector<std::tuple<std::string, std::string, std::string>>;
51
52AssociationList getAssociationsFromJson(const Json& j)
53{
54 AssociationList assocs{};
55 try
56 {
57 j.get_to(assocs);
58 }
59 catch (const std::exception& ex)
60 {
Patrick Williams82b39c62021-07-28 16:22:27 -050061 error("Failed to parse association: {ERROR}", "ERROR", ex);
Lei YU0fcf0e12021-06-04 11:14:17 +080062 }
63 return assocs;
64}
65
Rashmica Guptae7efe132021-07-27 19:42:11 +100066template <typename U>
67struct VariantToNumber
68{
69 template <typename T>
70 U operator()(const T& t) const
71 {
72 if constexpr (std::is_convertible<T, U>::value)
73 {
74 return static_cast<U>(t);
75 }
76 throw std::invalid_argument("Invalid number type in config\n");
77 }
78};
79
80template <typename U>
81U getNumberFromConfig(const PropertyMap& map, const std::string& name,
Jiaqing Zhao190f6d02022-05-21 23:26:15 +080082 bool required,
83 U defaultValue = std::numeric_limits<U>::quiet_NaN())
Rashmica Guptae7efe132021-07-27 19:42:11 +100084{
85 if (auto itr = map.find(name); itr != map.end())
86 {
87 return std::visit(VariantToNumber<U>(), itr->second);
88 }
89 else if (required)
90 {
Patrick Williams82b39c62021-07-28 16:22:27 -050091 error("Required field {NAME} missing in config", "NAME", name);
Rashmica Guptae7efe132021-07-27 19:42:11 +100092 throw std::invalid_argument("Required field missing in config");
93 }
Jiaqing Zhao190f6d02022-05-21 23:26:15 +080094 return defaultValue;
Rashmica Guptae7efe132021-07-27 19:42:11 +100095}
96
97bool isCalculationType(const std::string& interface)
98{
99 auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
100 interface);
101 if (itr != calculationIfaces.end())
102 {
103 return true;
104 }
105 return false;
106}
107
108const std::string getThresholdType(const std::string& direction,
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100109 const std::string& severity)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000110{
Rashmica Guptae7efe132021-07-27 19:42:11 +1000111 std::string suffix;
Rashmica Guptae7efe132021-07-27 19:42:11 +1000112
113 if (direction == "less than")
114 {
115 suffix = "Low";
116 }
117 else if (direction == "greater than")
118 {
119 suffix = "High";
120 }
121 else
122 {
123 throw std::invalid_argument(
124 "Invalid threshold direction specified in entity manager");
125 }
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100126 return severity + suffix;
127}
128
129std::string getSeverityField(const PropertyMap& propertyMap)
130{
Patrick Williams150d5f62024-08-16 15:21:45 -0400131 static const std::array thresholdTypes{
132 "Warning", "Critical", "PerformanceLoss", "SoftShutdown",
133 "HardShutdown"};
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100134
135 std::string severity;
136 if (auto itr = propertyMap.find("Severity"); itr != propertyMap.end())
137 {
138 /* Severity should be a string, but can be an unsigned int */
139 if (std::holds_alternative<std::string>(itr->second))
140 {
141 severity = std::get<std::string>(itr->second);
142 if (0 == std::ranges::count(thresholdTypes, severity))
143 {
144 throw std::invalid_argument(
145 "Invalid threshold severity specified in entity manager");
146 }
147 }
148 else
149 {
Patrick Williams150d5f62024-08-16 15:21:45 -0400150 auto sev =
151 getNumberFromConfig<uint64_t>(propertyMap, "Severity", true);
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100152 /* Checking bounds ourselves so we throw invalid argument on
153 * invalid user input */
154 if (sev >= thresholdTypes.size())
155 {
156 throw std::invalid_argument(
157 "Invalid threshold severity specified in entity manager");
158 }
159 severity = thresholdTypes.at(sev);
160 }
161 }
162 return severity;
Rashmica Guptae7efe132021-07-27 19:42:11 +1000163}
164
Tao Lin91799db2022-07-27 21:02:20 +0800165void parseThresholds(Json& thresholds, const PropertyMap& propertyMap,
166 const std::string& entityInterface = "")
Rashmica Guptae7efe132021-07-27 19:42:11 +1000167{
168 std::string direction;
169
Rashmica Guptae7efe132021-07-27 19:42:11 +1000170 auto value = getNumberFromConfig<double>(propertyMap, "Value", true);
171
Rashmica Gupta05b1d412021-11-03 12:01:36 +1100172 auto severity = getSeverityField(propertyMap);
173
174 if (auto itr = propertyMap.find("Direction"); itr != propertyMap.end())
Rashmica Guptae7efe132021-07-27 19:42:11 +1000175 {
176 direction = std::get<std::string>(itr->second);
177 }
178
179 auto threshold = getThresholdType(direction, severity);
180 thresholds[threshold] = value;
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000181
Patrick Williams150d5f62024-08-16 15:21:45 -0400182 auto hysteresis =
183 getNumberFromConfig<double>(propertyMap, "Hysteresis", false);
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000184 if (hysteresis != std::numeric_limits<double>::quiet_NaN())
185 {
186 thresholds[threshold + "Hysteresis"] = hysteresis;
187 }
Tao Lin91799db2022-07-27 21:02:20 +0800188
189 if (!entityInterface.empty())
190 {
191 thresholds[threshold + "Direction"] = entityInterface;
192 }
Rashmica Guptae7efe132021-07-27 19:42:11 +1000193}
194
195void VirtualSensor::parseConfigInterface(const PropertyMap& propertyMap,
196 const std::string& sensorType,
197 const std::string& interface)
198{
199 /* Parse sensors / DBus params */
200 if (auto itr = propertyMap.find("Sensors"); itr != propertyMap.end())
201 {
202 auto sensors = std::get<std::vector<std::string>>(itr->second);
203 for (auto sensor : sensors)
204 {
205 std::replace(sensor.begin(), sensor.end(), ' ', '_');
206 auto sensorObjPath = sensorDbusPath + sensorType + "/" + sensor;
207
Patrick Williams150d5f62024-08-16 15:21:45 -0400208 auto paramPtr =
209 std::make_unique<SensorParam>(bus, sensorObjPath, *this);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000210 symbols.create_variable(sensor);
211 paramMap.emplace(std::move(sensor), std::move(paramPtr));
212 }
213 }
214 /* Get expression string */
215 if (!isCalculationType(interface))
216 {
217 throw std::invalid_argument("Invalid expression in interface");
218 }
219 exprStr = interface;
220
221 /* Get optional min and max input and output values */
222 ValueIface::maxValue(
223 getNumberFromConfig<double>(propertyMap, "MaxValue", false));
224 ValueIface::minValue(
225 getNumberFromConfig<double>(propertyMap, "MinValue", false));
226 maxValidInput =
Jiaqing Zhao190f6d02022-05-21 23:26:15 +0800227 getNumberFromConfig<double>(propertyMap, "MaxValidInput", false,
228 std::numeric_limits<double>::infinity());
Rashmica Guptae7efe132021-07-27 19:42:11 +1000229 minValidInput =
Jiaqing Zhao190f6d02022-05-21 23:26:15 +0800230 getNumberFromConfig<double>(propertyMap, "MinValidInput", false,
231 -std::numeric_limits<double>::infinity());
Rashmica Guptae7efe132021-07-27 19:42:11 +1000232}
233
Matt Spinlerce675222021-01-14 16:38:09 -0600234void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
235 const std::string& objPath)
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700236{
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700237 static const Json empty{};
238
239 /* Get threshold values if defined in config */
240 auto threshold = sensorConfig.value("Threshold", empty);
Matt Spinlerf15189e2021-01-15 10:13:28 -0600241
Rashmica Gupta3e999192021-06-09 16:17:04 +1000242 createThresholds(threshold, objPath);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700243
Harvey Wuf6443742021-04-09 16:47:36 +0800244 /* Get MaxValue, MinValue setting if defined in config */
245 auto confDesc = sensorConfig.value("Desc", empty);
246 if (auto maxConf = confDesc.find("MaxValue");
247 maxConf != confDesc.end() && maxConf->is_number())
248 {
249 ValueIface::maxValue(maxConf->get<double>());
250 }
251 if (auto minConf = confDesc.find("MinValue");
252 minConf != confDesc.end() && minConf->is_number())
253 {
254 ValueIface::minValue(minConf->get<double>());
255 }
256
Lei YU0fcf0e12021-06-04 11:14:17 +0800257 /* Get optional association */
258 auto assocJson = sensorConfig.value("Associations", empty);
259 if (!assocJson.empty())
260 {
261 auto assocs = getAssociationsFromJson(assocJson);
262 if (!assocs.empty())
263 {
264 associationIface =
265 std::make_unique<AssociationObject>(bus, objPath.c_str());
266 associationIface->associations(assocs);
267 }
268 }
269
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700270 /* Get expression string */
Patrick Williams03c4c8e2022-04-14 22:19:44 -0500271 static constexpr auto exprKey = "Expression";
272 if (sensorConfig.contains(exprKey))
273 {
Patrick Williamsa9596782022-04-15 10:20:07 -0500274 auto& ref = sensorConfig.at(exprKey);
Patrick Williams03c4c8e2022-04-14 22:19:44 -0500275 if (ref.is_array())
276 {
277 exprStr = std::string{};
278 for (auto& s : ref)
279 {
280 exprStr += s;
281 }
282 }
283 else if (ref.is_string())
284 {
285 exprStr = std::string{ref};
286 }
287 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700288
289 /* Get all the parameter listed in configuration */
290 auto params = sensorConfig.value("Params", empty);
291
292 /* Check for constant parameter */
293 const auto& consParams = params.value("ConstParam", empty);
294 if (!consParams.empty())
295 {
296 for (auto& j : consParams)
297 {
298 if (j.find("ParamName") != j.end())
299 {
300 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700301 std::string name = j["ParamName"];
302 symbols.create_variable(name);
303 paramMap.emplace(std::move(name), std::move(paramPtr));
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700304 }
305 else
306 {
307 /* Invalid configuration */
308 throw std::invalid_argument(
309 "ParamName not found in configuration");
310 }
311 }
312 }
313
Vijay Khemka7452a862020-08-11 16:01:23 -0700314 /* Check for dbus parameter */
315 auto dbusParams = params.value("DbusParam", empty);
316 if (!dbusParams.empty())
317 {
318 for (auto& j : dbusParams)
319 {
320 /* Get parameter dbus sensor descriptor */
321 auto desc = j.value("Desc", empty);
322 if ((!desc.empty()) && (j.find("ParamName") != j.end()))
323 {
324 std::string sensorType = desc.value("SensorType", "");
325 std::string name = desc.value("Name", "");
326
327 if (!sensorType.empty() && !name.empty())
328 {
George Liu1204b432021-12-29 17:24:48 +0800329 auto path = sensorDbusPath + sensorType + "/" + name;
Vijay Khemka7452a862020-08-11 16:01:23 -0700330
Patrick Williams150d5f62024-08-16 15:21:45 -0400331 auto paramPtr =
332 std::make_unique<SensorParam>(bus, path, *this);
George Liu1204b432021-12-29 17:24:48 +0800333 std::string paramName = j["ParamName"];
334 symbols.create_variable(paramName);
335 paramMap.emplace(std::move(paramName), std::move(paramPtr));
Vijay Khemka7452a862020-08-11 16:01:23 -0700336 }
337 }
338 }
339 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700340
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700341 symbols.add_constants();
Matt Spinler9f1ef4f2020-11-09 15:59:11 -0600342 symbols.add_package(vecopsPackage);
Lei YU0ab9d832022-07-19 07:12:50 +0000343 symbols.add_function("maxIgnoreNaN", funcMaxIgnoreNaN);
Lei YU87d35112022-10-24 05:54:25 +0000344 symbols.add_function("sumIgnoreNaN", funcSumIgnoreNaN);
Lei YUc77b6b32023-06-08 12:02:05 +0000345 symbols.add_function("ifNan", funcIfNan);
Lei YU0ab9d832022-07-19 07:12:50 +0000346
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700347 expression.register_symbol_table(symbols);
348
349 /* parser from exprtk */
350 exprtk::parser<double> parser{};
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600351 if (!parser.compile(exprStr, expression))
352 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500353 error("Expression compilation failed");
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600354
355 for (std::size_t i = 0; i < parser.error_count(); ++i)
356 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500357 auto err = parser.get_error(i);
358 error("Error parsing token at {POSITION}: {ERROR}", "POSITION",
359 err.token.position, "TYPE",
360 exprtk::parser_error::to_str(err.mode), "ERROR",
361 err.diagnostic);
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600362 }
363 throw std::runtime_error("Expression compilation failed");
364 }
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700365
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700366 /* Print all parameters for debug purpose only */
George Liua630f082024-08-28 14:42:55 +0800367 printParams(paramMap);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700368}
369
Tao Lindc777012022-07-27 20:41:46 +0800370void VirtualSensor::createAssociation(const std::string& objPath,
371 const std::string& entityPath)
372{
373 if (objPath.empty() || entityPath.empty())
374 {
375 return;
376 }
377
378 std::filesystem::path p(entityPath);
379 auto assocsDbus =
380 AssociationList{{"chassis", "all_sensors", p.parent_path().string()}};
Patrick Williams150d5f62024-08-16 15:21:45 -0400381 associationIface =
382 std::make_unique<AssociationObject>(bus, objPath.c_str());
Tao Lindc777012022-07-27 20:41:46 +0800383 associationIface->associations(assocsDbus);
384}
385
Patrick Williams150d5f62024-08-16 15:21:45 -0400386void VirtualSensor::initVirtualSensor(
387 const InterfaceMap& interfaceMap, const std::string& objPath,
388 const std::string& sensorType, const std::string& calculationIface)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000389{
390 Json thresholds;
Patrick Williams150d5f62024-08-16 15:21:45 -0400391 const std::string vsThresholdsIntf =
392 calculationIface + vsThresholdsIfaceSuffix;
Rashmica Guptae7efe132021-07-27 19:42:11 +1000393
394 for (const auto& [interface, propertyMap] : interfaceMap)
395 {
396 /* Each threshold is on it's own interface with a number as a suffix
397 * eg xyz.openbmc_project.Configuration.ModifiedMedian.Thresholds1 */
398 if (interface.find(vsThresholdsIntf) != std::string::npos)
399 {
Tao Lin91799db2022-07-27 21:02:20 +0800400 parseThresholds(thresholds, propertyMap, interface);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000401 }
402 else if (interface == calculationIface)
403 {
404 parseConfigInterface(propertyMap, sensorType, interface);
405 }
406 }
407
408 createThresholds(thresholds, objPath);
409 symbols.add_constants();
410 symbols.add_package(vecopsPackage);
411 expression.register_symbol_table(symbols);
412
Tao Lindc777012022-07-27 20:41:46 +0800413 createAssociation(objPath, entityPath);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000414 /* Print all parameters for debug purpose only */
George Liua630f082024-08-28 14:42:55 +0800415 printParams(paramMap);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000416}
417
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700418void VirtualSensor::setSensorValue(double value)
419{
Patrick Williams543bf662021-04-29 09:03:53 -0500420 value = std::clamp(value, ValueIface::minValue(), ValueIface::maxValue());
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700421 ValueIface::value(value);
422}
423
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000424double VirtualSensor::calculateValue(const std::string& calculation,
425 const VirtualSensor::ParamMap& paramMap)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000426{
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000427 auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
428 calculation);
429 if (itr == calculationIfaces.end())
430 {
431 return std::numeric_limits<double>::quiet_NaN();
432 }
433 else if (calculation == "xyz.openbmc_project.Configuration.ModifiedMedian")
434 {
435 return calculateModifiedMedianValue(paramMap);
436 }
Tao Linf6b7e0a2022-10-09 09:35:44 +0800437 else if (calculation == "xyz.openbmc_project.Configuration.Maximum")
438 {
439 return calculateMaximumValue(paramMap);
440 }
Rashmica Guptae7efe132021-07-27 19:42:11 +1000441 return std::numeric_limits<double>::quiet_NaN();
442}
443
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000444bool VirtualSensor::sensorInRange(double value)
445{
446 if (value <= this->maxValidInput && value >= this->minValidInput)
447 {
448 return true;
449 }
450 return false;
451}
452
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700453void VirtualSensor::updateVirtualSensor()
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700454{
455 for (auto& param : paramMap)
456 {
457 auto& name = param.first;
458 auto& data = param.second;
459 if (auto var = symbols.get_variable(name))
460 {
461 var->ref() = data->getParamValue();
462 }
463 else
464 {
465 /* Invalid parameter */
466 throw std::invalid_argument("ParamName not found in symbols");
467 }
468 }
Patrick Williams150d5f62024-08-16 15:21:45 -0400469 auto itr =
470 std::find(calculationIfaces.begin(), calculationIfaces.end(), exprStr);
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000471 auto val = (itr == calculationIfaces.end())
472 ? expression.value()
473 : calculateValue(exprStr, paramMap);
Vijay Khemka32a71562020-09-10 15:29:18 -0700474
475 /* Set sensor value to dbus interface */
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700476 setSensorValue(val);
George Liua630f082024-08-28 14:42:55 +0800477 debug("Sensor {NAME} = {VALUE}", "NAME", this->name, "VALUE", val);
Vijay Khemka32a71562020-09-10 15:29:18 -0700478
Matt Spinler8f5e6112021-01-15 10:44:32 -0600479 /* Check sensor thresholds and log required message */
Matt Spinlerb306b032021-02-01 10:05:46 -0600480 checkThresholds(val, perfLossIface);
Patrick Williamsfdb826d2021-01-20 14:37:53 -0600481 checkThresholds(val, warningIface);
482 checkThresholds(val, criticalIface);
483 checkThresholds(val, softShutdownIface);
484 checkThresholds(val, hardShutdownIface);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700485}
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700486
Rashmica Gupta304fd0e2021-08-10 16:50:44 +1000487double VirtualSensor::calculateModifiedMedianValue(
488 const VirtualSensor::ParamMap& paramMap)
489{
490 std::vector<double> values;
491
492 for (auto& param : paramMap)
493 {
494 auto& name = param.first;
495 if (auto var = symbols.get_variable(name))
496 {
497 if (!sensorInRange(var->ref()))
498 {
499 continue;
500 }
501 values.push_back(var->ref());
502 }
503 }
504
505 size_t size = values.size();
506 std::sort(values.begin(), values.end());
507 switch (size)
508 {
509 case 2:
510 /* Choose biggest value */
511 return values.at(1);
512 case 0:
513 return std::numeric_limits<double>::quiet_NaN();
514 default:
515 /* Choose median value */
516 if (size % 2 == 0)
517 {
518 // Average of the two middle values
519 return (values.at(size / 2) + values.at(size / 2 - 1)) / 2;
520 }
521 else
522 {
523 return values.at((size - 1) / 2);
524 }
525 }
526}
527
Tao Linf6b7e0a2022-10-09 09:35:44 +0800528double VirtualSensor::calculateMaximumValue(
529 const VirtualSensor::ParamMap& paramMap)
530{
531 std::vector<double> values;
532
533 for (auto& param : paramMap)
534 {
535 auto& name = param.first;
536 if (auto var = symbols.get_variable(name))
537 {
538 if (!sensorInRange(var->ref()))
539 {
540 continue;
541 }
542 values.push_back(var->ref());
543 }
544 }
545 auto maxIt = std::max_element(values.begin(), values.end());
546 if (maxIt == values.end())
547 {
548 return std::numeric_limits<double>::quiet_NaN();
549 }
550 return *maxIt;
551}
552
Rashmica Gupta3e999192021-06-09 16:17:04 +1000553void VirtualSensor::createThresholds(const Json& threshold,
554 const std::string& objPath)
555{
556 if (threshold.empty())
557 {
558 return;
559 }
560 // Only create the threshold interfaces if
561 // at least one of their values is present.
562 if (threshold.contains("CriticalHigh") || threshold.contains("CriticalLow"))
563 {
564 criticalIface =
565 std::make_unique<Threshold<CriticalObject>>(bus, objPath.c_str());
566
Tao Lin91799db2022-07-27 21:02:20 +0800567 if (threshold.contains("CriticalHigh"))
568 {
569 criticalIface->setEntityInterfaceHigh(
570 threshold.value("CriticalHighDirection", ""));
George Liua630f082024-08-28 14:42:55 +0800571 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
572 "INTF", threshold.value("CriticalHighDirection", ""));
Tao Lin91799db2022-07-27 21:02:20 +0800573 }
574 if (threshold.contains("CriticalLow"))
575 {
576 criticalIface->setEntityInterfaceLow(
577 threshold.value("CriticalLowDirection", ""));
George Liua630f082024-08-28 14:42:55 +0800578 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
579 "INTF", threshold.value("CriticalLowDirection", ""));
Tao Lin91799db2022-07-27 21:02:20 +0800580 }
581
582 criticalIface->setEntityPath(entityPath);
George Liua630f082024-08-28 14:42:55 +0800583 debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath, "PATH",
584 entityPath);
Matt Spinlera291ce12023-02-06 15:12:44 -0600585
586 criticalIface->criticalHigh(threshold.value(
587 "CriticalHigh", std::numeric_limits<double>::quiet_NaN()));
588 criticalIface->criticalLow(threshold.value(
589 "CriticalLow", std::numeric_limits<double>::quiet_NaN()));
590 criticalIface->setHighHysteresis(
591 threshold.value("CriticalHighHysteresis", defaultHysteresis));
592 criticalIface->setLowHysteresis(
593 threshold.value("CriticalLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000594 }
595
596 if (threshold.contains("WarningHigh") || threshold.contains("WarningLow"))
597 {
598 warningIface =
599 std::make_unique<Threshold<WarningObject>>(bus, objPath.c_str());
600
Tao Lin91799db2022-07-27 21:02:20 +0800601 if (threshold.contains("WarningHigh"))
602 {
603 warningIface->setEntityInterfaceHigh(
604 threshold.value("WarningHighDirection", ""));
George Liua630f082024-08-28 14:42:55 +0800605 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
606 "INTF", threshold.value("WarningHighDirection", ""));
Tao Lin91799db2022-07-27 21:02:20 +0800607 }
608 if (threshold.contains("WarningLow"))
609 {
610 warningIface->setEntityInterfaceLow(
611 threshold.value("WarningLowDirection", ""));
George Liua630f082024-08-28 14:42:55 +0800612 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
613 "INTF", threshold.value("WarningLowDirection", ""));
Tao Lin91799db2022-07-27 21:02:20 +0800614 }
615
616 warningIface->setEntityPath(entityPath);
George Liua630f082024-08-28 14:42:55 +0800617 debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath, "PATH",
618 entityPath);
Matt Spinlera291ce12023-02-06 15:12:44 -0600619
620 warningIface->warningHigh(threshold.value(
621 "WarningHigh", std::numeric_limits<double>::quiet_NaN()));
622 warningIface->warningLow(threshold.value(
623 "WarningLow", std::numeric_limits<double>::quiet_NaN()));
624 warningIface->setHighHysteresis(
625 threshold.value("WarningHighHysteresis", defaultHysteresis));
626 warningIface->setLowHysteresis(
627 threshold.value("WarningLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000628 }
629
630 if (threshold.contains("HardShutdownHigh") ||
631 threshold.contains("HardShutdownLow"))
632 {
633 hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>(
634 bus, objPath.c_str());
635
636 hardShutdownIface->hardShutdownHigh(threshold.value(
637 "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
638 hardShutdownIface->hardShutdownLow(threshold.value(
639 "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000640 hardShutdownIface->setHighHysteresis(
641 threshold.value("HardShutdownHighHysteresis", defaultHysteresis));
642 hardShutdownIface->setLowHysteresis(
643 threshold.value("HardShutdownLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000644 }
645
646 if (threshold.contains("SoftShutdownHigh") ||
647 threshold.contains("SoftShutdownLow"))
648 {
649 softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>(
650 bus, objPath.c_str());
651
652 softShutdownIface->softShutdownHigh(threshold.value(
653 "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
654 softShutdownIface->softShutdownLow(threshold.value(
655 "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000656 softShutdownIface->setHighHysteresis(
657 threshold.value("SoftShutdownHighHysteresis", defaultHysteresis));
658 softShutdownIface->setLowHysteresis(
659 threshold.value("SoftShutdownLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000660 }
661
662 if (threshold.contains("PerformanceLossHigh") ||
663 threshold.contains("PerformanceLossLow"))
664 {
665 perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>(
666 bus, objPath.c_str());
667
668 perfLossIface->performanceLossHigh(threshold.value(
669 "PerformanceLossHigh", std::numeric_limits<double>::quiet_NaN()));
670 perfLossIface->performanceLossLow(threshold.value(
671 "PerformanceLossLow", std::numeric_limits<double>::quiet_NaN()));
Rashmica Gupta1dff7dc2021-07-27 19:43:31 +1000672 perfLossIface->setHighHysteresis(threshold.value(
673 "PerformanceLossHighHysteresis", defaultHysteresis));
674 perfLossIface->setLowHysteresis(
675 threshold.value("PerformanceLossLowHysteresis", defaultHysteresis));
Rashmica Gupta3e999192021-06-09 16:17:04 +1000676 }
677}
678
Rashmica Guptae7efe132021-07-27 19:42:11 +1000679ManagedObjectType VirtualSensors::getObjectsFromDBus()
680{
681 ManagedObjectType objects;
682
683 try
684 {
Patrick Williams150d5f62024-08-16 15:21:45 -0400685 auto method = bus.new_method_call(
686 "xyz.openbmc_project.EntityManager",
687 "/xyz/openbmc_project/inventory",
688 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rashmica Guptae7efe132021-07-27 19:42:11 +1000689 auto reply = bus.call(method);
690 reply.read(objects);
691 }
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500692 catch (const sdbusplus::exception_t& ex)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000693 {
694 // If entity manager isn't running yet, keep going.
695 if (std::string("org.freedesktop.DBus.Error.ServiceUnknown") !=
696 ex.name())
697 {
Matt Spinler71b9c112022-10-18 09:14:45 -0500698 error("Could not reach entity-manager: {ERROR}", "ERROR", ex);
699 throw;
Rashmica Guptae7efe132021-07-27 19:42:11 +1000700 }
701 }
702
703 return objects;
704}
705
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500706void VirtualSensors::propertiesChanged(sdbusplus::message_t& msg)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000707{
708 std::string path;
709 PropertyMap properties;
710
711 msg.read(path, properties);
712
713 /* We get multiple callbacks for one sensor. 'Type' is a required field and
714 * is a unique label so use to to only proceed once per sensor */
715 if (properties.contains("Type"))
716 {
717 if (isCalculationType(path))
718 {
719 createVirtualSensorsFromDBus(path);
720 }
721 }
722}
723
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700724/** @brief Parsing Virtual Sensor config JSON file */
Patrick Williams32dff212023-02-09 13:54:18 -0600725Json VirtualSensors::parseConfigFile()
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700726{
Patrick Williams32dff212023-02-09 13:54:18 -0600727 using path = std::filesystem::path;
728 auto configFile = []() -> path {
729 static constexpr auto name = "virtual_sensor_config.json";
730
731 for (auto pathSeg : {std::filesystem::current_path(),
732 path{"/var/lib/phosphor-virtual-sensor"},
733 path{"/usr/share/phosphor-virtual-sensor"}})
734 {
735 auto file = pathSeg / name;
736 if (std::filesystem::exists(file))
737 {
738 return file;
739 }
740 }
741 return name;
742 }();
743
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700744 std::ifstream jsonFile(configFile);
745 if (!jsonFile.is_open())
746 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500747 error("config JSON file {FILENAME} not found", "FILENAME", configFile);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000748 return {};
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700749 }
750
751 auto data = Json::parse(jsonFile, nullptr, false);
752 if (data.is_discarded())
753 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500754 error("config readings JSON parser failure with {FILENAME}", "FILENAME",
755 configFile);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700756 throw std::exception{};
757 }
758
759 return data;
760}
761
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700762std::map<std::string, ValueIface::Unit> unitMap = {
763 {"temperature", ValueIface::Unit::DegreesC},
764 {"fan_tach", ValueIface::Unit::RPMS},
Konstantin Aladyshev9358f6b2024-03-14 14:23:40 +0300765 {"fan_pwm", ValueIface::Unit::Percent},
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700766 {"voltage", ValueIface::Unit::Volts},
767 {"altitude", ValueIface::Unit::Meters},
768 {"current", ValueIface::Unit::Amperes},
769 {"power", ValueIface::Unit::Watts},
770 {"energy", ValueIface::Unit::Joules},
Kumar Thangavel2b56ddb2021-01-13 20:16:11 +0530771 {"utilization", ValueIface::Unit::Percent},
Rashmica Gupta4ac7a7f2021-07-08 12:30:50 +1000772 {"airflow", ValueIface::Unit::CFM},
773 {"pressure", ValueIface::Unit::Pascals}};
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700774
Rashmica Guptae7efe132021-07-27 19:42:11 +1000775const std::string getSensorTypeFromUnit(const std::string& unit)
776{
777 std::string unitPrefix = "xyz.openbmc_project.Sensor.Value.Unit.";
778 for (auto [type, unitObj] : unitMap)
779 {
780 auto unitPath = ValueIface::convertUnitToString(unitObj);
781 if (unitPath == (unitPrefix + unit))
782 {
783 return type;
784 }
785 }
786 return "";
787}
788
789void VirtualSensors::setupMatches()
790{
791 /* Already setup */
792 if (!this->matches.empty())
793 {
794 return;
795 }
796
797 /* Setup matches */
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500798 auto eventHandler = [this](sdbusplus::message_t& message) {
Rashmica Guptae7efe132021-07-27 19:42:11 +1000799 if (message.is_method_error())
800 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500801 error("Callback method error");
Rashmica Guptae7efe132021-07-27 19:42:11 +1000802 return;
803 }
804 this->propertiesChanged(message);
805 };
806
807 for (const char* iface : calculationIfaces)
808 {
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500809 auto match = std::make_unique<sdbusplus::bus::match_t>(
Rashmica Guptae7efe132021-07-27 19:42:11 +1000810 bus,
811 sdbusplus::bus::match::rules::propertiesChangedNamespace(
812 "/xyz/openbmc_project/inventory", iface),
813 eventHandler);
814 this->matches.emplace_back(std::move(match));
815 }
816}
817
818void VirtualSensors::createVirtualSensorsFromDBus(
819 const std::string& calculationIface)
820{
821 if (calculationIface.empty())
822 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500823 error("No calculation type supplied");
Rashmica Guptae7efe132021-07-27 19:42:11 +1000824 return;
825 }
826 auto objects = getObjectsFromDBus();
827
828 /* Get virtual sensors config data */
829 for (const auto& [path, interfaceMap] : objects)
830 {
Rashmica Guptae7efe132021-07-27 19:42:11 +1000831 /* Find Virtual Sensor interfaces */
George Liu2db8d412023-08-21 16:41:04 +0800832 auto intfIter = interfaceMap.find(calculationIface);
833 if (intfIter == interfaceMap.end())
Rashmica Guptae7efe132021-07-27 19:42:11 +1000834 {
835 continue;
836 }
George Liu2db8d412023-08-21 16:41:04 +0800837
838 std::string name = path.filename();
Rashmica Guptae7efe132021-07-27 19:42:11 +1000839 if (name.empty())
840 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500841 error("Virtual Sensor name not found in entity manager config");
Rashmica Guptae7efe132021-07-27 19:42:11 +1000842 continue;
843 }
844 if (virtualSensorsMap.contains(name))
845 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500846 error("A virtual sensor named {NAME} already exists", "NAME", name);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000847 continue;
848 }
849
850 /* Extract the virtual sensor type as we need this to initialize the
851 * sensor */
George Liu2db8d412023-08-21 16:41:04 +0800852 std::string sensorType, sensorUnit;
853 auto propertyMap = intfIter->second;
854 auto proIter = propertyMap.find("Units");
855 if (proIter != propertyMap.end())
Rashmica Guptae7efe132021-07-27 19:42:11 +1000856 {
George Liu2db8d412023-08-21 16:41:04 +0800857 sensorUnit = std::get<std::string>(proIter->second);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000858 }
859 sensorType = getSensorTypeFromUnit(sensorUnit);
860 if (sensorType.empty())
861 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500862 error("Sensor unit type {TYPE} is not supported", "TYPE",
863 sensorUnit);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000864 continue;
865 }
866
867 try
868 {
George Liu2db8d412023-08-21 16:41:04 +0800869 auto objpath = static_cast<std::string>(path);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000870 auto virtObjPath = sensorDbusPath + sensorType + "/" + name;
871
872 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
873 bus, virtObjPath.c_str(), interfaceMap, name, sensorType,
Tao Lindc777012022-07-27 20:41:46 +0800874 calculationIface, objpath);
Patrick Williams82b39c62021-07-28 16:22:27 -0500875 info("Added a new virtual sensor: {NAME} {TYPE}", "NAME", name,
876 "TYPE", sensorType);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000877 virtualSensorPtr->updateVirtualSensor();
878
879 /* Initialize unit value for virtual sensor */
880 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
881 virtualSensorPtr->emit_object_added();
882
883 virtualSensorsMap.emplace(name, std::move(virtualSensorPtr));
884
885 /* Setup match for interfaces removed */
Patrick Williamsae10c522023-10-20 11:19:44 -0500886 auto intfRemoved = [this, objpath,
887 name](sdbusplus::message_t& message) {
Rashmica Guptae7efe132021-07-27 19:42:11 +1000888 if (!virtualSensorsMap.contains(name))
889 {
890 return;
891 }
892 sdbusplus::message::object_path path;
893 message.read(path);
894 if (static_cast<const std::string&>(path) == objpath)
895 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500896 info("Removed a virtual sensor: {NAME}", "NAME", name);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000897 virtualSensorsMap.erase(name);
898 }
899 };
Patrick Williams8e11ccc2022-07-22 19:26:57 -0500900 auto matchOnRemove = std::make_unique<sdbusplus::bus::match_t>(
Rashmica Guptae7efe132021-07-27 19:42:11 +1000901 bus,
902 sdbusplus::bus::match::rules::interfacesRemoved() +
903 sdbusplus::bus::match::rules::argNpath(0, objpath),
904 intfRemoved);
905 /* TODO: slight race condition here. Check that the config still
906 * exists */
907 this->matches.emplace_back(std::move(matchOnRemove));
908 }
Patrick Williamsdac26632021-10-06 14:38:47 -0500909 catch (const std::invalid_argument& ia)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000910 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500911 error("Failed to set up virtual sensor: {ERROR}", "ERROR", ia);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000912 }
913 }
914}
915
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700916void VirtualSensors::createVirtualSensors()
917{
918 static const Json empty{};
919
Patrick Williams32dff212023-02-09 13:54:18 -0600920 auto data = parseConfigFile();
Rashmica Guptae7efe132021-07-27 19:42:11 +1000921
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700922 // print values
George Liua630f082024-08-28 14:42:55 +0800923 debug("JSON: {JSON}", "JSON", data.dump());
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700924
925 /* Get virtual sensors config data */
926 for (const auto& j : data)
927 {
928 auto desc = j.value("Desc", empty);
929 if (!desc.empty())
930 {
Rashmica Guptae7efe132021-07-27 19:42:11 +1000931 if (desc.value("Config", "") == "D-Bus")
932 {
933 /* Look on D-Bus for a virtual sensor config. Set up matches
934 * first because the configs may not be on D-Bus yet and we
935 * don't want to miss them */
936 setupMatches();
937
938 if (desc.contains("Type"))
939 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500940 auto type = desc.value("Type", "");
941 auto path = "xyz.openbmc_project.Configuration." + type;
942
Rashmica Guptae7efe132021-07-27 19:42:11 +1000943 if (!isCalculationType(path))
944 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500945 error("Invalid calculation type {TYPE} supplied.",
946 "TYPE", type);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000947 continue;
948 }
949 createVirtualSensorsFromDBus(path);
950 }
951 continue;
952 }
953
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700954 std::string sensorType = desc.value("SensorType", "");
955 std::string name = desc.value("Name", "");
Rashmica Gupta665a0a22021-06-30 11:35:28 +1000956 std::replace(name.begin(), name.end(), ' ', '_');
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700957
958 if (!name.empty() && !sensorType.empty())
959 {
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700960 if (unitMap.find(sensorType) == unitMap.end())
961 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500962 error("Sensor type {TYPE} is not supported", "TYPE",
963 sensorType);
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700964 }
965 else
966 {
Rashmica Gupta67d8b9d2021-06-30 11:41:14 +1000967 if (virtualSensorsMap.find(name) != virtualSensorsMap.end())
968 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500969 error("A virtual sensor named {NAME} already exists",
970 "NAME", name);
Rashmica Gupta67d8b9d2021-06-30 11:41:14 +1000971 continue;
972 }
Rashmica Gupta862c3d12021-08-06 12:19:31 +1000973 auto objPath = sensorDbusPath + sensorType + "/" + name;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700974
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700975 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
976 bus, objPath.c_str(), j, name);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700977
Patrick Williams82b39c62021-07-28 16:22:27 -0500978 info("Added a new virtual sensor: {NAME}", "NAME", name);
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700979 virtualSensorPtr->updateVirtualSensor();
980
981 /* Initialize unit value for virtual sensor */
982 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
Rashmica Guptaa2fa63a2021-08-06 12:21:13 +1000983 virtualSensorPtr->emit_object_added();
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700984
985 virtualSensorsMap.emplace(std::move(name),
986 std::move(virtualSensorPtr));
987 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700988 }
989 else
990 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500991 error(
992 "Sensor type ({TYPE}) or name ({NAME}) not found in config file",
993 "NAME", name, "TYPE", sensorType);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700994 }
995 }
996 else
997 {
Patrick Williams82b39c62021-07-28 16:22:27 -0500998 error("Descriptor for new virtual sensor not found in config file");
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700999 }
1000 }
1001}
1002
Tao Linf2e94222023-10-31 17:38:17 +08001003} // namespace phosphor::virtual_sensor
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001004
1005/**
1006 * @brief Main
1007 */
1008int main()
1009{
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001010 // Get a handle to system dbus
1011 auto bus = sdbusplus::bus::new_default();
1012
Matt Spinler6c19e7d2021-01-12 16:26:45 -06001013 // Add the ObjectManager interface
Ed Tanousf7ec40a2022-10-04 17:39:40 -07001014 sdbusplus::server::manager_t objManager(bus,
1015 "/xyz/openbmc_project/sensors");
Matt Spinler6c19e7d2021-01-12 16:26:45 -06001016
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001017 // Create an virtual sensors object
Tao Linf2e94222023-10-31 17:38:17 +08001018 phosphor::virtual_sensor::VirtualSensors virtualSensors(bus);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001019
1020 // Request service bus name
George Liu94921492023-08-21 16:18:14 +08001021 bus.request_name("xyz.openbmc_project.VirtualSensor");
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001022
Patrick Williamse6672392022-09-02 09:03:24 -05001023 // Run the dbus loop.
1024 bus.process_loop();
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001025
1026 return 0;
1027}