1abcc94faSVijay Khemka #include "virtualSensor.hpp"
2abcc94faSVijay Khemka 
3abcc94faSVijay Khemka #include "config.hpp"
4abcc94faSVijay Khemka 
5ddc6dcd6SMatt Spinler #include <fmt/format.h>
6ddc6dcd6SMatt Spinler 
7abcc94faSVijay Khemka #include <phosphor-logging/log.hpp>
8abcc94faSVijay Khemka #include <sdeventplus/event.hpp>
9abcc94faSVijay Khemka 
10abcc94faSVijay Khemka #include <fstream>
11abcc94faSVijay Khemka #include <iostream>
12abcc94faSVijay Khemka 
13abcc94faSVijay Khemka static constexpr bool DEBUG = false;
14abcc94faSVijay Khemka static constexpr auto busName = "xyz.openbmc_project.VirtualSensor";
15abcc94faSVijay Khemka static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/";
16abcc94faSVijay Khemka 
17abcc94faSVijay Khemka using namespace phosphor::logging;
18abcc94faSVijay Khemka 
1951f898e2SVijay Khemka int handleDbusSignal(sd_bus_message* msg, void* usrData, sd_bus_error*)
2051f898e2SVijay Khemka {
2151f898e2SVijay Khemka     if (usrData == nullptr)
2251f898e2SVijay Khemka     {
2351f898e2SVijay Khemka         throw std::runtime_error("Invalid match");
2451f898e2SVijay Khemka     }
2551f898e2SVijay Khemka 
2651f898e2SVijay Khemka     auto sdbpMsg = sdbusplus::message::message(msg);
2751f898e2SVijay Khemka     std::string msgIfce;
2851f898e2SVijay Khemka     std::map<std::string, std::variant<int64_t, double, bool>> msgData;
2951f898e2SVijay Khemka 
3051f898e2SVijay Khemka     sdbpMsg.read(msgIfce, msgData);
3151f898e2SVijay Khemka 
3251f898e2SVijay Khemka     if (msgData.find("Value") != msgData.end())
3351f898e2SVijay Khemka     {
3451f898e2SVijay Khemka         using namespace phosphor::virtualSensor;
3551f898e2SVijay Khemka         VirtualSensor* obj = static_cast<VirtualSensor*>(usrData);
3651f898e2SVijay Khemka         // TODO(openbmc/phosphor-virtual-sensor#1): updateVirtualSensor should
3751f898e2SVijay Khemka         // be changed to take the information we got from the signal, to avoid
3851f898e2SVijay Khemka         // having to do numerous dbus queries.
3951f898e2SVijay Khemka         obj->updateVirtualSensor();
4051f898e2SVijay Khemka     }
4151f898e2SVijay Khemka     return 0;
4251f898e2SVijay Khemka }
4351f898e2SVijay Khemka 
44abcc94faSVijay Khemka namespace phosphor
45abcc94faSVijay Khemka {
46abcc94faSVijay Khemka namespace virtualSensor
47abcc94faSVijay Khemka {
48abcc94faSVijay Khemka 
49abcc94faSVijay Khemka void printParams(const VirtualSensor::ParamMap& paramMap)
50abcc94faSVijay Khemka {
51abcc94faSVijay Khemka     for (const auto& p : paramMap)
52abcc94faSVijay Khemka     {
53abcc94faSVijay Khemka         const auto& p1 = p.first;
54abcc94faSVijay Khemka         const auto& p2 = p.second;
55abcc94faSVijay Khemka         auto val = p2->getParamValue();
56abcc94faSVijay Khemka         std::cout << p1 << " = " << val << "\n";
57abcc94faSVijay Khemka     }
58abcc94faSVijay Khemka }
59abcc94faSVijay Khemka 
60abcc94faSVijay Khemka double SensorParam::getParamValue()
61abcc94faSVijay Khemka {
62abcc94faSVijay Khemka     switch (paramType)
63abcc94faSVijay Khemka     {
64abcc94faSVijay Khemka         case constParam:
65abcc94faSVijay Khemka             return value;
66abcc94faSVijay Khemka             break;
677452a867SVijay Khemka         case dbusParam:
687452a867SVijay Khemka             return dbusSensor->getSensorValue();
697452a867SVijay Khemka             break;
70abcc94faSVijay Khemka         default:
71abcc94faSVijay Khemka             throw std::invalid_argument("param type not supported");
72abcc94faSVijay Khemka     }
73abcc94faSVijay Khemka }
74abcc94faSVijay Khemka 
75ce675228SMatt Spinler void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
76ce675228SMatt Spinler                                       const std::string& objPath)
77abcc94faSVijay Khemka {
78abcc94faSVijay Khemka 
79abcc94faSVijay Khemka     static const Json empty{};
80abcc94faSVijay Khemka 
81abcc94faSVijay Khemka     /* Get threshold values if defined in config */
82abcc94faSVijay Khemka     auto threshold = sensorConfig.value("Threshold", empty);
83abcc94faSVijay Khemka     if (!threshold.empty())
84abcc94faSVijay Khemka     {
85f15189e3SMatt Spinler         // Only create the threshold interfaces if
86ce675228SMatt Spinler         // at least one of their values is present.
87f15189e3SMatt Spinler 
88f15189e3SMatt Spinler         if (threshold.contains("CriticalHigh") ||
89f15189e3SMatt Spinler             threshold.contains("CriticalLow"))
90f15189e3SMatt Spinler         {
91fdb826d5SPatrick Williams             criticalIface = std::make_unique<Threshold<CriticalObject>>(
92fdb826d5SPatrick Williams                 bus, objPath.c_str());
93f15189e3SMatt Spinler 
94f15189e3SMatt Spinler             criticalIface->criticalHigh(threshold.value(
95f15189e3SMatt Spinler                 "CriticalHigh", std::numeric_limits<double>::quiet_NaN()));
96f15189e3SMatt Spinler             criticalIface->criticalLow(threshold.value(
97f15189e3SMatt Spinler                 "CriticalLow", std::numeric_limits<double>::quiet_NaN()));
98f15189e3SMatt Spinler         }
99f15189e3SMatt Spinler 
100f15189e3SMatt Spinler         if (threshold.contains("WarningHigh") ||
101f15189e3SMatt Spinler             threshold.contains("WarningLow"))
102f15189e3SMatt Spinler         {
103fdb826d5SPatrick Williams             warningIface = std::make_unique<Threshold<WarningObject>>(
104fdb826d5SPatrick Williams                 bus, objPath.c_str());
105f15189e3SMatt Spinler 
106f15189e3SMatt Spinler             warningIface->warningHigh(threshold.value(
107f15189e3SMatt Spinler                 "WarningHigh", std::numeric_limits<double>::quiet_NaN()));
108f15189e3SMatt Spinler             warningIface->warningLow(threshold.value(
109f15189e3SMatt Spinler                 "WarningLow", std::numeric_limits<double>::quiet_NaN()));
110f15189e3SMatt Spinler         }
111f15189e3SMatt Spinler 
112ce675228SMatt Spinler         if (threshold.contains("HardShutdownHigh") ||
113ce675228SMatt Spinler             threshold.contains("HardShutdownLow"))
114ce675228SMatt Spinler         {
115fdb826d5SPatrick Williams             hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>(
116fdb826d5SPatrick Williams                 bus, objPath.c_str());
117ce675228SMatt Spinler 
118ce675228SMatt Spinler             hardShutdownIface->hardShutdownHigh(threshold.value(
119ce675228SMatt Spinler                 "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
120ce675228SMatt Spinler             hardShutdownIface->hardShutdownLow(threshold.value(
121ce675228SMatt Spinler                 "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
122ce675228SMatt Spinler         }
123ce675228SMatt Spinler 
124ce675228SMatt Spinler         if (threshold.contains("SoftShutdownHigh") ||
125ce675228SMatt Spinler             threshold.contains("SoftShutdownLow"))
126ce675228SMatt Spinler         {
127fdb826d5SPatrick Williams             softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>(
128fdb826d5SPatrick Williams                 bus, objPath.c_str());
129ce675228SMatt Spinler 
130ce675228SMatt Spinler             softShutdownIface->softShutdownHigh(threshold.value(
131ce675228SMatt Spinler                 "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
132ce675228SMatt Spinler             softShutdownIface->softShutdownLow(threshold.value(
133ce675228SMatt Spinler                 "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
134ce675228SMatt Spinler         }
135*b306b03dSMatt Spinler 
136*b306b03dSMatt Spinler         if (threshold.contains("PerformanceLossHigh") ||
137*b306b03dSMatt Spinler             threshold.contains("PerformanceLossLow"))
138*b306b03dSMatt Spinler         {
139*b306b03dSMatt Spinler             perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>(
140*b306b03dSMatt Spinler                 bus, objPath.c_str());
141*b306b03dSMatt Spinler 
142*b306b03dSMatt Spinler             perfLossIface->performanceLossHigh(
143*b306b03dSMatt Spinler                 threshold.value("PerformanceLossHigh",
144*b306b03dSMatt Spinler                                 std::numeric_limits<double>::quiet_NaN()));
145*b306b03dSMatt Spinler             perfLossIface->performanceLossLow(
146*b306b03dSMatt Spinler                 threshold.value("PerformanceLossLow",
147*b306b03dSMatt Spinler                                 std::numeric_limits<double>::quiet_NaN()));
148*b306b03dSMatt Spinler         }
149c62a5548SVijay Khemka     }
150abcc94faSVijay Khemka 
151abcc94faSVijay Khemka     /* Get expression string */
152abcc94faSVijay Khemka     exprStr = sensorConfig.value("Expression", "");
153abcc94faSVijay Khemka 
154abcc94faSVijay Khemka     /* Get all the parameter listed in configuration */
155abcc94faSVijay Khemka     auto params = sensorConfig.value("Params", empty);
156abcc94faSVijay Khemka 
157abcc94faSVijay Khemka     /* Check for constant parameter */
158abcc94faSVijay Khemka     const auto& consParams = params.value("ConstParam", empty);
159abcc94faSVijay Khemka     if (!consParams.empty())
160abcc94faSVijay Khemka     {
161abcc94faSVijay Khemka         for (auto& j : consParams)
162abcc94faSVijay Khemka         {
163abcc94faSVijay Khemka             if (j.find("ParamName") != j.end())
164abcc94faSVijay Khemka             {
165abcc94faSVijay Khemka                 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
1663ed9a516SVijay Khemka                 std::string name = j["ParamName"];
1673ed9a516SVijay Khemka                 symbols.create_variable(name);
1683ed9a516SVijay Khemka                 paramMap.emplace(std::move(name), std::move(paramPtr));
169abcc94faSVijay Khemka             }
170abcc94faSVijay Khemka             else
171abcc94faSVijay Khemka             {
172abcc94faSVijay Khemka                 /* Invalid configuration */
173abcc94faSVijay Khemka                 throw std::invalid_argument(
174abcc94faSVijay Khemka                     "ParamName not found in configuration");
175abcc94faSVijay Khemka             }
176abcc94faSVijay Khemka         }
177abcc94faSVijay Khemka     }
178abcc94faSVijay Khemka 
1797452a867SVijay Khemka     /* Check for dbus parameter */
1807452a867SVijay Khemka     auto dbusParams = params.value("DbusParam", empty);
1817452a867SVijay Khemka     if (!dbusParams.empty())
1827452a867SVijay Khemka     {
1837452a867SVijay Khemka         for (auto& j : dbusParams)
1847452a867SVijay Khemka         {
1857452a867SVijay Khemka             /* Get parameter dbus sensor descriptor */
1867452a867SVijay Khemka             auto desc = j.value("Desc", empty);
1877452a867SVijay Khemka             if ((!desc.empty()) && (j.find("ParamName") != j.end()))
1887452a867SVijay Khemka             {
1897452a867SVijay Khemka                 std::string sensorType = desc.value("SensorType", "");
1907452a867SVijay Khemka                 std::string name = desc.value("Name", "");
1917452a867SVijay Khemka 
1927452a867SVijay Khemka                 if (!sensorType.empty() && !name.empty())
1937452a867SVijay Khemka                 {
1947452a867SVijay Khemka                     std::string objPath(sensorDbusPath);
1957452a867SVijay Khemka                     objPath += sensorType + "/" + name;
1967452a867SVijay Khemka 
19751f898e2SVijay Khemka                     auto paramPtr =
19851f898e2SVijay Khemka                         std::make_unique<SensorParam>(bus, objPath, this);
1993ed9a516SVijay Khemka                     std::string name = j["ParamName"];
2003ed9a516SVijay Khemka                     symbols.create_variable(name);
2013ed9a516SVijay Khemka                     paramMap.emplace(std::move(name), std::move(paramPtr));
2027452a867SVijay Khemka                 }
2037452a867SVijay Khemka             }
2047452a867SVijay Khemka         }
2057452a867SVijay Khemka     }
206abcc94faSVijay Khemka 
2073ed9a516SVijay Khemka     symbols.add_constants();
2089f1ef4f5SMatt Spinler     symbols.add_package(vecopsPackage);
2093ed9a516SVijay Khemka     expression.register_symbol_table(symbols);
2103ed9a516SVijay Khemka 
2113ed9a516SVijay Khemka     /* parser from exprtk */
2123ed9a516SVijay Khemka     exprtk::parser<double> parser{};
213ddc6dcd6SMatt Spinler     if (!parser.compile(exprStr, expression))
214ddc6dcd6SMatt Spinler     {
215ddc6dcd6SMatt Spinler         log<level::ERR>("Expression compilation failed");
216ddc6dcd6SMatt Spinler 
217ddc6dcd6SMatt Spinler         for (std::size_t i = 0; i < parser.error_count(); ++i)
218ddc6dcd6SMatt Spinler         {
219ddc6dcd6SMatt Spinler             auto error = parser.get_error(i);
220ddc6dcd6SMatt Spinler 
221ddc6dcd6SMatt Spinler             log<level::ERR>(
222ddc6dcd6SMatt Spinler                 fmt::format(
223ddc6dcd6SMatt Spinler                     "Position: {} Type: {} Message: {}", error.token.position,
224ddc6dcd6SMatt Spinler                     exprtk::parser_error::to_str(error.mode), error.diagnostic)
225ddc6dcd6SMatt Spinler                     .c_str());
226ddc6dcd6SMatt Spinler         }
227ddc6dcd6SMatt Spinler         throw std::runtime_error("Expression compilation failed");
228ddc6dcd6SMatt Spinler     }
2293ed9a516SVijay Khemka 
230abcc94faSVijay Khemka     /* Print all parameters for debug purpose only */
231abcc94faSVijay Khemka     if (DEBUG)
232abcc94faSVijay Khemka         printParams(paramMap);
233abcc94faSVijay Khemka }
234abcc94faSVijay Khemka 
235abcc94faSVijay Khemka void VirtualSensor::setSensorValue(double value)
236abcc94faSVijay Khemka {
237abcc94faSVijay Khemka     ValueIface::value(value);
238abcc94faSVijay Khemka }
239abcc94faSVijay Khemka 
240abcc94faSVijay Khemka void VirtualSensor::updateVirtualSensor()
2413ed9a516SVijay Khemka {
2423ed9a516SVijay Khemka     for (auto& param : paramMap)
2433ed9a516SVijay Khemka     {
2443ed9a516SVijay Khemka         auto& name = param.first;
2453ed9a516SVijay Khemka         auto& data = param.second;
2463ed9a516SVijay Khemka         if (auto var = symbols.get_variable(name))
2473ed9a516SVijay Khemka         {
2483ed9a516SVijay Khemka             var->ref() = data->getParamValue();
2493ed9a516SVijay Khemka         }
2503ed9a516SVijay Khemka         else
2513ed9a516SVijay Khemka         {
2523ed9a516SVijay Khemka             /* Invalid parameter */
2533ed9a516SVijay Khemka             throw std::invalid_argument("ParamName not found in symbols");
2543ed9a516SVijay Khemka         }
2553ed9a516SVijay Khemka     }
2563ed9a516SVijay Khemka     double val = expression.value();
25732a7156bSVijay Khemka 
25832a7156bSVijay Khemka     /* Set sensor value to dbus interface */
2593ed9a516SVijay Khemka     setSensorValue(val);
26032a7156bSVijay Khemka 
2613ed9a516SVijay Khemka     if (DEBUG)
2623ed9a516SVijay Khemka         std::cout << "Sensor value is " << val << "\n";
26332a7156bSVijay Khemka 
2648f5e6119SMatt Spinler     /* Check sensor thresholds and log required message */
265*b306b03dSMatt Spinler     checkThresholds(val, perfLossIface);
266fdb826d5SPatrick Williams     checkThresholds(val, warningIface);
267fdb826d5SPatrick Williams     checkThresholds(val, criticalIface);
268fdb826d5SPatrick Williams     checkThresholds(val, softShutdownIface);
269fdb826d5SPatrick Williams     checkThresholds(val, hardShutdownIface);
2703ed9a516SVijay Khemka }
271abcc94faSVijay Khemka 
272abcc94faSVijay Khemka /** @brief Parsing Virtual Sensor config JSON file  */
273abcc94faSVijay Khemka Json VirtualSensors::parseConfigFile(const std::string configFile)
274abcc94faSVijay Khemka {
275abcc94faSVijay Khemka     std::ifstream jsonFile(configFile);
276abcc94faSVijay Khemka     if (!jsonFile.is_open())
277abcc94faSVijay Khemka     {
278abcc94faSVijay Khemka         log<level::ERR>("config JSON file not found",
279abcc94faSVijay Khemka                         entry("FILENAME = %s", configFile.c_str()));
280abcc94faSVijay Khemka         throw std::exception{};
281abcc94faSVijay Khemka     }
282abcc94faSVijay Khemka 
283abcc94faSVijay Khemka     auto data = Json::parse(jsonFile, nullptr, false);
284abcc94faSVijay Khemka     if (data.is_discarded())
285abcc94faSVijay Khemka     {
286abcc94faSVijay Khemka         log<level::ERR>("config readings JSON parser failure",
287abcc94faSVijay Khemka                         entry("FILENAME = %s", configFile.c_str()));
288abcc94faSVijay Khemka         throw std::exception{};
289abcc94faSVijay Khemka     }
290abcc94faSVijay Khemka 
291abcc94faSVijay Khemka     return data;
292abcc94faSVijay Khemka }
293abcc94faSVijay Khemka 
294e0d371e4SVijay Khemka std::map<std::string, ValueIface::Unit> unitMap = {
295e0d371e4SVijay Khemka     {"temperature", ValueIface::Unit::DegreesC},
296e0d371e4SVijay Khemka     {"fan_tach", ValueIface::Unit::RPMS},
297e0d371e4SVijay Khemka     {"voltage", ValueIface::Unit::Volts},
298e0d371e4SVijay Khemka     {"altitude", ValueIface::Unit::Meters},
299e0d371e4SVijay Khemka     {"current", ValueIface::Unit::Amperes},
300e0d371e4SVijay Khemka     {"power", ValueIface::Unit::Watts},
301e0d371e4SVijay Khemka     {"energy", ValueIface::Unit::Joules},
302e0d371e4SVijay Khemka     {"utilization", ValueIface::Unit::Percent}};
303e0d371e4SVijay Khemka 
304abcc94faSVijay Khemka void VirtualSensors::createVirtualSensors()
305abcc94faSVijay Khemka {
306abcc94faSVijay Khemka     static const Json empty{};
307abcc94faSVijay Khemka 
308abcc94faSVijay Khemka     auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE);
309abcc94faSVijay Khemka     // print values
310abcc94faSVijay Khemka     if (DEBUG)
311abcc94faSVijay Khemka         std::cout << "Config json data:\n" << data << "\n\n";
312abcc94faSVijay Khemka 
313abcc94faSVijay Khemka     /* Get virtual sensors  config data */
314abcc94faSVijay Khemka     for (const auto& j : data)
315abcc94faSVijay Khemka     {
316abcc94faSVijay Khemka         auto desc = j.value("Desc", empty);
317abcc94faSVijay Khemka         if (!desc.empty())
318abcc94faSVijay Khemka         {
319abcc94faSVijay Khemka             std::string sensorType = desc.value("SensorType", "");
320abcc94faSVijay Khemka             std::string name = desc.value("Name", "");
321abcc94faSVijay Khemka 
322abcc94faSVijay Khemka             if (!name.empty() && !sensorType.empty())
323abcc94faSVijay Khemka             {
324e0d371e4SVijay Khemka                 if (unitMap.find(sensorType) == unitMap.end())
325e0d371e4SVijay Khemka                 {
326e0d371e4SVijay Khemka                     log<level::ERR>("Sensor type is not supported",
327e0d371e4SVijay Khemka                                     entry("TYPE = %s", sensorType.c_str()));
328e0d371e4SVijay Khemka                 }
329e0d371e4SVijay Khemka                 else
330e0d371e4SVijay Khemka                 {
331abcc94faSVijay Khemka                     std::string objPath(sensorDbusPath);
332abcc94faSVijay Khemka                     objPath += sensorType + "/" + name;
333abcc94faSVijay Khemka 
33432a7156bSVijay Khemka                     auto virtualSensorPtr = std::make_unique<VirtualSensor>(
33532a7156bSVijay Khemka                         bus, objPath.c_str(), j, name);
336abcc94faSVijay Khemka 
337abcc94faSVijay Khemka                     log<level::INFO>("Added a new virtual sensor",
338abcc94faSVijay Khemka                                      entry("NAME = %s", name.c_str()));
3393ed9a516SVijay Khemka                     virtualSensorPtr->updateVirtualSensor();
340e0d371e4SVijay Khemka 
341e0d371e4SVijay Khemka                     /* Initialize unit value for virtual sensor */
342e0d371e4SVijay Khemka                     virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
343e0d371e4SVijay Khemka 
3443ed9a516SVijay Khemka                     virtualSensorsMap.emplace(std::move(name),
3453ed9a516SVijay Khemka                                               std::move(virtualSensorPtr));
346abcc94faSVijay Khemka                 }
347e0d371e4SVijay Khemka             }
348abcc94faSVijay Khemka             else
349abcc94faSVijay Khemka             {
350abcc94faSVijay Khemka                 log<level::ERR>("Sensor type or name not found in config file");
351abcc94faSVijay Khemka             }
352abcc94faSVijay Khemka         }
353abcc94faSVijay Khemka         else
354abcc94faSVijay Khemka         {
355abcc94faSVijay Khemka             log<level::ERR>(
356abcc94faSVijay Khemka                 "Descriptor for new virtual sensor not found in config file");
357abcc94faSVijay Khemka         }
358abcc94faSVijay Khemka     }
359abcc94faSVijay Khemka }
360abcc94faSVijay Khemka 
361abcc94faSVijay Khemka } // namespace virtualSensor
362abcc94faSVijay Khemka } // namespace phosphor
363abcc94faSVijay Khemka 
364abcc94faSVijay Khemka /**
365abcc94faSVijay Khemka  * @brief Main
366abcc94faSVijay Khemka  */
367abcc94faSVijay Khemka int main()
368abcc94faSVijay Khemka {
369abcc94faSVijay Khemka 
370abcc94faSVijay Khemka     // Get a default event loop
371abcc94faSVijay Khemka     auto event = sdeventplus::Event::get_default();
372abcc94faSVijay Khemka 
373abcc94faSVijay Khemka     // Get a handle to system dbus
374abcc94faSVijay Khemka     auto bus = sdbusplus::bus::new_default();
375abcc94faSVijay Khemka 
3766c19e7d2SMatt Spinler     // Add the ObjectManager interface
3776c19e7d2SMatt Spinler     sdbusplus::server::manager::manager objManager(bus, "/");
3786c19e7d2SMatt Spinler 
379abcc94faSVijay Khemka     // Create an virtual sensors object
380abcc94faSVijay Khemka     phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
381abcc94faSVijay Khemka 
382abcc94faSVijay Khemka     // Request service bus name
383abcc94faSVijay Khemka     bus.request_name(busName);
384abcc94faSVijay Khemka 
385abcc94faSVijay Khemka     // Attach the bus to sd_event to service user requests
386abcc94faSVijay Khemka     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
387abcc94faSVijay Khemka     event.loop();
388abcc94faSVijay Khemka 
389abcc94faSVijay Khemka     return 0;
390abcc94faSVijay Khemka }
391