blob: 5c1a6b95591f809e6b1c0926633d14016477517d [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
77void VirtualSensor::initVirtualSensor(const Json& sensorConfig)
78{
79
80 static const Json empty{};
81
82 /* Get threshold values if defined in config */
83 auto threshold = sensorConfig.value("Threshold", empty);
84 if (!threshold.empty())
85 {
Vijay Khemkac62a5542020-09-10 14:45:49 -070086 Threshold sensorThreshold;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070087 sensorThreshold.criticalHigh =
88 threshold.value("CriticalHigh", defaultHighThreshold);
89 sensorThreshold.criticalLow =
90 threshold.value("CriticalLow", defaultLowThreshold);
91 sensorThreshold.warningHigh =
92 threshold.value("WarningHigh", defaultHighThreshold);
93 sensorThreshold.warningLow =
94 threshold.value("WarningLow", defaultLowThreshold);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070095
Vijay Khemkac62a5542020-09-10 14:45:49 -070096 /* Set threshold value to dbus */
97 setSensorThreshold(sensorThreshold);
98 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070099
100 /* Get expression string */
101 exprStr = sensorConfig.value("Expression", "");
102
103 /* Get all the parameter listed in configuration */
104 auto params = sensorConfig.value("Params", empty);
105
106 /* Check for constant parameter */
107 const auto& consParams = params.value("ConstParam", empty);
108 if (!consParams.empty())
109 {
110 for (auto& j : consParams)
111 {
112 if (j.find("ParamName") != j.end())
113 {
114 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700115 std::string name = j["ParamName"];
116 symbols.create_variable(name);
117 paramMap.emplace(std::move(name), std::move(paramPtr));
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700118 }
119 else
120 {
121 /* Invalid configuration */
122 throw std::invalid_argument(
123 "ParamName not found in configuration");
124 }
125 }
126 }
127
Vijay Khemka7452a862020-08-11 16:01:23 -0700128 /* Check for dbus parameter */
129 auto dbusParams = params.value("DbusParam", empty);
130 if (!dbusParams.empty())
131 {
132 for (auto& j : dbusParams)
133 {
134 /* Get parameter dbus sensor descriptor */
135 auto desc = j.value("Desc", empty);
136 if ((!desc.empty()) && (j.find("ParamName") != j.end()))
137 {
138 std::string sensorType = desc.value("SensorType", "");
139 std::string name = desc.value("Name", "");
140
141 if (!sensorType.empty() && !name.empty())
142 {
143 std::string objPath(sensorDbusPath);
144 objPath += sensorType + "/" + name;
145
Vijay Khemka51f898e2020-09-09 22:24:18 -0700146 auto paramPtr =
147 std::make_unique<SensorParam>(bus, objPath, this);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700148 std::string name = j["ParamName"];
149 symbols.create_variable(name);
150 paramMap.emplace(std::move(name), std::move(paramPtr));
Vijay Khemka7452a862020-08-11 16:01:23 -0700151 }
152 }
153 }
154 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700155
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700156 symbols.add_constants();
Matt Spinler9f1ef4f2020-11-09 15:59:11 -0600157 symbols.add_package(vecopsPackage);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700158 expression.register_symbol_table(symbols);
159
160 /* parser from exprtk */
161 exprtk::parser<double> parser{};
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600162 if (!parser.compile(exprStr, expression))
163 {
164 log<level::ERR>("Expression compilation failed");
165
166 for (std::size_t i = 0; i < parser.error_count(); ++i)
167 {
168 auto error = parser.get_error(i);
169
170 log<level::ERR>(
171 fmt::format(
172 "Position: {} Type: {} Message: {}", error.token.position,
173 exprtk::parser_error::to_str(error.mode), error.diagnostic)
174 .c_str());
175 }
176 throw std::runtime_error("Expression compilation failed");
177 }
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700178
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700179 /* Print all parameters for debug purpose only */
180 if (DEBUG)
181 printParams(paramMap);
182}
183
184void VirtualSensor::setSensorValue(double value)
185{
186 ValueIface::value(value);
187}
188
Vijay Khemkac62a5542020-09-10 14:45:49 -0700189void VirtualSensor::setSensorThreshold(Threshold& sensorThreshold)
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700190{
191 CriticalInterface::criticalHigh(sensorThreshold.criticalHigh);
192 CriticalInterface::criticalLow(sensorThreshold.criticalLow);
193 WarningInterface::warningHigh(sensorThreshold.warningHigh);
194 WarningInterface::warningLow(sensorThreshold.warningLow);
195}
196
Vijay Khemka32a71562020-09-10 15:29:18 -0700197void VirtualSensor::checkSensorThreshold(const double value)
198{
199 auto criticalHigh = CriticalInterface::criticalHigh();
200 auto criticalLow = CriticalInterface::criticalLow();
201 auto warningHigh = WarningInterface::warningHigh();
202 auto warningLow = WarningInterface::warningLow();
203
Matt Spinler26edaad2020-12-01 09:53:42 -0600204 if (value >= warningHigh)
Vijay Khemka32a71562020-09-10 15:29:18 -0700205 {
206 if (!WarningInterface::warningAlarmHigh())
207 {
208 WarningInterface::warningAlarmHigh(true);
209 log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
210 "warning high threshold",
211 entry("NAME = %s", name.c_str()));
212 }
Vijay Khemka32a71562020-09-10 15:29:18 -0700213 }
Matt Spinler26edaad2020-12-01 09:53:42 -0600214 else if (WarningInterface::warningAlarmHigh())
Vijay Khemka32a71562020-09-10 15:29:18 -0700215 {
216 WarningInterface::warningAlarmHigh(false);
217 log<level::INFO>("DEASSERT: Virtual Sensor is under "
218 "warning high threshold",
219 entry("NAME = %s", name.c_str()));
220 }
221
Matt Spinler26edaad2020-12-01 09:53:42 -0600222 if (value >= criticalHigh)
Vijay Khemka32a71562020-09-10 15:29:18 -0700223 {
Matt Spinler26edaad2020-12-01 09:53:42 -0600224 if (!CriticalInterface::criticalAlarmHigh())
Vijay Khemka32a71562020-09-10 15:29:18 -0700225 {
Matt Spinler26edaad2020-12-01 09:53:42 -0600226 CriticalInterface::criticalAlarmHigh(true);
227 log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
228 "critical high threshold",
Vijay Khemka32a71562020-09-10 15:29:18 -0700229 entry("NAME = %s", name.c_str()));
230 }
Vijay Khemka32a71562020-09-10 15:29:18 -0700231 }
Matt Spinler26edaad2020-12-01 09:53:42 -0600232 else if (CriticalInterface::criticalAlarmHigh())
Vijay Khemka32a71562020-09-10 15:29:18 -0700233 {
Matt Spinler26edaad2020-12-01 09:53:42 -0600234 CriticalInterface::criticalAlarmHigh(false);
235 log<level::INFO>("DEASSERT: Virtual Sensor is under "
236 "critical high threshold",
237 entry("NAME = %s", name.c_str()));
Vijay Khemka32a71562020-09-10 15:29:18 -0700238 }
239
Matt Spinler26edaad2020-12-01 09:53:42 -0600240 if (value <= warningLow)
Vijay Khemka32a71562020-09-10 15:29:18 -0700241 {
242 if (!WarningInterface::warningAlarmLow())
243 {
244 WarningInterface::warningAlarmLow(true);
245 log<level::ERR>("ASSERT: Virtual Sensor is under "
246 "warning low threshold",
247 entry("NAME = %s", name.c_str()));
248 }
Vijay Khemka32a71562020-09-10 15:29:18 -0700249 }
Matt Spinler26edaad2020-12-01 09:53:42 -0600250 else if (WarningInterface::warningAlarmLow())
Vijay Khemka32a71562020-09-10 15:29:18 -0700251 {
252 WarningInterface::warningAlarmLow(false);
Matt Spinler26edaad2020-12-01 09:53:42 -0600253 log<level::INFO>("DEASSERT: Virtual Sensor is above "
254 "warning low threshold",
255 entry("NAME = %s", name.c_str()));
256 }
257
258 if (value <= criticalLow)
259 {
260 if (!CriticalInterface::criticalAlarmLow())
261 {
262 CriticalInterface::criticalAlarmLow(true);
263 log<level::ERR>("ASSERT: Virtual Sensor is under "
264 "critical low threshold",
265 entry("NAME = %s", name.c_str()));
266 }
267 }
268 else if (CriticalInterface::criticalAlarmLow())
269 {
270 CriticalInterface::criticalAlarmLow(false);
271 log<level::INFO>("DEASSERT: Virtual Sensor is above "
272 "critical low threshold",
273 entry("NAME = %s", name.c_str()));
Vijay Khemka32a71562020-09-10 15:29:18 -0700274 }
275}
276
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700277void VirtualSensor::updateVirtualSensor()
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700278{
279 for (auto& param : paramMap)
280 {
281 auto& name = param.first;
282 auto& data = param.second;
283 if (auto var = symbols.get_variable(name))
284 {
285 var->ref() = data->getParamValue();
286 }
287 else
288 {
289 /* Invalid parameter */
290 throw std::invalid_argument("ParamName not found in symbols");
291 }
292 }
293 double val = expression.value();
Vijay Khemka32a71562020-09-10 15:29:18 -0700294
295 /* Set sensor value to dbus interface */
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700296 setSensorValue(val);
Vijay Khemka32a71562020-09-10 15:29:18 -0700297
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700298 if (DEBUG)
299 std::cout << "Sensor value is " << val << "\n";
Vijay Khemka32a71562020-09-10 15:29:18 -0700300
301 /* Check sensor threshold and log required message */
302 checkSensorThreshold(val);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700303}
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700304
305/** @brief Parsing Virtual Sensor config JSON file */
306Json VirtualSensors::parseConfigFile(const std::string configFile)
307{
308 std::ifstream jsonFile(configFile);
309 if (!jsonFile.is_open())
310 {
311 log<level::ERR>("config JSON file not found",
312 entry("FILENAME = %s", configFile.c_str()));
313 throw std::exception{};
314 }
315
316 auto data = Json::parse(jsonFile, nullptr, false);
317 if (data.is_discarded())
318 {
319 log<level::ERR>("config readings JSON parser failure",
320 entry("FILENAME = %s", configFile.c_str()));
321 throw std::exception{};
322 }
323
324 return data;
325}
326
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700327std::map<std::string, ValueIface::Unit> unitMap = {
328 {"temperature", ValueIface::Unit::DegreesC},
329 {"fan_tach", ValueIface::Unit::RPMS},
330 {"voltage", ValueIface::Unit::Volts},
331 {"altitude", ValueIface::Unit::Meters},
332 {"current", ValueIface::Unit::Amperes},
333 {"power", ValueIface::Unit::Watts},
334 {"energy", ValueIface::Unit::Joules},
335 {"utilization", ValueIface::Unit::Percent}};
336
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700337void VirtualSensors::createVirtualSensors()
338{
339 static const Json empty{};
340
341 auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE);
342 // print values
343 if (DEBUG)
344 std::cout << "Config json data:\n" << data << "\n\n";
345
346 /* Get virtual sensors config data */
347 for (const auto& j : data)
348 {
349 auto desc = j.value("Desc", empty);
350 if (!desc.empty())
351 {
352 std::string sensorType = desc.value("SensorType", "");
353 std::string name = desc.value("Name", "");
354
355 if (!name.empty() && !sensorType.empty())
356 {
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700357 if (unitMap.find(sensorType) == unitMap.end())
358 {
359 log<level::ERR>("Sensor type is not supported",
360 entry("TYPE = %s", sensorType.c_str()));
361 }
362 else
363 {
364 std::string objPath(sensorDbusPath);
365 objPath += sensorType + "/" + name;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700366
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700367 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
368 bus, objPath.c_str(), j, name);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700369
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700370 log<level::INFO>("Added a new virtual sensor",
371 entry("NAME = %s", name.c_str()));
372 virtualSensorPtr->updateVirtualSensor();
373
374 /* Initialize unit value for virtual sensor */
375 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
376
377 virtualSensorsMap.emplace(std::move(name),
378 std::move(virtualSensorPtr));
379 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700380 }
381 else
382 {
383 log<level::ERR>("Sensor type or name not found in config file");
384 }
385 }
386 else
387 {
388 log<level::ERR>(
389 "Descriptor for new virtual sensor not found in config file");
390 }
391 }
392}
393
394} // namespace virtualSensor
395} // namespace phosphor
396
397/**
398 * @brief Main
399 */
400int main()
401{
402
403 // Get a default event loop
404 auto event = sdeventplus::Event::get_default();
405
406 // Get a handle to system dbus
407 auto bus = sdbusplus::bus::new_default();
408
409 // Create an virtual sensors object
410 phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
411
412 // Request service bus name
413 bus.request_name(busName);
414
415 // Attach the bus to sd_event to service user requests
416 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
417 event.loop();
418
419 return 0;
420}