blob: 29e71eb5798f1df61efd6e14ef27784b2ddb58ae [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/";
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070016
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
Matt Spinlerce675222021-01-14 16:38:09 -060075void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
76 const std::string& objPath)
Vijay Khemkaabcc94f2020-08-11 15:27:44 -070077{
78
79 static const Json empty{};
80
81 /* Get threshold values if defined in config */
82 auto threshold = sensorConfig.value("Threshold", empty);
83 if (!threshold.empty())
84 {
Matt Spinlerf15189e2021-01-15 10:13:28 -060085 // Only create the threshold interfaces if
Matt Spinlerce675222021-01-14 16:38:09 -060086 // at least one of their values is present.
Matt Spinlerf15189e2021-01-15 10:13:28 -060087
88 if (threshold.contains("CriticalHigh") ||
89 threshold.contains("CriticalLow"))
90 {
Patrick Williamsfdb826d2021-01-20 14:37:53 -060091 criticalIface = std::make_unique<Threshold<CriticalObject>>(
92 bus, objPath.c_str());
Matt Spinlerf15189e2021-01-15 10:13:28 -060093
94 criticalIface->criticalHigh(threshold.value(
95 "CriticalHigh", std::numeric_limits<double>::quiet_NaN()));
96 criticalIface->criticalLow(threshold.value(
97 "CriticalLow", std::numeric_limits<double>::quiet_NaN()));
98 }
99
100 if (threshold.contains("WarningHigh") ||
101 threshold.contains("WarningLow"))
102 {
Patrick Williamsfdb826d2021-01-20 14:37:53 -0600103 warningIface = std::make_unique<Threshold<WarningObject>>(
104 bus, objPath.c_str());
Matt Spinlerf15189e2021-01-15 10:13:28 -0600105
106 warningIface->warningHigh(threshold.value(
107 "WarningHigh", std::numeric_limits<double>::quiet_NaN()));
108 warningIface->warningLow(threshold.value(
109 "WarningLow", std::numeric_limits<double>::quiet_NaN()));
110 }
111
Matt Spinlerce675222021-01-14 16:38:09 -0600112 if (threshold.contains("HardShutdownHigh") ||
113 threshold.contains("HardShutdownLow"))
114 {
Patrick Williamsfdb826d2021-01-20 14:37:53 -0600115 hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>(
116 bus, objPath.c_str());
Matt Spinlerce675222021-01-14 16:38:09 -0600117
118 hardShutdownIface->hardShutdownHigh(threshold.value(
119 "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
120 hardShutdownIface->hardShutdownLow(threshold.value(
121 "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
122 }
123
124 if (threshold.contains("SoftShutdownHigh") ||
125 threshold.contains("SoftShutdownLow"))
126 {
Patrick Williamsfdb826d2021-01-20 14:37:53 -0600127 softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>(
128 bus, objPath.c_str());
Matt Spinlerce675222021-01-14 16:38:09 -0600129
130 softShutdownIface->softShutdownHigh(threshold.value(
131 "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
132 softShutdownIface->softShutdownLow(threshold.value(
133 "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
134 }
Matt Spinlerb306b032021-02-01 10:05:46 -0600135
136 if (threshold.contains("PerformanceLossHigh") ||
137 threshold.contains("PerformanceLossLow"))
138 {
139 perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>(
140 bus, objPath.c_str());
141
142 perfLossIface->performanceLossHigh(
143 threshold.value("PerformanceLossHigh",
144 std::numeric_limits<double>::quiet_NaN()));
145 perfLossIface->performanceLossLow(
146 threshold.value("PerformanceLossLow",
147 std::numeric_limits<double>::quiet_NaN()));
148 }
Vijay Khemkac62a5542020-09-10 14:45:49 -0700149 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700150
Harvey Wuf6443742021-04-09 16:47:36 +0800151 /* Get MaxValue, MinValue setting if defined in config */
152 auto confDesc = sensorConfig.value("Desc", empty);
153 if (auto maxConf = confDesc.find("MaxValue");
154 maxConf != confDesc.end() && maxConf->is_number())
155 {
156 ValueIface::maxValue(maxConf->get<double>());
157 }
158 if (auto minConf = confDesc.find("MinValue");
159 minConf != confDesc.end() && minConf->is_number())
160 {
161 ValueIface::minValue(minConf->get<double>());
162 }
163
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700164 /* Get expression string */
165 exprStr = sensorConfig.value("Expression", "");
166
167 /* Get all the parameter listed in configuration */
168 auto params = sensorConfig.value("Params", empty);
169
170 /* Check for constant parameter */
171 const auto& consParams = params.value("ConstParam", empty);
172 if (!consParams.empty())
173 {
174 for (auto& j : consParams)
175 {
176 if (j.find("ParamName") != j.end())
177 {
178 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700179 std::string name = j["ParamName"];
180 symbols.create_variable(name);
181 paramMap.emplace(std::move(name), std::move(paramPtr));
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700182 }
183 else
184 {
185 /* Invalid configuration */
186 throw std::invalid_argument(
187 "ParamName not found in configuration");
188 }
189 }
190 }
191
Vijay Khemka7452a862020-08-11 16:01:23 -0700192 /* Check for dbus parameter */
193 auto dbusParams = params.value("DbusParam", empty);
194 if (!dbusParams.empty())
195 {
196 for (auto& j : dbusParams)
197 {
198 /* Get parameter dbus sensor descriptor */
199 auto desc = j.value("Desc", empty);
200 if ((!desc.empty()) && (j.find("ParamName") != j.end()))
201 {
202 std::string sensorType = desc.value("SensorType", "");
203 std::string name = desc.value("Name", "");
204
205 if (!sensorType.empty() && !name.empty())
206 {
207 std::string objPath(sensorDbusPath);
208 objPath += sensorType + "/" + name;
209
Vijay Khemka51f898e2020-09-09 22:24:18 -0700210 auto paramPtr =
211 std::make_unique<SensorParam>(bus, objPath, this);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700212 std::string name = j["ParamName"];
213 symbols.create_variable(name);
214 paramMap.emplace(std::move(name), std::move(paramPtr));
Vijay Khemka7452a862020-08-11 16:01:23 -0700215 }
216 }
217 }
218 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700219
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700220 symbols.add_constants();
Matt Spinler9f1ef4f2020-11-09 15:59:11 -0600221 symbols.add_package(vecopsPackage);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700222 expression.register_symbol_table(symbols);
223
224 /* parser from exprtk */
225 exprtk::parser<double> parser{};
Matt Spinlerddc6dcd2020-11-09 11:16:31 -0600226 if (!parser.compile(exprStr, expression))
227 {
228 log<level::ERR>("Expression compilation failed");
229
230 for (std::size_t i = 0; i < parser.error_count(); ++i)
231 {
232 auto error = parser.get_error(i);
233
234 log<level::ERR>(
235 fmt::format(
236 "Position: {} Type: {} Message: {}", error.token.position,
237 exprtk::parser_error::to_str(error.mode), error.diagnostic)
238 .c_str());
239 }
240 throw std::runtime_error("Expression compilation failed");
241 }
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700242
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700243 /* Print all parameters for debug purpose only */
244 if (DEBUG)
245 printParams(paramMap);
246}
247
248void VirtualSensor::setSensorValue(double value)
249{
250 ValueIface::value(value);
251}
252
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700253void VirtualSensor::updateVirtualSensor()
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700254{
255 for (auto& param : paramMap)
256 {
257 auto& name = param.first;
258 auto& data = param.second;
259 if (auto var = symbols.get_variable(name))
260 {
261 var->ref() = data->getParamValue();
262 }
263 else
264 {
265 /* Invalid parameter */
266 throw std::invalid_argument("ParamName not found in symbols");
267 }
268 }
269 double val = expression.value();
Vijay Khemka32a71562020-09-10 15:29:18 -0700270
271 /* Set sensor value to dbus interface */
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700272 setSensorValue(val);
Vijay Khemka32a71562020-09-10 15:29:18 -0700273
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700274 if (DEBUG)
275 std::cout << "Sensor value is " << val << "\n";
Vijay Khemka32a71562020-09-10 15:29:18 -0700276
Matt Spinler8f5e6112021-01-15 10:44:32 -0600277 /* Check sensor thresholds and log required message */
Matt Spinlerb306b032021-02-01 10:05:46 -0600278 checkThresholds(val, perfLossIface);
Patrick Williamsfdb826d2021-01-20 14:37:53 -0600279 checkThresholds(val, warningIface);
280 checkThresholds(val, criticalIface);
281 checkThresholds(val, softShutdownIface);
282 checkThresholds(val, hardShutdownIface);
Vijay Khemka3ed9a512020-08-21 16:13:05 -0700283}
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700284
285/** @brief Parsing Virtual Sensor config JSON file */
286Json VirtualSensors::parseConfigFile(const std::string configFile)
287{
288 std::ifstream jsonFile(configFile);
289 if (!jsonFile.is_open())
290 {
291 log<level::ERR>("config JSON file not found",
292 entry("FILENAME = %s", configFile.c_str()));
293 throw std::exception{};
294 }
295
296 auto data = Json::parse(jsonFile, nullptr, false);
297 if (data.is_discarded())
298 {
299 log<level::ERR>("config readings JSON parser failure",
300 entry("FILENAME = %s", configFile.c_str()));
301 throw std::exception{};
302 }
303
304 return data;
305}
306
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700307std::map<std::string, ValueIface::Unit> unitMap = {
308 {"temperature", ValueIface::Unit::DegreesC},
309 {"fan_tach", ValueIface::Unit::RPMS},
310 {"voltage", ValueIface::Unit::Volts},
311 {"altitude", ValueIface::Unit::Meters},
312 {"current", ValueIface::Unit::Amperes},
313 {"power", ValueIface::Unit::Watts},
314 {"energy", ValueIface::Unit::Joules},
Kumar Thangavel2b56ddb2021-01-13 20:16:11 +0530315 {"utilization", ValueIface::Unit::Percent},
316 {"airflow", ValueIface::Unit::CFM}};
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700317
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700318void VirtualSensors::createVirtualSensors()
319{
320 static const Json empty{};
321
322 auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE);
323 // print values
324 if (DEBUG)
325 std::cout << "Config json data:\n" << data << "\n\n";
326
327 /* Get virtual sensors config data */
328 for (const auto& j : data)
329 {
330 auto desc = j.value("Desc", empty);
331 if (!desc.empty())
332 {
333 std::string sensorType = desc.value("SensorType", "");
334 std::string name = desc.value("Name", "");
335
336 if (!name.empty() && !sensorType.empty())
337 {
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700338 if (unitMap.find(sensorType) == unitMap.end())
339 {
340 log<level::ERR>("Sensor type is not supported",
341 entry("TYPE = %s", sensorType.c_str()));
342 }
343 else
344 {
345 std::string objPath(sensorDbusPath);
346 objPath += sensorType + "/" + name;
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700347
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700348 auto virtualSensorPtr = std::make_unique<VirtualSensor>(
349 bus, objPath.c_str(), j, name);
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700350
Vijay Khemkae0d371e2020-09-21 18:35:52 -0700351 log<level::INFO>("Added a new virtual sensor",
352 entry("NAME = %s", name.c_str()));
353 virtualSensorPtr->updateVirtualSensor();
354
355 /* Initialize unit value for virtual sensor */
356 virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
357
358 virtualSensorsMap.emplace(std::move(name),
359 std::move(virtualSensorPtr));
360 }
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700361 }
362 else
363 {
364 log<level::ERR>("Sensor type or name not found in config file");
365 }
366 }
367 else
368 {
369 log<level::ERR>(
370 "Descriptor for new virtual sensor not found in config file");
371 }
372 }
373}
374
375} // namespace virtualSensor
376} // namespace phosphor
377
378/**
379 * @brief Main
380 */
381int main()
382{
383
384 // Get a default event loop
385 auto event = sdeventplus::Event::get_default();
386
387 // Get a handle to system dbus
388 auto bus = sdbusplus::bus::new_default();
389
Matt Spinler6c19e7d2021-01-12 16:26:45 -0600390 // Add the ObjectManager interface
391 sdbusplus::server::manager::manager objManager(bus, "/");
392
Vijay Khemkaabcc94f2020-08-11 15:27:44 -0700393 // Create an virtual sensors object
394 phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
395
396 // Request service bus name
397 bus.request_name(busName);
398
399 // Attach the bus to sd_event to service user requests
400 bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
401 event.loop();
402
403 return 0;
404}