blob: 63baea5677e13143aecab9b51d9ec799bd4f363d [file] [log] [blame]
Vijay Khemkaabcc94f2020-08-11 15:27:44 -07001#include "virtualSensor.hpp"
2
3#include "config.hpp"
4
5#include <phosphor-logging/log.hpp>
6#include <sdeventplus/event.hpp>
7
8#include <fstream>
9#include <iostream>
10
11static constexpr bool DEBUG = false;
12static constexpr auto busName = "xyz.openbmc_project.VirtualSensor";
13static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/";
14static constexpr uint8_t defaultHighThreshold = 100;
15static constexpr uint8_t defaultLowThreshold = 0;
16
17using namespace phosphor::logging;
18
Vijay Khemka51f898e2020-09-09 22:24:18 -070019int handleDbusSignal(sd_bus_message* msg, void* usrData, sd_bus_error*)
20{
21 if (usrData == nullptr)
22 {
23 throw std::runtime_error("Invalid match");
24 }
25
26 auto sdbpMsg = sdbusplus::message::message(msg);
27 std::string msgIfce;
28 std::map<std::string, std::variant<int64_t, double, bool>> msgData;
29
30 sdbpMsg.read(msgIfce, msgData);
31
32 if (msgData.find("Value") != msgData.end())
33 {
34 using namespace phosphor::virtualSensor;
35 VirtualSensor* obj = static_cast<VirtualSensor*>(usrData);
36 // TODO(openbmc/phosphor-virtual-sensor#1): updateVirtualSensor should
37 // be changed to take the information we got from the signal, to avoid
38 // having to do numerous dbus queries.
39 obj->updateVirtualSensor();
40 }
41 return 0;
42}
43
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070044namespace phosphor
45{
46namespace virtualSensor
47{
48
49void printParams(const VirtualSensor::ParamMap& paramMap)
50{
51 for (const auto& p : paramMap)
52 {
53 const auto& p1 = p.first;
54 const auto& p2 = p.second;
55 auto val = p2->getParamValue();
56 std::cout << p1 << " = " << val << "\n";
57 }
58}
59
60double SensorParam::getParamValue()
61{
62 switch (paramType)
63 {
64 case constParam:
65 return value;
66 break;
Vijay Khemka7452a862020-08-11 16:01:23 -070067 case dbusParam:
68 return dbusSensor->getSensorValue();
69 break;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070070 default:
71 throw std::invalid_argument("param type not supported");
72 }
73}
74
75void VirtualSensor::initVirtualSensor(const Json& sensorConfig)
76{
77
78 static const Json empty{};
79
80 /* Get threshold values if defined in config */
81 auto threshold = sensorConfig.value("Threshold", empty);
82 if (!threshold.empty())
83 {
Vijay Khemkac62a5542020-09-10 14:45:49 -070084 Threshold sensorThreshold;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070085 sensorThreshold.criticalHigh =
86 threshold.value("CriticalHigh", defaultHighThreshold);
87 sensorThreshold.criticalLow =
88 threshold.value("CriticalLow", defaultLowThreshold);
89 sensorThreshold.warningHigh =
90 threshold.value("WarningHigh", defaultHighThreshold);
91 sensorThreshold.warningLow =
92 threshold.value("WarningLow", defaultLowThreshold);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070093
Vijay Khemkac62a5542020-09-10 14:45:49 -070094 /* Set threshold value to dbus */
95 setSensorThreshold(sensorThreshold);
96 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070097
98 /* Get expression string */
99 exprStr = sensorConfig.value("Expression", "");
100
101 /* Get all the parameter listed in configuration */
102 auto params = sensorConfig.value("Params", empty);
103
104 /* Check for constant parameter */
105 const auto& consParams = params.value("ConstParam", empty);
106 if (!consParams.empty())
107 {
108 for (auto& j : consParams)
109 {
110 if (j.find("ParamName") != j.end())
111 {
112 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700113 std::string name = j["ParamName"];
114 symbols.create_variable(name);
115 paramMap.emplace(std::move(name), std::move(paramPtr));
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700116 }
117 else
118 {
119 /* Invalid configuration */
120 throw std::invalid_argument(
121 "ParamName not found in configuration");
122 }
123 }
124 }
125
Vijay Khemka7452a862020-08-11 16:01:23 -0700126 /* Check for dbus parameter */
127 auto dbusParams = params.value("DbusParam", empty);
128 if (!dbusParams.empty())
129 {
130 for (auto& j : dbusParams)
131 {
132 /* Get parameter dbus sensor descriptor */
133 auto desc = j.value("Desc", empty);
134 if ((!desc.empty()) && (j.find("ParamName") != j.end()))
135 {
136 std::string sensorType = desc.value("SensorType", "");
137 std::string name = desc.value("Name", "");
138
139 if (!sensorType.empty() && !name.empty())
140 {
141 std::string objPath(sensorDbusPath);
142 objPath += sensorType + "/" + name;
143
Vijay Khemka51f898e2020-09-09 22:24:18 -0700144 auto paramPtr =
145 std::make_unique<SensorParam>(bus, objPath, this);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700146 std::string name = j["ParamName"];
147 symbols.create_variable(name);
148 paramMap.emplace(std::move(name), std::move(paramPtr));
Vijay Khemka7452a862020-08-11 16:01:23 -0700149 }
150 }
151 }
152 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700153
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700154 symbols.add_constants();
155 expression.register_symbol_table(symbols);
156
157 /* parser from exprtk */
158 exprtk::parser<double> parser{};
159 parser.compile(exprStr, expression);
160
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700161 /* Print all parameters for debug purpose only */
162 if (DEBUG)
163 printParams(paramMap);
164}
165
166void VirtualSensor::setSensorValue(double value)
167{
168 ValueIface::value(value);
169}
170
Vijay Khemkac62a5542020-09-10 14:45:49 -0700171void VirtualSensor::setSensorThreshold(Threshold& sensorThreshold)
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700172{
173 CriticalInterface::criticalHigh(sensorThreshold.criticalHigh);
174 CriticalInterface::criticalLow(sensorThreshold.criticalLow);
175 WarningInterface::warningHigh(sensorThreshold.warningHigh);
176 WarningInterface::warningLow(sensorThreshold.warningLow);
177}
178
Vijay Khemka32a71562020-09-10 15:29:18 -0700179void VirtualSensor::checkSensorThreshold(const double value)
180{
181 auto criticalHigh = CriticalInterface::criticalHigh();
182 auto criticalLow = CriticalInterface::criticalLow();
183 auto warningHigh = WarningInterface::warningHigh();
184 auto warningLow = WarningInterface::warningLow();
185
186 if (value > criticalHigh)
187 {
188 if (!CriticalInterface::criticalAlarmHigh())
189 {
190 CriticalInterface::criticalAlarmHigh(true);
191 log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
192 "critical high threshold",
193 entry("NAME = %s", name.c_str()));
194 }
195 return;
196 }
197
198 if (CriticalInterface::criticalAlarmHigh())
199 {
200 CriticalInterface::criticalAlarmHigh(false);
201 log<level::INFO>("DEASSERT: Virtual Sensor is under "
202 "critical high threshold",
203 entry("NAME = %s", name.c_str()));
204 }
205
206 if (value > warningHigh)
207 {
208 if (!WarningInterface::warningAlarmHigh())
209 {
210 WarningInterface::warningAlarmHigh(true);
211 log<level::ERR>("ASSERT: Virtual Sensor has exceeded "
212 "warning high threshold",
213 entry("NAME = %s", name.c_str()));
214 }
215 return;
216 }
217
218 if (WarningInterface::warningAlarmHigh())
219 {
220 WarningInterface::warningAlarmHigh(false);
221 log<level::INFO>("DEASSERT: Virtual Sensor is under "
222 "warning high threshold",
223 entry("NAME = %s", name.c_str()));
224 }
225
226 if (value < criticalLow)
227 {
228 if (!CriticalInterface::criticalAlarmLow())
229 {
230 CriticalInterface::criticalAlarmLow(true);
231 log<level::ERR>("ASSERT: Virtual Sensor is under "
232 "critical low threshold",
233 entry("NAME = %s", name.c_str()));
234 }
235 return;
236 }
237
238 if (CriticalInterface::criticalAlarmLow())
239 {
240 CriticalInterface::criticalAlarmLow(false);
241 log<level::ERR>("DEASSERT: Virtual Sensor is above "
242 "critical low threshold",
243 entry("NAME = %s", name.c_str()));
244 }
245
246 if (value < warningLow)
247 {
248 if (!WarningInterface::warningAlarmLow())
249 {
250 WarningInterface::warningAlarmLow(true);
251 log<level::ERR>("ASSERT: Virtual Sensor is under "
252 "warning low threshold",
253 entry("NAME = %s", name.c_str()));
254 }
255 return;
256 }
257
258 if (WarningInterface::warningAlarmLow())
259 {
260 WarningInterface::warningAlarmLow(false);
261 log<level::ERR>("DEASSERT: Virtual Sensor is above "
262 "warning low threshold",
263 entry("NAME = %s", name.c_str()));
264 }
265}
266
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700267void VirtualSensor::updateVirtualSensor()
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700268{
269 for (auto& param : paramMap)
270 {
271 auto& name = param.first;
272 auto& data = param.second;
273 if (auto var = symbols.get_variable(name))
274 {
275 var->ref() = data->getParamValue();
276 }
277 else
278 {
279 /* Invalid parameter */
280 throw std::invalid_argument("ParamName not found in symbols");
281 }
282 }
283 double val = expression.value();
Vijay Khemka32a71562020-09-10 15:29:18 -0700284
285 /* Set sensor value to dbus interface */
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700286 setSensorValue(val);
Vijay Khemka32a71562020-09-10 15:29:18 -0700287
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700288 if (DEBUG)
289 std::cout << "Sensor value is " << val << "\n";
Vijay Khemka32a71562020-09-10 15:29:18 -0700290
291 /* Check sensor threshold and log required message */
292 checkSensorThreshold(val);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700293}
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700294
295/** @brief Parsing Virtual Sensor config JSON file */
296Json VirtualSensors::parseConfigFile(const std::string configFile)
297{
298 std::ifstream jsonFile(configFile);
299 if (!jsonFile.is_open())
300 {
301 log<level::ERR>("config JSON file not found",
302 entry("FILENAME = %s", configFile.c_str()));
303 throw std::exception{};
304 }
305
306 auto data = Json::parse(jsonFile, nullptr, false);
307 if (data.is_discarded())
308 {
309 log<level::ERR>("config readings JSON parser failure",
310 entry("FILENAME = %s", configFile.c_str()));
311 throw std::exception{};
312 }
313
314 return data;
315}
316
317void VirtualSensors::createVirtualSensors()
318{
319 static const Json empty{};
320
321 auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE);
322 // print values
323 if (DEBUG)
324 std::cout << "Config json data:\n" << data << "\n\n";
325
326 /* Get virtual sensors config data */
327 for (const auto& j : data)
328 {
329 auto desc = j.value("Desc", empty);
330 if (!desc.empty())
331 {
332 std::string sensorType = desc.value("SensorType", "");
333 std::string name = desc.value("Name", "");
334
335 if (!name.empty() && !sensorType.empty())
336 {
337 std::string objPath(sensorDbusPath);
338 objPath += sensorType + "/" + name;
339
Vijay Khemka32a71562020-09-10 15:29:18 -0700340 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
341 bus, objPath.c_str(), j, name);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700342
343 log<level::INFO>("Added a new virtual sensor",
344 entry("NAME = %s", name.c_str()));
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700345 virtualSensorPtr->updateVirtualSensor();
346 virtualSensorsMap.emplace(std::move(name),
347 std::move(virtualSensorPtr));
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700348 }
349 else
350 {
351 log<level::ERR>("Sensor type or name not found in config file");
352 }
353 }
354 else
355 {
356 log<level::ERR>(
357 "Descriptor for new virtual sensor not found in config file");
358 }
359 }
360}
361
362} // namespace virtualSensor
363} // namespace phosphor
364
365/**
366 * @brief Main
367 */
368int main()
369{
370
371 // Get a default event loop
372 auto event = sdeventplus::Event::get_default();
373
374 // Get a handle to system dbus
375 auto bus = sdbusplus::bus::new_default();
376
377 // Create an virtual sensors object
378 phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
379
380 // Request service bus name
381 bus.request_name(busName);
382
383 // Attach the bus to sd_event to service user requests
384 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
385 event.loop();
386
387 return 0;
388}