blob: be9d78a5983b47ef2a08247792402f8778b820bd [file] [log] [blame]
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001#include "virtualSensor.hpp"
2
3#include "config.hpp"
4
Matt Spinlerddc6dcd2020-11-09 11:16:31 -06005#include <fmt/format.h>
6
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07007#include <phosphor-logging/log.hpp>
8#include <sdeventplus/event.hpp>
9
10#include <fstream>
11#include <iostream>
12
13static constexpr bool DEBUG = false;
14static constexpr auto busName = "xyz.openbmc_project.VirtualSensor";
15static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/";
Rashmica Guptae7efe132021-07-27 19:42:11 +100016static constexpr auto entityManagerBusName =
17 "xyz.openbmc_project.EntityManager";
18static constexpr auto vsThresholdsIfaceSuffix = ".Thresholds";
19static constexpr std::array<const char*, 0> calculationIfaces = {};
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070020
21using namespace phosphor::logging;
22
Vijay Khemka51f898e2020-09-09 22:24:18 -070023int handleDbusSignal(sd_bus_message* msg, void* usrData, sd_bus_error*)
24{
25 if (usrData == nullptr)
26 {
27 throw std::runtime_error("Invalid match");
28 }
29
30 auto sdbpMsg = sdbusplus::message::message(msg);
31 std::string msgIfce;
32 std::map<std::string, std::variant<int64_t, double, bool>> msgData;
33
34 sdbpMsg.read(msgIfce, msgData);
35
36 if (msgData.find("Value") != msgData.end())
37 {
38 using namespace phosphor::virtualSensor;
39 VirtualSensor* obj = static_cast<VirtualSensor*>(usrData);
40 // TODO(openbmc/phosphor-virtual-sensor#1): updateVirtualSensor should
41 // be changed to take the information we got from the signal, to avoid
42 // having to do numerous dbus queries.
43 obj->updateVirtualSensor();
44 }
45 return 0;
46}
47
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070048namespace phosphor
49{
50namespace virtualSensor
51{
52
53void printParams(const VirtualSensor::ParamMap& paramMap)
54{
55 for (const auto& p : paramMap)
56 {
57 const auto& p1 = p.first;
58 const auto& p2 = p.second;
59 auto val = p2->getParamValue();
60 std::cout << p1 << " = " << val << "\n";
61 }
62}
63
64double SensorParam::getParamValue()
65{
66 switch (paramType)
67 {
68 case constParam:
69 return value;
70 break;
Vijay Khemka7452a862020-08-11 16:01:23 -070071 case dbusParam:
72 return dbusSensor->getSensorValue();
73 break;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070074 default:
75 throw std::invalid_argument("param type not supported");
76 }
77}
78
Lei YU0fcf0e12021-06-04 11:14:17 +080079using AssociationList =
80 std::vector<std::tuple<std::string, std::string, std::string>>;
81
82AssociationList getAssociationsFromJson(const Json& j)
83{
84 AssociationList assocs{};
85 try
86 {
87 j.get_to(assocs);
88 }
89 catch (const std::exception& ex)
90 {
91 log<level::ERR>("Failed to parse association",
92 entry("EX=%s", ex.what()));
93 }
94 return assocs;
95}
96
Rashmica Guptae7efe132021-07-27 19:42:11 +100097template <typename U>
98struct VariantToNumber
99{
100 template <typename T>
101 U operator()(const T& t) const
102 {
103 if constexpr (std::is_convertible<T, U>::value)
104 {
105 return static_cast<U>(t);
106 }
107 throw std::invalid_argument("Invalid number type in config\n");
108 }
109};
110
111template <typename U>
112U getNumberFromConfig(const PropertyMap& map, const std::string& name,
113 bool required)
114{
115 if (auto itr = map.find(name); itr != map.end())
116 {
117 return std::visit(VariantToNumber<U>(), itr->second);
118 }
119 else if (required)
120 {
121 log<level::ERR>("Required field missing in config",
122 entry("NAME=%s", name.c_str()));
123 throw std::invalid_argument("Required field missing in config");
124 }
125 return std::numeric_limits<U>::quiet_NaN();
126}
127
128bool isCalculationType(const std::string& interface)
129{
130 auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
131 interface);
132 if (itr != calculationIfaces.end())
133 {
134 return true;
135 }
136 return false;
137}
138
139const std::string getThresholdType(const std::string& direction,
140 uint64_t severity)
141{
142 std::string threshold;
143 std::string suffix;
144 static const std::array thresholdTypes{"Warning", "Critical",
145 "PerformanceLoss", "SoftShutdown",
146 "HardShutdown"};
147
148 if (severity >= thresholdTypes.size())
149 {
150 throw std::invalid_argument(
151 "Invalid threshold severity specified in entity manager");
152 }
153 threshold = thresholdTypes[severity];
154
155 if (direction == "less than")
156 {
157 suffix = "Low";
158 }
159 else if (direction == "greater than")
160 {
161 suffix = "High";
162 }
163 else
164 {
165 throw std::invalid_argument(
166 "Invalid threshold direction specified in entity manager");
167 }
168 return threshold + suffix;
169}
170
171void parseThresholds(Json& thresholds, const PropertyMap& propertyMap)
172{
173 std::string direction;
174
175 auto severity =
176 getNumberFromConfig<uint64_t>(propertyMap, "Severity", true);
177 auto value = getNumberFromConfig<double>(propertyMap, "Value", true);
178
179 auto itr = propertyMap.find("Direction");
180 if (itr != propertyMap.end())
181 {
182 direction = std::get<std::string>(itr->second);
183 }
184
185 auto threshold = getThresholdType(direction, severity);
186 thresholds[threshold] = value;
187}
188
189void VirtualSensor::parseConfigInterface(const PropertyMap& propertyMap,
190 const std::string& sensorType,
191 const std::string& interface)
192{
193 /* Parse sensors / DBus params */
194 if (auto itr = propertyMap.find("Sensors"); itr != propertyMap.end())
195 {
196 auto sensors = std::get<std::vector<std::string>>(itr->second);
197 for (auto sensor : sensors)
198 {
199 std::replace(sensor.begin(), sensor.end(), ' ', '_');
200 auto sensorObjPath = sensorDbusPath + sensorType + "/" + sensor;
201
202 auto paramPtr =
203 std::make_unique<SensorParam>(bus, sensorObjPath, this);
204 symbols.create_variable(sensor);
205 paramMap.emplace(std::move(sensor), std::move(paramPtr));
206 }
207 }
208 /* Get expression string */
209 if (!isCalculationType(interface))
210 {
211 throw std::invalid_argument("Invalid expression in interface");
212 }
213 exprStr = interface;
214
215 /* Get optional min and max input and output values */
216 ValueIface::maxValue(
217 getNumberFromConfig<double>(propertyMap, "MaxValue", false));
218 ValueIface::minValue(
219 getNumberFromConfig<double>(propertyMap, "MinValue", false));
220 maxValidInput =
221 getNumberFromConfig<double>(propertyMap, "MaxValidInput", false);
222 minValidInput =
223 getNumberFromConfig<double>(propertyMap, "MinValidInput", false);
224}
225
Matt Spinlerce675222021-01-14 16:38:09 -0600226void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
227 const std::string& objPath)
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700228{
229
230 static const Json empty{};
231
232 /* Get threshold values if defined in config */
233 auto threshold = sensorConfig.value("Threshold", empty);
Matt Spinlerf15189e2021-01-15 10:13:28 -0600234
Rashmica Gupta3e999192021-06-09 16:17:04 +1000235 createThresholds(threshold, objPath);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700236
Harvey Wuf6443742021-04-09 16:47:36 +0800237 /* Get MaxValue, MinValue setting if defined in config */
238 auto confDesc = sensorConfig.value("Desc", empty);
239 if (auto maxConf = confDesc.find("MaxValue");
240 maxConf != confDesc.end() && maxConf->is_number())
241 {
242 ValueIface::maxValue(maxConf->get<double>());
243 }
244 if (auto minConf = confDesc.find("MinValue");
245 minConf != confDesc.end() && minConf->is_number())
246 {
247 ValueIface::minValue(minConf->get<double>());
248 }
249
Lei YU0fcf0e12021-06-04 11:14:17 +0800250 /* Get optional association */
251 auto assocJson = sensorConfig.value("Associations", empty);
252 if (!assocJson.empty())
253 {
254 auto assocs = getAssociationsFromJson(assocJson);
255 if (!assocs.empty())
256 {
257 associationIface =
258 std::make_unique<AssociationObject>(bus, objPath.c_str());
259 associationIface->associations(assocs);
260 }
261 }
262
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700263 /* Get expression string */
264 exprStr = sensorConfig.value("Expression", "");
265
266 /* Get all the parameter listed in configuration */
267 auto params = sensorConfig.value("Params", empty);
268
269 /* Check for constant parameter */
270 const auto& consParams = params.value("ConstParam", empty);
271 if (!consParams.empty())
272 {
273 for (auto& j : consParams)
274 {
275 if (j.find("ParamName") != j.end())
276 {
277 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700278 std::string name = j["ParamName"];
279 symbols.create_variable(name);
280 paramMap.emplace(std::move(name), std::move(paramPtr));
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700281 }
282 else
283 {
284 /* Invalid configuration */
285 throw std::invalid_argument(
286 "ParamName not found in configuration");
287 }
288 }
289 }
290
Vijay Khemka7452a862020-08-11 16:01:23 -0700291 /* Check for dbus parameter */
292 auto dbusParams = params.value("DbusParam", empty);
293 if (!dbusParams.empty())
294 {
295 for (auto& j : dbusParams)
296 {
297 /* Get parameter dbus sensor descriptor */
298 auto desc = j.value("Desc", empty);
299 if ((!desc.empty()) && (j.find("ParamName") != j.end()))
300 {
301 std::string sensorType = desc.value("SensorType", "");
302 std::string name = desc.value("Name", "");
303
304 if (!sensorType.empty() && !name.empty())
305 {
Rashmica Gupta862c3d12021-08-06 12:19:31 +1000306 auto objPath = sensorDbusPath + sensorType + "/" + name;
Vijay Khemka7452a862020-08-11 16:01:23 -0700307
Vijay Khemka51f898e2020-09-09 22:24:18 -0700308 auto paramPtr =
309 std::make_unique<SensorParam>(bus, objPath, this);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700310 std::string name = j["ParamName"];
311 symbols.create_variable(name);
312 paramMap.emplace(std::move(name), std::move(paramPtr));
Vijay Khemka7452a862020-08-11 16:01:23 -0700313 }
314 }
315 }
316 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700317
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700318 symbols.add_constants();
Matt Spinler9f1ef4f2020-11-09 15:59:11 -0600319 symbols.add_package(vecopsPackage);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700320 expression.register_symbol_table(symbols);
321
322 /* parser from exprtk */
323 exprtk::parser<double> parser{};
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600324 if (!parser.compile(exprStr, expression))
325 {
326 log<level::ERR>("Expression compilation failed");
327
328 for (std::size_t i = 0; i < parser.error_count(); ++i)
329 {
330 auto error = parser.get_error(i);
331
332 log<level::ERR>(
333 fmt::format(
334 "Position: {} Type: {} Message: {}", error.token.position,
335 exprtk::parser_error::to_str(error.mode), error.diagnostic)
336 .c_str());
337 }
338 throw std::runtime_error("Expression compilation failed");
339 }
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700340
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700341 /* Print all parameters for debug purpose only */
342 if (DEBUG)
343 printParams(paramMap);
344}
345
Rashmica Guptae7efe132021-07-27 19:42:11 +1000346void VirtualSensor::initVirtualSensor(const InterfaceMap& interfaceMap,
347 const std::string& objPath,
348 const std::string& sensorType,
349 const std::string& calculationIface)
350{
351 Json thresholds;
352 const std::string vsThresholdsIntf =
353 calculationIface + vsThresholdsIfaceSuffix;
354
355 for (const auto& [interface, propertyMap] : interfaceMap)
356 {
357 /* Each threshold is on it's own interface with a number as a suffix
358 * eg xyz.openbmc_project.Configuration.ModifiedMedian.Thresholds1 */
359 if (interface.find(vsThresholdsIntf) != std::string::npos)
360 {
361 parseThresholds(thresholds, propertyMap);
362 }
363 else if (interface == calculationIface)
364 {
365 parseConfigInterface(propertyMap, sensorType, interface);
366 }
367 }
368
369 createThresholds(thresholds, objPath);
370 symbols.add_constants();
371 symbols.add_package(vecopsPackage);
372 expression.register_symbol_table(symbols);
373
374 /* Print all parameters for debug purpose only */
375 if (DEBUG)
376 {
377 printParams(paramMap);
378 }
379}
380
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700381void VirtualSensor::setSensorValue(double value)
382{
Patrick Williams543bf662021-04-29 09:03:53 -0500383 value = std::clamp(value, ValueIface::minValue(), ValueIface::maxValue());
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700384 ValueIface::value(value);
385}
386
Rashmica Guptae7efe132021-07-27 19:42:11 +1000387double VirtualSensor::calculateValue()
388{
389 // Placeholder until calculation types are added
390 return std::numeric_limits<double>::quiet_NaN();
391}
392
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700393void VirtualSensor::updateVirtualSensor()
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700394{
395 for (auto& param : paramMap)
396 {
397 auto& name = param.first;
398 auto& data = param.second;
399 if (auto var = symbols.get_variable(name))
400 {
401 var->ref() = data->getParamValue();
402 }
403 else
404 {
405 /* Invalid parameter */
406 throw std::invalid_argument("ParamName not found in symbols");
407 }
408 }
Rashmica Guptae7efe132021-07-27 19:42:11 +1000409 auto itr =
410 std::find(calculationIfaces.begin(), calculationIfaces.end(), exprStr);
411 auto val = (itr == calculationIfaces.end()) ? expression.value()
412 : calculateValue();
Vijay Khemka32a71562020-09-10 15:29:18 -0700413
414 /* Set sensor value to dbus interface */
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700415 setSensorValue(val);
Vijay Khemka32a71562020-09-10 15:29:18 -0700416
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700417 if (DEBUG)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000418 {
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700419 std::cout << "Sensor value is " << val << "\n";
Rashmica Guptae7efe132021-07-27 19:42:11 +1000420 }
Vijay Khemka32a71562020-09-10 15:29:18 -0700421
Matt Spinler8f5e6112021-01-15 10:44:32 -0600422 /* Check sensor thresholds and log required message */
Matt Spinlerb306b032021-02-01 10:05:46 -0600423 checkThresholds(val, perfLossIface);
Patrick Williamsfdb826d2021-01-20 14:37:53 -0600424 checkThresholds(val, warningIface);
425 checkThresholds(val, criticalIface);
426 checkThresholds(val, softShutdownIface);
427 checkThresholds(val, hardShutdownIface);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700428}
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700429
Rashmica Gupta3e999192021-06-09 16:17:04 +1000430void VirtualSensor::createThresholds(const Json& threshold,
431 const std::string& objPath)
432{
433 if (threshold.empty())
434 {
435 return;
436 }
437 // Only create the threshold interfaces if
438 // at least one of their values is present.
439 if (threshold.contains("CriticalHigh") || threshold.contains("CriticalLow"))
440 {
441 criticalIface =
442 std::make_unique<Threshold<CriticalObject>>(bus, objPath.c_str());
443
444 criticalIface->criticalHigh(threshold.value(
445 "CriticalHigh", std::numeric_limits<double>::quiet_NaN()));
446 criticalIface->criticalLow(threshold.value(
447 "CriticalLow", std::numeric_limits<double>::quiet_NaN()));
448 }
449
450 if (threshold.contains("WarningHigh") || threshold.contains("WarningLow"))
451 {
452 warningIface =
453 std::make_unique<Threshold<WarningObject>>(bus, objPath.c_str());
454
455 warningIface->warningHigh(threshold.value(
456 "WarningHigh", std::numeric_limits<double>::quiet_NaN()));
457 warningIface->warningLow(threshold.value(
458 "WarningLow", std::numeric_limits<double>::quiet_NaN()));
459 }
460
461 if (threshold.contains("HardShutdownHigh") ||
462 threshold.contains("HardShutdownLow"))
463 {
464 hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>(
465 bus, objPath.c_str());
466
467 hardShutdownIface->hardShutdownHigh(threshold.value(
468 "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
469 hardShutdownIface->hardShutdownLow(threshold.value(
470 "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
471 }
472
473 if (threshold.contains("SoftShutdownHigh") ||
474 threshold.contains("SoftShutdownLow"))
475 {
476 softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>(
477 bus, objPath.c_str());
478
479 softShutdownIface->softShutdownHigh(threshold.value(
480 "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
481 softShutdownIface->softShutdownLow(threshold.value(
482 "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
483 }
484
485 if (threshold.contains("PerformanceLossHigh") ||
486 threshold.contains("PerformanceLossLow"))
487 {
488 perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>(
489 bus, objPath.c_str());
490
491 perfLossIface->performanceLossHigh(threshold.value(
492 "PerformanceLossHigh", std::numeric_limits<double>::quiet_NaN()));
493 perfLossIface->performanceLossLow(threshold.value(
494 "PerformanceLossLow", std::numeric_limits<double>::quiet_NaN()));
495 }
496}
497
Rashmica Guptae7efe132021-07-27 19:42:11 +1000498ManagedObjectType VirtualSensors::getObjectsFromDBus()
499{
500 ManagedObjectType objects;
501
502 try
503 {
504 auto method = bus.new_method_call(entityManagerBusName, "/",
505 "org.freedesktop.DBus.ObjectManager",
506 "GetManagedObjects");
507 auto reply = bus.call(method);
508 reply.read(objects);
509 }
510 catch (const sdbusplus::exception::SdBusError& ex)
511 {
512 // If entity manager isn't running yet, keep going.
513 if (std::string("org.freedesktop.DBus.Error.ServiceUnknown") !=
514 ex.name())
515 {
516 throw ex.name();
517 }
518 }
519
520 return objects;
521}
522
523void VirtualSensors::propertiesChanged(sdbusplus::message::message& msg)
524{
525 std::string path;
526 PropertyMap properties;
527
528 msg.read(path, properties);
529
530 /* We get multiple callbacks for one sensor. 'Type' is a required field and
531 * is a unique label so use to to only proceed once per sensor */
532 if (properties.contains("Type"))
533 {
534 if (isCalculationType(path))
535 {
536 createVirtualSensorsFromDBus(path);
537 }
538 }
539}
540
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700541/** @brief Parsing Virtual Sensor config JSON file */
542Json VirtualSensors::parseConfigFile(const std::string configFile)
543{
544 std::ifstream jsonFile(configFile);
545 if (!jsonFile.is_open())
546 {
547 log<level::ERR>("config JSON file not found",
Patrick Williams1846d822021-06-23 14:44:07 -0500548 entry("FILENAME=%s", configFile.c_str()));
Rashmica Guptae7efe132021-07-27 19:42:11 +1000549 return {};
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700550 }
551
552 auto data = Json::parse(jsonFile, nullptr, false);
553 if (data.is_discarded())
554 {
555 log<level::ERR>("config readings JSON parser failure",
Patrick Williams1846d822021-06-23 14:44:07 -0500556 entry("FILENAME=%s", configFile.c_str()));
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700557 throw std::exception{};
558 }
559
560 return data;
561}
562
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700563std::map<std::string, ValueIface::Unit> unitMap = {
564 {"temperature", ValueIface::Unit::DegreesC},
565 {"fan_tach", ValueIface::Unit::RPMS},
566 {"voltage", ValueIface::Unit::Volts},
567 {"altitude", ValueIface::Unit::Meters},
568 {"current", ValueIface::Unit::Amperes},
569 {"power", ValueIface::Unit::Watts},
570 {"energy", ValueIface::Unit::Joules},
Kumar Thangavel2b56ddb2021-01-13 20:16:11 +0530571 {"utilization", ValueIface::Unit::Percent},
Rashmica Gupta4ac7a7f2021-07-08 12:30:50 +1000572 {"airflow", ValueIface::Unit::CFM},
573 {"pressure", ValueIface::Unit::Pascals}};
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700574
Rashmica Guptae7efe132021-07-27 19:42:11 +1000575const std::string getSensorTypeFromUnit(const std::string& unit)
576{
577 std::string unitPrefix = "xyz.openbmc_project.Sensor.Value.Unit.";
578 for (auto [type, unitObj] : unitMap)
579 {
580 auto unitPath = ValueIface::convertUnitToString(unitObj);
581 if (unitPath == (unitPrefix + unit))
582 {
583 return type;
584 }
585 }
586 return "";
587}
588
589void VirtualSensors::setupMatches()
590{
591 /* Already setup */
592 if (!this->matches.empty())
593 {
594 return;
595 }
596
597 /* Setup matches */
598 auto eventHandler = [this](sdbusplus::message::message& message) {
599 if (message.is_method_error())
600 {
601 log<level::ERR>("Callback method error");
602 return;
603 }
604 this->propertiesChanged(message);
605 };
606
607 for (const char* iface : calculationIfaces)
608 {
609 auto match = std::make_unique<sdbusplus::bus::match::match>(
610 bus,
611 sdbusplus::bus::match::rules::propertiesChangedNamespace(
612 "/xyz/openbmc_project/inventory", iface),
613 eventHandler);
614 this->matches.emplace_back(std::move(match));
615 }
616}
617
618void VirtualSensors::createVirtualSensorsFromDBus(
619 const std::string& calculationIface)
620{
621 if (calculationIface.empty())
622 {
623 log<level::ERR>("No calculation type supplied");
624 return;
625 }
626 auto objects = getObjectsFromDBus();
627
628 /* Get virtual sensors config data */
629 for (const auto& [path, interfaceMap] : objects)
630 {
631 auto objpath = static_cast<std::string>(path);
632 std::string name = path.filename();
633 std::string sensorType, sensorUnit;
634
635 /* Find Virtual Sensor interfaces */
636 if (!interfaceMap.contains(calculationIface))
637 {
638 continue;
639 }
640 if (name.empty())
641 {
642 log<level::ERR>(
643 "Virtual Sensor name not found in entity manager config");
644 continue;
645 }
646 if (virtualSensorsMap.contains(name))
647 {
648 log<level::ERR>("A virtual sensor with this name already exists",
649 entry("NAME=%s", name.c_str()));
650 continue;
651 }
652
653 /* Extract the virtual sensor type as we need this to initialize the
654 * sensor */
655 for (const auto& [interface, propertyMap] : interfaceMap)
656 {
657 if (interface != calculationIface)
658 {
659 continue;
660 }
661 auto itr = propertyMap.find("Units");
662 if (itr != propertyMap.end())
663 {
664 sensorUnit = std::get<std::string>(itr->second);
665 break;
666 }
667 }
668 sensorType = getSensorTypeFromUnit(sensorUnit);
669 if (sensorType.empty())
670 {
671 log<level::ERR>("Sensor unit is not supported",
672 entry("TYPE=%s", sensorUnit.c_str()));
673 continue;
674 }
675
676 try
677 {
678 auto virtObjPath = sensorDbusPath + sensorType + "/" + name;
679
680 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
681 bus, virtObjPath.c_str(), interfaceMap, name, sensorType,
682 calculationIface);
683 log<level::INFO>("Added a new virtual sensor",
684 entry("NAME=%s", name.c_str()));
685 virtualSensorPtr->updateVirtualSensor();
686
687 /* Initialize unit value for virtual sensor */
688 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
689 virtualSensorPtr->emit_object_added();
690
691 virtualSensorsMap.emplace(name, std::move(virtualSensorPtr));
692
693 /* Setup match for interfaces removed */
694 auto intfRemoved = [this, objpath,
695 name](sdbusplus::message::message& message) {
696 if (!virtualSensorsMap.contains(name))
697 {
698 return;
699 }
700 sdbusplus::message::object_path path;
701 message.read(path);
702 if (static_cast<const std::string&>(path) == objpath)
703 {
704 log<level::INFO>("Removed a virtual sensor",
705 entry("NAME=%s", name.c_str()));
706 virtualSensorsMap.erase(name);
707 }
708 };
709 auto matchOnRemove = std::make_unique<sdbusplus::bus::match::match>(
710 bus,
711 sdbusplus::bus::match::rules::interfacesRemoved() +
712 sdbusplus::bus::match::rules::argNpath(0, objpath),
713 intfRemoved);
714 /* TODO: slight race condition here. Check that the config still
715 * exists */
716 this->matches.emplace_back(std::move(matchOnRemove));
717 }
718 catch (std::invalid_argument& ia)
719 {
720 log<level::ERR>("Failed to set up virtual sensor",
721 entry("Error=%s", ia.what()));
722 }
723 }
724}
725
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700726void VirtualSensors::createVirtualSensors()
727{
728 static const Json empty{};
729
730 auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE);
Rashmica Guptae7efe132021-07-27 19:42:11 +1000731
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700732 // print values
733 if (DEBUG)
Rashmica Guptae7efe132021-07-27 19:42:11 +1000734 {
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700735 std::cout << "Config json data:\n" << data << "\n\n";
Rashmica Guptae7efe132021-07-27 19:42:11 +1000736 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700737
738 /* Get virtual sensors config data */
739 for (const auto& j : data)
740 {
741 auto desc = j.value("Desc", empty);
742 if (!desc.empty())
743 {
Rashmica Guptae7efe132021-07-27 19:42:11 +1000744 if (desc.value("Config", "") == "D-Bus")
745 {
746 /* Look on D-Bus for a virtual sensor config. Set up matches
747 * first because the configs may not be on D-Bus yet and we
748 * don't want to miss them */
749 setupMatches();
750
751 if (desc.contains("Type"))
752 {
753 auto path = "xyz.openbmc_project.Configuration." +
754 desc.value("Type", "");
755 if (!isCalculationType(path))
756 {
757 log<level::ERR>(
758 "Invalid calculation type supplied\n",
759 entry("TYPE=%s", desc.value("Type", "").c_str()));
760 continue;
761 }
762 createVirtualSensorsFromDBus(path);
763 }
764 continue;
765 }
766
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700767 std::string sensorType = desc.value("SensorType", "");
768 std::string name = desc.value("Name", "");
Rashmica Gupta665a0a22021-06-30 11:35:28 +1000769 std::replace(name.begin(), name.end(), ' ', '_');
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700770
771 if (!name.empty() && !sensorType.empty())
772 {
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700773 if (unitMap.find(sensorType) == unitMap.end())
774 {
775 log<level::ERR>("Sensor type is not supported",
Patrick Williams1846d822021-06-23 14:44:07 -0500776 entry("TYPE=%s", sensorType.c_str()));
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700777 }
778 else
779 {
Rashmica Gupta67d8b9d2021-06-30 11:41:14 +1000780 if (virtualSensorsMap.find(name) != virtualSensorsMap.end())
781 {
782 log<level::ERR>(
783 "A virtual sensor with this name already exists",
784 entry("TYPE=%s", name.c_str()));
785 continue;
786 }
Rashmica Gupta862c3d12021-08-06 12:19:31 +1000787 auto objPath = sensorDbusPath + sensorType + "/" + name;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700788
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700789 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
790 bus, objPath.c_str(), j, name);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700791
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700792 log<level::INFO>("Added a new virtual sensor",
Patrick Williams1846d822021-06-23 14:44:07 -0500793 entry("NAME=%s", name.c_str()));
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700794 virtualSensorPtr->updateVirtualSensor();
795
796 /* Initialize unit value for virtual sensor */
797 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
Rashmica Guptaa2fa63a2021-08-06 12:21:13 +1000798 virtualSensorPtr->emit_object_added();
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700799
800 virtualSensorsMap.emplace(std::move(name),
801 std::move(virtualSensorPtr));
802 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700803 }
804 else
805 {
806 log<level::ERR>("Sensor type or name not found in config file");
807 }
808 }
809 else
810 {
811 log<level::ERR>(
812 "Descriptor for new virtual sensor not found in config file");
813 }
814 }
815}
816
817} // namespace virtualSensor
818} // namespace phosphor
819
820/**
821 * @brief Main
822 */
823int main()
824{
825
826 // Get a default event loop
827 auto event = sdeventplus::Event::get_default();
828
829 // Get a handle to system dbus
830 auto bus = sdbusplus::bus::new_default();
831
Matt Spinler6c19e7d2021-01-12 16:26:45 -0600832 // Add the ObjectManager interface
833 sdbusplus::server::manager::manager objManager(bus, "/");
834
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700835 // Create an virtual sensors object
836 phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
837
838 // Request service bus name
839 bus.request_name(busName);
840
841 // Attach the bus to sd_event to service user requests
842 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
843 event.loop();
844
845 return 0;
846}