1 #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 
11 static constexpr bool DEBUG = false;
12 static constexpr auto busName = "xyz.openbmc_project.VirtualSensor";
13 static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/";
14 static constexpr uint8_t defaultHighThreshold = 100;
15 static constexpr uint8_t defaultLowThreshold = 0;
16 
17 using namespace phosphor::logging;
18 
19 namespace phosphor
20 {
21 namespace virtualSensor
22 {
23 
24 void printParams(const VirtualSensor::ParamMap& paramMap)
25 {
26     for (const auto& p : paramMap)
27     {
28         const auto& p1 = p.first;
29         const auto& p2 = p.second;
30         auto val = p2->getParamValue();
31         std::cout << p1 << " = " << val << "\n";
32     }
33 }
34 
35 double SensorParam::getParamValue()
36 {
37     switch (paramType)
38     {
39         case constParam:
40             return value;
41             break;
42         case dbusParam:
43             return dbusSensor->getSensorValue();
44             break;
45         default:
46             throw std::invalid_argument("param type not supported");
47     }
48 }
49 
50 void VirtualSensor::initVirtualSensor(const Json& sensorConfig)
51 {
52 
53     static const Json empty{};
54 
55     /* Get threshold values if defined in config */
56     auto threshold = sensorConfig.value("Threshold", empty);
57     if (!threshold.empty())
58     {
59         sensorThreshold.criticalHigh =
60             threshold.value("CriticalHigh", defaultHighThreshold);
61         sensorThreshold.criticalLow =
62             threshold.value("CriticalLow", defaultLowThreshold);
63         sensorThreshold.warningHigh =
64             threshold.value("WarningHigh", defaultHighThreshold);
65         sensorThreshold.warningLow =
66             threshold.value("WarningLow", defaultLowThreshold);
67     }
68 
69     /* Set threshold value to dbus */
70     setSensorThreshold();
71 
72     /* Get expression string */
73     exprStr = sensorConfig.value("Expression", "");
74 
75     /* Get all the parameter listed in configuration */
76     auto params = sensorConfig.value("Params", empty);
77 
78     /* Check for constant parameter */
79     const auto& consParams = params.value("ConstParam", empty);
80     if (!consParams.empty())
81     {
82         for (auto& j : consParams)
83         {
84             if (j.find("ParamName") != j.end())
85             {
86                 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
87                 std::string name = j["ParamName"];
88                 symbols.create_variable(name);
89                 paramMap.emplace(std::move(name), std::move(paramPtr));
90             }
91             else
92             {
93                 /* Invalid configuration */
94                 throw std::invalid_argument(
95                     "ParamName not found in configuration");
96             }
97         }
98     }
99 
100     /* Check for dbus parameter */
101     auto dbusParams = params.value("DbusParam", empty);
102     if (!dbusParams.empty())
103     {
104         for (auto& j : dbusParams)
105         {
106             /* Get parameter dbus sensor descriptor */
107             auto desc = j.value("Desc", empty);
108             if ((!desc.empty()) && (j.find("ParamName") != j.end()))
109             {
110                 std::string sensorType = desc.value("SensorType", "");
111                 std::string name = desc.value("Name", "");
112 
113                 if (!sensorType.empty() && !name.empty())
114                 {
115                     std::string objPath(sensorDbusPath);
116                     objPath += sensorType + "/" + name;
117 
118                     auto paramPtr = std::make_unique<SensorParam>(bus, objPath);
119                     std::string name = j["ParamName"];
120                     symbols.create_variable(name);
121                     paramMap.emplace(std::move(name), std::move(paramPtr));
122                 }
123             }
124         }
125     }
126 
127     symbols.add_constants();
128     expression.register_symbol_table(symbols);
129 
130     /* parser from exprtk */
131     exprtk::parser<double> parser{};
132     parser.compile(exprStr, expression);
133 
134     /* Print all parameters for debug purpose only */
135     if (DEBUG)
136         printParams(paramMap);
137 }
138 
139 void VirtualSensor::setSensorValue(double value)
140 {
141     ValueIface::value(value);
142 }
143 
144 void VirtualSensor::setSensorThreshold()
145 {
146     CriticalInterface::criticalHigh(sensorThreshold.criticalHigh);
147     CriticalInterface::criticalLow(sensorThreshold.criticalLow);
148     WarningInterface::warningHigh(sensorThreshold.warningHigh);
149     WarningInterface::warningLow(sensorThreshold.warningLow);
150 }
151 
152 void VirtualSensor::updateVirtualSensor()
153 {
154     for (auto& param : paramMap)
155     {
156         auto& name = param.first;
157         auto& data = param.second;
158         if (auto var = symbols.get_variable(name))
159         {
160             var->ref() = data->getParamValue();
161         }
162         else
163         {
164             /* Invalid parameter */
165             throw std::invalid_argument("ParamName not found in symbols");
166         }
167     }
168     double val = expression.value();
169     setSensorValue(val);
170     if (DEBUG)
171         std::cout << "Sensor value is " << val << "\n";
172 }
173 
174 /** @brief Parsing Virtual Sensor config JSON file  */
175 Json VirtualSensors::parseConfigFile(const std::string configFile)
176 {
177     std::ifstream jsonFile(configFile);
178     if (!jsonFile.is_open())
179     {
180         log<level::ERR>("config JSON file not found",
181                         entry("FILENAME = %s", configFile.c_str()));
182         throw std::exception{};
183     }
184 
185     auto data = Json::parse(jsonFile, nullptr, false);
186     if (data.is_discarded())
187     {
188         log<level::ERR>("config readings JSON parser failure",
189                         entry("FILENAME = %s", configFile.c_str()));
190         throw std::exception{};
191     }
192 
193     return data;
194 }
195 
196 void VirtualSensors::createVirtualSensors()
197 {
198     static const Json empty{};
199 
200     auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE);
201     // print values
202     if (DEBUG)
203         std::cout << "Config json data:\n" << data << "\n\n";
204 
205     /* Get virtual sensors  config data */
206     for (const auto& j : data)
207     {
208         auto desc = j.value("Desc", empty);
209         if (!desc.empty())
210         {
211             std::string sensorType = desc.value("SensorType", "");
212             std::string name = desc.value("Name", "");
213 
214             if (!name.empty() && !sensorType.empty())
215             {
216                 std::string objPath(sensorDbusPath);
217                 objPath += sensorType + "/" + name;
218 
219                 auto virtualSensorPtr =
220                     std::make_unique<VirtualSensor>(bus, objPath.c_str(), j);
221 
222                 log<level::INFO>("Added a new virtual sensor",
223                                  entry("NAME = %s", name.c_str()));
224                 virtualSensorPtr->updateVirtualSensor();
225                 virtualSensorsMap.emplace(std::move(name),
226                                           std::move(virtualSensorPtr));
227             }
228             else
229             {
230                 log<level::ERR>("Sensor type or name not found in config file");
231             }
232         }
233         else
234         {
235             log<level::ERR>(
236                 "Descriptor for new virtual sensor not found in config file");
237         }
238     }
239 }
240 
241 } // namespace virtualSensor
242 } // namespace phosphor
243 
244 /**
245  * @brief Main
246  */
247 int main()
248 {
249 
250     // Get a default event loop
251     auto event = sdeventplus::Event::get_default();
252 
253     // Get a handle to system dbus
254     auto bus = sdbusplus::bus::new_default();
255 
256     // Create an virtual sensors object
257     phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
258 
259     // Request service bus name
260     bus.request_name(busName);
261 
262     // Attach the bus to sd_event to service user requests
263     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
264     event.loop();
265 
266     return 0;
267 }
268