blob: 25c127c6160074bd8ce9ac07ae30b1a08c71508c [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
204 if (value > criticalHigh)
205 {
206 if (!CriticalInterface::criticalAlarmHigh())
207 {
208 CriticalInterface::criticalAlarmHigh(true);
209 log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
210 "critical high threshold",
211 entry("NAME = %s", name.c_str()));
212 }
213 return;
214 }
215
216 if (CriticalInterface::criticalAlarmHigh())
217 {
218 CriticalInterface::criticalAlarmHigh(false);
219 log<level::INFO>("DEASSERT: Virtual Sensor is under "
220 "critical high threshold",
221 entry("NAME = %s", name.c_str()));
222 }
223
224 if (value > warningHigh)
225 {
226 if (!WarningInterface::warningAlarmHigh())
227 {
228 WarningInterface::warningAlarmHigh(true);
229 log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
230 "warning high threshold",
231 entry("NAME = %s", name.c_str()));
232 }
233 return;
234 }
235
236 if (WarningInterface::warningAlarmHigh())
237 {
238 WarningInterface::warningAlarmHigh(false);
239 log<level::INFO>("DEASSERT: Virtual Sensor is under "
240 "warning high threshold",
241 entry("NAME = %s", name.c_str()));
242 }
243
244 if (value < criticalLow)
245 {
246 if (!CriticalInterface::criticalAlarmLow())
247 {
248 CriticalInterface::criticalAlarmLow(true);
249 log<level::ERR>("ASSERT: Virtual Sensor is under "
250 "critical low threshold",
251 entry("NAME = %s", name.c_str()));
252 }
253 return;
254 }
255
256 if (CriticalInterface::criticalAlarmLow())
257 {
258 CriticalInterface::criticalAlarmLow(false);
259 log<level::ERR>("DEASSERT: Virtual Sensor is above "
260 "critical low threshold",
261 entry("NAME = %s", name.c_str()));
262 }
263
264 if (value < warningLow)
265 {
266 if (!WarningInterface::warningAlarmLow())
267 {
268 WarningInterface::warningAlarmLow(true);
269 log<level::ERR>("ASSERT: Virtual Sensor is under "
270 "warning low threshold",
271 entry("NAME = %s", name.c_str()));
272 }
273 return;
274 }
275
276 if (WarningInterface::warningAlarmLow())
277 {
278 WarningInterface::warningAlarmLow(false);
279 log<level::ERR>("DEASSERT: Virtual Sensor is above "
280 "warning low threshold",
281 entry("NAME = %s", name.c_str()));
282 }
283}
284
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700285void VirtualSensor::updateVirtualSensor()
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700286{
287 for (auto& param : paramMap)
288 {
289 auto& name = param.first;
290 auto& data = param.second;
291 if (auto var = symbols.get_variable(name))
292 {
293 var->ref() = data->getParamValue();
294 }
295 else
296 {
297 /* Invalid parameter */
298 throw std::invalid_argument("ParamName not found in symbols");
299 }
300 }
301 double val = expression.value();
Vijay Khemka32a71562020-09-10 15:29:18 -0700302
303 /* Set sensor value to dbus interface */
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700304 setSensorValue(val);
Vijay Khemka32a71562020-09-10 15:29:18 -0700305
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700306 if (DEBUG)
307 std::cout << "Sensor value is " << val << "\n";
Vijay Khemka32a71562020-09-10 15:29:18 -0700308
309 /* Check sensor threshold and log required message */
310 checkSensorThreshold(val);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700311}
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700312
313/** @brief Parsing Virtual Sensor config JSON file */
314Json VirtualSensors::parseConfigFile(const std::string configFile)
315{
316 std::ifstream jsonFile(configFile);
317 if (!jsonFile.is_open())
318 {
319 log<level::ERR>("config JSON file not found",
320 entry("FILENAME = %s", configFile.c_str()));
321 throw std::exception{};
322 }
323
324 auto data = Json::parse(jsonFile, nullptr, false);
325 if (data.is_discarded())
326 {
327 log<level::ERR>("config readings JSON parser failure",
328 entry("FILENAME = %s", configFile.c_str()));
329 throw std::exception{};
330 }
331
332 return data;
333}
334
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700335std::map<std::string, ValueIface::Unit> unitMap = {
336 {"temperature", ValueIface::Unit::DegreesC},
337 {"fan_tach", ValueIface::Unit::RPMS},
338 {"voltage", ValueIface::Unit::Volts},
339 {"altitude", ValueIface::Unit::Meters},
340 {"current", ValueIface::Unit::Amperes},
341 {"power", ValueIface::Unit::Watts},
342 {"energy", ValueIface::Unit::Joules},
343 {"utilization", ValueIface::Unit::Percent}};
344
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700345void VirtualSensors::createVirtualSensors()
346{
347 static const Json empty{};
348
349 auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE);
350 // print values
351 if (DEBUG)
352 std::cout << "Config json data:\n" << data << "\n\n";
353
354 /* Get virtual sensors config data */
355 for (const auto& j : data)
356 {
357 auto desc = j.value("Desc", empty);
358 if (!desc.empty())
359 {
360 std::string sensorType = desc.value("SensorType", "");
361 std::string name = desc.value("Name", "");
362
363 if (!name.empty() && !sensorType.empty())
364 {
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700365 if (unitMap.find(sensorType) == unitMap.end())
366 {
367 log<level::ERR>("Sensor type is not supported",
368 entry("TYPE = %s", sensorType.c_str()));
369 }
370 else
371 {
372 std::string objPath(sensorDbusPath);
373 objPath += sensorType + "/" + name;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700374
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700375 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
376 bus, objPath.c_str(), j, name);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700377
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700378 log<level::INFO>("Added a new virtual sensor",
379 entry("NAME = %s", name.c_str()));
380 virtualSensorPtr->updateVirtualSensor();
381
382 /* Initialize unit value for virtual sensor */
383 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
384
385 virtualSensorsMap.emplace(std::move(name),
386 std::move(virtualSensorPtr));
387 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700388 }
389 else
390 {
391 log<level::ERR>("Sensor type or name not found in config file");
392 }
393 }
394 else
395 {
396 log<level::ERR>(
397 "Descriptor for new virtual sensor not found in config file");
398 }
399 }
400}
401
402} // namespace virtualSensor
403} // namespace phosphor
404
405/**
406 * @brief Main
407 */
408int main()
409{
410
411 // Get a default event loop
412 auto event = sdeventplus::Event::get_default();
413
414 // Get a handle to system dbus
415 auto bus = sdbusplus::bus::new_default();
416
417 // Create an virtual sensors object
418 phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
419
420 // Request service bus name
421 bus.request_name(busName);
422
423 // Attach the bus to sd_event to service user requests
424 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
425 event.loop();
426
427 return 0;
428}