blob: ce429f6860f90798cb8f15c55ee85beb1ed4827c [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();
157 expression.register_symbol_table(symbols);
158
159 /* parser from exprtk */
160 exprtk::parser<double> parser{};
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600161 if (!parser.compile(exprStr, expression))
162 {
163 log<level::ERR>("Expression compilation failed");
164
165 for (std::size_t i = 0; i < parser.error_count(); ++i)
166 {
167 auto error = parser.get_error(i);
168
169 log<level::ERR>(
170 fmt::format(
171 "Position: {} Type: {} Message: {}", error.token.position,
172 exprtk::parser_error::to_str(error.mode), error.diagnostic)
173 .c_str());
174 }
175 throw std::runtime_error("Expression compilation failed");
176 }
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700177
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700178 /* Print all parameters for debug purpose only */
179 if (DEBUG)
180 printParams(paramMap);
181}
182
183void VirtualSensor::setSensorValue(double value)
184{
185 ValueIface::value(value);
186}
187
Vijay Khemkac62a5542020-09-10 14:45:49 -0700188void VirtualSensor::setSensorThreshold(Threshold& sensorThreshold)
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700189{
190 CriticalInterface::criticalHigh(sensorThreshold.criticalHigh);
191 CriticalInterface::criticalLow(sensorThreshold.criticalLow);
192 WarningInterface::warningHigh(sensorThreshold.warningHigh);
193 WarningInterface::warningLow(sensorThreshold.warningLow);
194}
195
Vijay Khemka32a71562020-09-10 15:29:18 -0700196void VirtualSensor::checkSensorThreshold(const double value)
197{
198 auto criticalHigh = CriticalInterface::criticalHigh();
199 auto criticalLow = CriticalInterface::criticalLow();
200 auto warningHigh = WarningInterface::warningHigh();
201 auto warningLow = WarningInterface::warningLow();
202
203 if (value > criticalHigh)
204 {
205 if (!CriticalInterface::criticalAlarmHigh())
206 {
207 CriticalInterface::criticalAlarmHigh(true);
208 log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
209 "critical high threshold",
210 entry("NAME = %s", name.c_str()));
211 }
212 return;
213 }
214
215 if (CriticalInterface::criticalAlarmHigh())
216 {
217 CriticalInterface::criticalAlarmHigh(false);
218 log<level::INFO>("DEASSERT: Virtual Sensor is under "
219 "critical high threshold",
220 entry("NAME = %s", name.c_str()));
221 }
222
223 if (value > warningHigh)
224 {
225 if (!WarningInterface::warningAlarmHigh())
226 {
227 WarningInterface::warningAlarmHigh(true);
228 log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
229 "warning high threshold",
230 entry("NAME = %s", name.c_str()));
231 }
232 return;
233 }
234
235 if (WarningInterface::warningAlarmHigh())
236 {
237 WarningInterface::warningAlarmHigh(false);
238 log<level::INFO>("DEASSERT: Virtual Sensor is under "
239 "warning high threshold",
240 entry("NAME = %s", name.c_str()));
241 }
242
243 if (value < criticalLow)
244 {
245 if (!CriticalInterface::criticalAlarmLow())
246 {
247 CriticalInterface::criticalAlarmLow(true);
248 log<level::ERR>("ASSERT: Virtual Sensor is under "
249 "critical low threshold",
250 entry("NAME = %s", name.c_str()));
251 }
252 return;
253 }
254
255 if (CriticalInterface::criticalAlarmLow())
256 {
257 CriticalInterface::criticalAlarmLow(false);
258 log<level::ERR>("DEASSERT: Virtual Sensor is above "
259 "critical low threshold",
260 entry("NAME = %s", name.c_str()));
261 }
262
263 if (value < warningLow)
264 {
265 if (!WarningInterface::warningAlarmLow())
266 {
267 WarningInterface::warningAlarmLow(true);
268 log<level::ERR>("ASSERT: Virtual Sensor is under "
269 "warning low threshold",
270 entry("NAME = %s", name.c_str()));
271 }
272 return;
273 }
274
275 if (WarningInterface::warningAlarmLow())
276 {
277 WarningInterface::warningAlarmLow(false);
278 log<level::ERR>("DEASSERT: Virtual Sensor is above "
279 "warning low threshold",
280 entry("NAME = %s", name.c_str()));
281 }
282}
283
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700284void VirtualSensor::updateVirtualSensor()
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700285{
286 for (auto& param : paramMap)
287 {
288 auto& name = param.first;
289 auto& data = param.second;
290 if (auto var = symbols.get_variable(name))
291 {
292 var->ref() = data->getParamValue();
293 }
294 else
295 {
296 /* Invalid parameter */
297 throw std::invalid_argument("ParamName not found in symbols");
298 }
299 }
300 double val = expression.value();
Vijay Khemka32a71562020-09-10 15:29:18 -0700301
302 /* Set sensor value to dbus interface */
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700303 setSensorValue(val);
Vijay Khemka32a71562020-09-10 15:29:18 -0700304
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700305 if (DEBUG)
306 std::cout << "Sensor value is " << val << "\n";
Vijay Khemka32a71562020-09-10 15:29:18 -0700307
308 /* Check sensor threshold and log required message */
309 checkSensorThreshold(val);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700310}
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700311
312/** @brief Parsing Virtual Sensor config JSON file */
313Json VirtualSensors::parseConfigFile(const std::string configFile)
314{
315 std::ifstream jsonFile(configFile);
316 if (!jsonFile.is_open())
317 {
318 log<level::ERR>("config JSON file not found",
319 entry("FILENAME = %s", configFile.c_str()));
320 throw std::exception{};
321 }
322
323 auto data = Json::parse(jsonFile, nullptr, false);
324 if (data.is_discarded())
325 {
326 log<level::ERR>("config readings JSON parser failure",
327 entry("FILENAME = %s", configFile.c_str()));
328 throw std::exception{};
329 }
330
331 return data;
332}
333
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700334std::map<std::string, ValueIface::Unit> unitMap = {
335 {"temperature", ValueIface::Unit::DegreesC},
336 {"fan_tach", ValueIface::Unit::RPMS},
337 {"voltage", ValueIface::Unit::Volts},
338 {"altitude", ValueIface::Unit::Meters},
339 {"current", ValueIface::Unit::Amperes},
340 {"power", ValueIface::Unit::Watts},
341 {"energy", ValueIface::Unit::Joules},
342 {"utilization", ValueIface::Unit::Percent}};
343
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700344void VirtualSensors::createVirtualSensors()
345{
346 static const Json empty{};
347
348 auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE);
349 // print values
350 if (DEBUG)
351 std::cout << "Config json data:\n" << data << "\n\n";
352
353 /* Get virtual sensors config data */
354 for (const auto& j : data)
355 {
356 auto desc = j.value("Desc", empty);
357 if (!desc.empty())
358 {
359 std::string sensorType = desc.value("SensorType", "");
360 std::string name = desc.value("Name", "");
361
362 if (!name.empty() && !sensorType.empty())
363 {
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700364 if (unitMap.find(sensorType) == unitMap.end())
365 {
366 log<level::ERR>("Sensor type is not supported",
367 entry("TYPE = %s", sensorType.c_str()));
368 }
369 else
370 {
371 std::string objPath(sensorDbusPath);
372 objPath += sensorType + "/" + name;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700373
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700374 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
375 bus, objPath.c_str(), j, name);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700376
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700377 log<level::INFO>("Added a new virtual sensor",
378 entry("NAME = %s", name.c_str()));
379 virtualSensorPtr->updateVirtualSensor();
380
381 /* Initialize unit value for virtual sensor */
382 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
383
384 virtualSensorsMap.emplace(std::move(name),
385 std::move(virtualSensorPtr));
386 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700387 }
388 else
389 {
390 log<level::ERR>("Sensor type or name not found in config file");
391 }
392 }
393 else
394 {
395 log<level::ERR>(
396 "Descriptor for new virtual sensor not found in config file");
397 }
398 }
399}
400
401} // namespace virtualSensor
402} // namespace phosphor
403
404/**
405 * @brief Main
406 */
407int main()
408{
409
410 // Get a default event loop
411 auto event = sdeventplus::Event::get_default();
412
413 // Get a handle to system dbus
414 auto bus = sdbusplus::bus::new_default();
415
416 // Create an virtual sensors object
417 phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
418
419 // Request service bus name
420 bus.request_name(busName);
421
422 // Attach the bus to sd_event to service user requests
423 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
424 event.loop();
425
426 return 0;
427}