blob: e7363cca2fe7b819e77481a8a1fdaf15e4472738 [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/";
16static constexpr uint8_t defaultHighThreshold = 100;
17static constexpr uint8_t defaultLowThreshold = 0;
18
19using namespace phosphor::logging;
20
Vijay Khemka51f898e2020-09-09 22:24:18 -070021int handleDbusSignal(sd_bus_message* msg, void* usrData, sd_bus_error*)
22{
23 if (usrData == nullptr)
24 {
25 throw std::runtime_error("Invalid match");
26 }
27
28 auto sdbpMsg = sdbusplus::message::message(msg);
29 std::string msgIfce;
30 std::map<std::string, std::variant<int64_t, double, bool>> msgData;
31
32 sdbpMsg.read(msgIfce, msgData);
33
34 if (msgData.find("Value") != msgData.end())
35 {
36 using namespace phosphor::virtualSensor;
37 VirtualSensor* obj = static_cast<VirtualSensor*>(usrData);
38 // TODO(openbmc/phosphor-virtual-sensor#1): updateVirtualSensor should
39 // be changed to take the information we got from the signal, to avoid
40 // having to do numerous dbus queries.
41 obj->updateVirtualSensor();
42 }
43 return 0;
44}
45
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070046namespace phosphor
47{
48namespace virtualSensor
49{
50
51void printParams(const VirtualSensor::ParamMap& paramMap)
52{
53 for (const auto& p : paramMap)
54 {
55 const auto& p1 = p.first;
56 const auto& p2 = p.second;
57 auto val = p2->getParamValue();
58 std::cout << p1 << " = " << val << "\n";
59 }
60}
61
62double SensorParam::getParamValue()
63{
64 switch (paramType)
65 {
66 case constParam:
67 return value;
68 break;
Vijay Khemka7452a862020-08-11 16:01:23 -070069 case dbusParam:
70 return dbusSensor->getSensorValue();
71 break;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070072 default:
73 throw std::invalid_argument("param type not supported");
74 }
75}
76
Matt Spinlerce675222021-01-14 16:38:09 -060077void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
78 const std::string& objPath)
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070079{
80
81 static const Json empty{};
82
83 /* Get threshold values if defined in config */
84 auto threshold = sensorConfig.value("Threshold", empty);
85 if (!threshold.empty())
86 {
Matt Spinlerce675222021-01-14 16:38:09 -060087 criticalIface = std::make_unique<CriticalObject>(bus, objPath.c_str());
88 criticalIface->criticalHigh(
89 threshold.value("CriticalHigh", defaultHighThreshold));
90 criticalIface->criticalLow(
91 threshold.value("CriticalLow", defaultLowThreshold));
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070092
Matt Spinlerce675222021-01-14 16:38:09 -060093 warningIface = std::make_unique<WarningObject>(bus, objPath.c_str());
94 warningIface->warningHigh(
95 threshold.value("WarningHigh", defaultHighThreshold));
96 warningIface->warningLow(
97 threshold.value("WarningLow", defaultLowThreshold));
98
99 // Only create the high and low shutdown interfaces if
100 // at least one of their values is present.
101 if (threshold.contains("HardShutdownHigh") ||
102 threshold.contains("HardShutdownLow"))
103 {
104 hardShutdownIface =
105 std::make_unique<HardShutdownObject>(bus, objPath.c_str());
106
107 hardShutdownIface->hardShutdownHigh(threshold.value(
108 "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
109 hardShutdownIface->hardShutdownLow(threshold.value(
110 "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
111 }
112
113 if (threshold.contains("SoftShutdownHigh") ||
114 threshold.contains("SoftShutdownLow"))
115 {
116 softShutdownIface =
117 std::make_unique<SoftShutdownObject>(bus, objPath.c_str());
118
119 softShutdownIface->softShutdownHigh(threshold.value(
120 "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
121 softShutdownIface->softShutdownLow(threshold.value(
122 "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
123 }
Vijay Khemkac62a5542020-09-10 14:45:49 -0700124 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700125
126 /* Get expression string */
127 exprStr = sensorConfig.value("Expression", "");
128
129 /* Get all the parameter listed in configuration */
130 auto params = sensorConfig.value("Params", empty);
131
132 /* Check for constant parameter */
133 const auto& consParams = params.value("ConstParam", empty);
134 if (!consParams.empty())
135 {
136 for (auto& j : consParams)
137 {
138 if (j.find("ParamName") != j.end())
139 {
140 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700141 std::string name = j["ParamName"];
142 symbols.create_variable(name);
143 paramMap.emplace(std::move(name), std::move(paramPtr));
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700144 }
145 else
146 {
147 /* Invalid configuration */
148 throw std::invalid_argument(
149 "ParamName not found in configuration");
150 }
151 }
152 }
153
Vijay Khemka7452a862020-08-11 16:01:23 -0700154 /* Check for dbus parameter */
155 auto dbusParams = params.value("DbusParam", empty);
156 if (!dbusParams.empty())
157 {
158 for (auto& j : dbusParams)
159 {
160 /* Get parameter dbus sensor descriptor */
161 auto desc = j.value("Desc", empty);
162 if ((!desc.empty()) && (j.find("ParamName") != j.end()))
163 {
164 std::string sensorType = desc.value("SensorType", "");
165 std::string name = desc.value("Name", "");
166
167 if (!sensorType.empty() && !name.empty())
168 {
169 std::string objPath(sensorDbusPath);
170 objPath += sensorType + "/" + name;
171
Vijay Khemka51f898e2020-09-09 22:24:18 -0700172 auto paramPtr =
173 std::make_unique<SensorParam>(bus, objPath, this);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700174 std::string name = j["ParamName"];
175 symbols.create_variable(name);
176 paramMap.emplace(std::move(name), std::move(paramPtr));
Vijay Khemka7452a862020-08-11 16:01:23 -0700177 }
178 }
179 }
180 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700181
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700182 symbols.add_constants();
Matt Spinler9f1ef4f2020-11-09 15:59:11 -0600183 symbols.add_package(vecopsPackage);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700184 expression.register_symbol_table(symbols);
185
186 /* parser from exprtk */
187 exprtk::parser<double> parser{};
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600188 if (!parser.compile(exprStr, expression))
189 {
190 log<level::ERR>("Expression compilation failed");
191
192 for (std::size_t i = 0; i < parser.error_count(); ++i)
193 {
194 auto error = parser.get_error(i);
195
196 log<level::ERR>(
197 fmt::format(
198 "Position: {} Type: {} Message: {}", error.token.position,
199 exprtk::parser_error::to_str(error.mode), error.diagnostic)
200 .c_str());
201 }
202 throw std::runtime_error("Expression compilation failed");
203 }
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700204
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700205 /* Print all parameters for debug purpose only */
206 if (DEBUG)
207 printParams(paramMap);
208}
209
210void VirtualSensor::setSensorValue(double value)
211{
212 ValueIface::value(value);
213}
214
Vijay Khemka32a71562020-09-10 15:29:18 -0700215void VirtualSensor::checkSensorThreshold(const double value)
216{
Matt Spinlerce675222021-01-14 16:38:09 -0600217 if (warningIface)
Vijay Khemka32a71562020-09-10 15:29:18 -0700218 {
Matt Spinlerce675222021-01-14 16:38:09 -0600219 if (value >= warningIface->warningHigh())
Vijay Khemka32a71562020-09-10 15:29:18 -0700220 {
Matt Spinlerce675222021-01-14 16:38:09 -0600221 if (!warningIface->warningAlarmHigh())
222 {
223 warningIface->warningAlarmHigh(true);
224 log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
225 "warning high threshold",
226 entry("NAME = %s", name.c_str()));
227 }
228 }
229 else if (warningIface->warningAlarmHigh())
230 {
231 warningIface->warningAlarmHigh(false);
232 log<level::INFO>("DEASSERT: Virtual Sensor is under "
233 "warning high threshold",
234 entry("NAME = %s", name.c_str()));
235 }
236
237 if (value <= warningIface->warningLow())
238 {
239 if (!warningIface->warningAlarmLow())
240 {
241 warningIface->warningAlarmLow(true);
242 log<level::ERR>("ASSERT: Virtual Sensor is under "
243 "warning low threshold",
244 entry("NAME = %s", name.c_str()));
245 }
246 }
247 else if (warningIface->warningAlarmLow())
248 {
249 warningIface->warningAlarmLow(false);
250 log<level::INFO>("DEASSERT: Virtual Sensor is above "
251 "warning low threshold",
252 entry("NAME = %s", name.c_str()));
Vijay Khemka32a71562020-09-10 15:29:18 -0700253 }
Vijay Khemka32a71562020-09-10 15:29:18 -0700254 }
Vijay Khemka32a71562020-09-10 15:29:18 -0700255
Matt Spinlerce675222021-01-14 16:38:09 -0600256 if (criticalIface)
Vijay Khemka32a71562020-09-10 15:29:18 -0700257 {
Matt Spinlerce675222021-01-14 16:38:09 -0600258 if (value >= criticalIface->criticalHigh())
Vijay Khemka32a71562020-09-10 15:29:18 -0700259 {
Matt Spinlerce675222021-01-14 16:38:09 -0600260 if (!criticalIface->criticalAlarmHigh())
261 {
262 criticalIface->criticalAlarmHigh(true);
263 log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
264 "critical high threshold",
265 entry("NAME = %s", name.c_str()));
266 }
267 }
268 else if (criticalIface->criticalAlarmHigh())
269 {
270 criticalIface->criticalAlarmHigh(false);
271 log<level::INFO>("DEASSERT: Virtual Sensor is under "
272 "critical high threshold",
273 entry("NAME = %s", name.c_str()));
274 }
275
276 if (value <= criticalIface->criticalLow())
277 {
278 if (!criticalIface->criticalAlarmLow())
279 {
280 criticalIface->criticalAlarmLow(true);
281 log<level::ERR>("ASSERT: Virtual Sensor is under "
282 "critical low threshold",
283 entry("NAME = %s", name.c_str()));
284 }
285 }
286 else if (criticalIface->criticalAlarmLow())
287 {
288 criticalIface->criticalAlarmLow(false);
289 log<level::INFO>("DEASSERT: Virtual Sensor is above "
290 "critical low threshold",
291 entry("NAME = %s", name.c_str()));
Vijay Khemka32a71562020-09-10 15:29:18 -0700292 }
Vijay Khemka32a71562020-09-10 15:29:18 -0700293 }
Vijay Khemka32a71562020-09-10 15:29:18 -0700294
Matt Spinlerce675222021-01-14 16:38:09 -0600295 if (softShutdownIface)
Vijay Khemka32a71562020-09-10 15:29:18 -0700296 {
Matt Spinlerce675222021-01-14 16:38:09 -0600297 if (value >= softShutdownIface->softShutdownHigh())
Vijay Khemka32a71562020-09-10 15:29:18 -0700298 {
Matt Spinlerce675222021-01-14 16:38:09 -0600299 if (!softShutdownIface->softShutdownAlarmHigh())
300 {
301 softShutdownIface->softShutdownAlarmHigh(true);
302 log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
303 "softShutdown high threshold",
304 entry("NAME = %s", name.c_str()));
305 }
306 }
307 else if (softShutdownIface->softShutdownAlarmHigh())
308 {
309 softShutdownIface->softShutdownAlarmHigh(false);
310 log<level::INFO>("DEASSERT: Virtual Sensor is under "
311 "softShutdown high threshold",
312 entry("NAME = %s", name.c_str()));
313 }
314
315 if (value <= softShutdownIface->softShutdownLow())
316 {
317 if (!softShutdownIface->softShutdownAlarmLow())
318 {
319 softShutdownIface->softShutdownAlarmLow(true);
320 log<level::ERR>("ASSERT: Virtual Sensor is under "
321 "softShutdown low threshold",
322 entry("NAME = %s", name.c_str()));
323 }
324 }
325 else if (softShutdownIface->softShutdownAlarmLow())
326 {
327 softShutdownIface->softShutdownAlarmLow(false);
328 log<level::INFO>("DEASSERT: Virtual Sensor is above "
329 "softShutdown low threshold",
330 entry("NAME = %s", name.c_str()));
Vijay Khemka32a71562020-09-10 15:29:18 -0700331 }
Vijay Khemka32a71562020-09-10 15:29:18 -0700332 }
Matt Spinler26edaad2020-12-01 09:53:42 -0600333
Matt Spinlerce675222021-01-14 16:38:09 -0600334 if (hardShutdownIface)
Matt Spinler26edaad2020-12-01 09:53:42 -0600335 {
Matt Spinlerce675222021-01-14 16:38:09 -0600336 if (value >= hardShutdownIface->hardShutdownHigh())
Matt Spinler26edaad2020-12-01 09:53:42 -0600337 {
Matt Spinlerce675222021-01-14 16:38:09 -0600338 if (!hardShutdownIface->hardShutdownAlarmHigh())
339 {
340 hardShutdownIface->hardShutdownAlarmHigh(true);
341 log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
342 "hardShutdown high threshold",
343 entry("NAME = %s", name.c_str()));
344 }
Matt Spinler26edaad2020-12-01 09:53:42 -0600345 }
Matt Spinlerce675222021-01-14 16:38:09 -0600346 else if (hardShutdownIface->hardShutdownAlarmHigh())
347 {
348 hardShutdownIface->hardShutdownAlarmHigh(false);
349 log<level::INFO>("DEASSERT: Virtual Sensor is under "
350 "hardShutdown high threshold",
351 entry("NAME = %s", name.c_str()));
352 }
353
354 if (value <= hardShutdownIface->hardShutdownLow())
355 {
356 if (!hardShutdownIface->hardShutdownAlarmLow())
357 {
358 hardShutdownIface->hardShutdownAlarmLow(true);
359 log<level::ERR>("ASSERT: Virtual Sensor is under "
360 "hardShutdown low threshold",
361 entry("NAME = %s", name.c_str()));
362 }
363 }
364 else if (hardShutdownIface->hardShutdownAlarmLow())
365 {
366 hardShutdownIface->hardShutdownAlarmLow(false);
367 log<level::INFO>("DEASSERT: Virtual Sensor is above "
368 "hardShutdown low threshold",
369 entry("NAME = %s", name.c_str()));
370 }
Vijay Khemka32a71562020-09-10 15:29:18 -0700371 }
372}
373
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700374void VirtualSensor::updateVirtualSensor()
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700375{
376 for (auto& param : paramMap)
377 {
378 auto& name = param.first;
379 auto& data = param.second;
380 if (auto var = symbols.get_variable(name))
381 {
382 var->ref() = data->getParamValue();
383 }
384 else
385 {
386 /* Invalid parameter */
387 throw std::invalid_argument("ParamName not found in symbols");
388 }
389 }
390 double val = expression.value();
Vijay Khemka32a71562020-09-10 15:29:18 -0700391
392 /* Set sensor value to dbus interface */
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700393 setSensorValue(val);
Vijay Khemka32a71562020-09-10 15:29:18 -0700394
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700395 if (DEBUG)
396 std::cout << "Sensor value is " << val << "\n";
Vijay Khemka32a71562020-09-10 15:29:18 -0700397
398 /* Check sensor threshold and log required message */
399 checkSensorThreshold(val);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700400}
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700401
402/** @brief Parsing Virtual Sensor config JSON file */
403Json VirtualSensors::parseConfigFile(const std::string configFile)
404{
405 std::ifstream jsonFile(configFile);
406 if (!jsonFile.is_open())
407 {
408 log<level::ERR>("config JSON file not found",
409 entry("FILENAME = %s", configFile.c_str()));
410 throw std::exception{};
411 }
412
413 auto data = Json::parse(jsonFile, nullptr, false);
414 if (data.is_discarded())
415 {
416 log<level::ERR>("config readings JSON parser failure",
417 entry("FILENAME = %s", configFile.c_str()));
418 throw std::exception{};
419 }
420
421 return data;
422}
423
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700424std::map<std::string, ValueIface::Unit> unitMap = {
425 {"temperature", ValueIface::Unit::DegreesC},
426 {"fan_tach", ValueIface::Unit::RPMS},
427 {"voltage", ValueIface::Unit::Volts},
428 {"altitude", ValueIface::Unit::Meters},
429 {"current", ValueIface::Unit::Amperes},
430 {"power", ValueIface::Unit::Watts},
431 {"energy", ValueIface::Unit::Joules},
432 {"utilization", ValueIface::Unit::Percent}};
433
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700434void VirtualSensors::createVirtualSensors()
435{
436 static const Json empty{};
437
438 auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE);
439 // print values
440 if (DEBUG)
441 std::cout << "Config json data:\n" << data << "\n\n";
442
443 /* Get virtual sensors config data */
444 for (const auto& j : data)
445 {
446 auto desc = j.value("Desc", empty);
447 if (!desc.empty())
448 {
449 std::string sensorType = desc.value("SensorType", "");
450 std::string name = desc.value("Name", "");
451
452 if (!name.empty() && !sensorType.empty())
453 {
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700454 if (unitMap.find(sensorType) == unitMap.end())
455 {
456 log<level::ERR>("Sensor type is not supported",
457 entry("TYPE = %s", sensorType.c_str()));
458 }
459 else
460 {
461 std::string objPath(sensorDbusPath);
462 objPath += sensorType + "/" + name;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700463
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700464 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
465 bus, objPath.c_str(), j, name);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700466
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700467 log<level::INFO>("Added a new virtual sensor",
468 entry("NAME = %s", name.c_str()));
469 virtualSensorPtr->updateVirtualSensor();
470
471 /* Initialize unit value for virtual sensor */
472 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
473
474 virtualSensorsMap.emplace(std::move(name),
475 std::move(virtualSensorPtr));
476 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700477 }
478 else
479 {
480 log<level::ERR>("Sensor type or name not found in config file");
481 }
482 }
483 else
484 {
485 log<level::ERR>(
486 "Descriptor for new virtual sensor not found in config file");
487 }
488 }
489}
490
491} // namespace virtualSensor
492} // namespace phosphor
493
494/**
495 * @brief Main
496 */
497int main()
498{
499
500 // Get a default event loop
501 auto event = sdeventplus::Event::get_default();
502
503 // Get a handle to system dbus
504 auto bus = sdbusplus::bus::new_default();
505
Matt Spinler6c19e7d2021-01-12 16:26:45 -0600506 // Add the ObjectManager interface
507 sdbusplus::server::manager::manager objManager(bus, "/");
508
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700509 // Create an virtual sensors object
510 phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
511
512 // Request service bus name
513 bus.request_name(busName);
514
515 // Attach the bus to sd_event to service user requests
516 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
517 event.loop();
518
519 return 0;
520}