1abcc94faSVijay Khemka #include "virtualSensor.hpp"
2abcc94faSVijay Khemka 
3abcc94faSVijay Khemka #include "config.hpp"
4abcc94faSVijay Khemka 
5*82b39c66SPatrick Williams #include <phosphor-logging/lg2.hpp>
6abcc94faSVijay Khemka #include <sdeventplus/event.hpp>
7abcc94faSVijay Khemka 
8abcc94faSVijay Khemka #include <fstream>
9abcc94faSVijay Khemka #include <iostream>
10abcc94faSVijay Khemka 
11abcc94faSVijay Khemka static constexpr bool DEBUG = false;
12abcc94faSVijay Khemka static constexpr auto busName = "xyz.openbmc_project.VirtualSensor";
13abcc94faSVijay Khemka static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/";
14e7efe135SRashmica Gupta static constexpr auto entityManagerBusName =
15e7efe135SRashmica Gupta     "xyz.openbmc_project.EntityManager";
16e7efe135SRashmica Gupta static constexpr auto vsThresholdsIfaceSuffix = ".Thresholds";
17304fd0e4SRashmica Gupta static constexpr std::array<const char*, 1> calculationIfaces = {
18304fd0e4SRashmica Gupta     "xyz.openbmc_project.Configuration.ModifiedMedian"};
191dff7dceSRashmica Gupta static constexpr auto defaultHysteresis = 0;
20abcc94faSVijay Khemka 
21*82b39c66SPatrick Williams PHOSPHOR_LOG2_USING_WITH_FLAGS;
22abcc94faSVijay Khemka 
2351f898e2SVijay Khemka int handleDbusSignal(sd_bus_message* msg, void* usrData, sd_bus_error*)
2451f898e2SVijay Khemka {
2551f898e2SVijay Khemka     if (usrData == nullptr)
2651f898e2SVijay Khemka     {
2751f898e2SVijay Khemka         throw std::runtime_error("Invalid match");
2851f898e2SVijay Khemka     }
2951f898e2SVijay Khemka 
3051f898e2SVijay Khemka     auto sdbpMsg = sdbusplus::message::message(msg);
3151f898e2SVijay Khemka     std::string msgIfce;
3251f898e2SVijay Khemka     std::map<std::string, std::variant<int64_t, double, bool>> msgData;
3351f898e2SVijay Khemka 
3451f898e2SVijay Khemka     sdbpMsg.read(msgIfce, msgData);
3551f898e2SVijay Khemka 
3651f898e2SVijay Khemka     if (msgData.find("Value") != msgData.end())
3751f898e2SVijay Khemka     {
3851f898e2SVijay Khemka         using namespace phosphor::virtualSensor;
3951f898e2SVijay Khemka         VirtualSensor* obj = static_cast<VirtualSensor*>(usrData);
4051f898e2SVijay Khemka         // TODO(openbmc/phosphor-virtual-sensor#1): updateVirtualSensor should
4151f898e2SVijay Khemka         // be changed to take the information we got from the signal, to avoid
4251f898e2SVijay Khemka         // having to do numerous dbus queries.
4351f898e2SVijay Khemka         obj->updateVirtualSensor();
4451f898e2SVijay Khemka     }
4551f898e2SVijay Khemka     return 0;
4651f898e2SVijay Khemka }
4751f898e2SVijay Khemka 
48abcc94faSVijay Khemka namespace phosphor
49abcc94faSVijay Khemka {
50abcc94faSVijay Khemka namespace virtualSensor
51abcc94faSVijay Khemka {
52abcc94faSVijay Khemka 
53abcc94faSVijay Khemka void printParams(const VirtualSensor::ParamMap& paramMap)
54abcc94faSVijay Khemka {
55abcc94faSVijay Khemka     for (const auto& p : paramMap)
56abcc94faSVijay Khemka     {
57abcc94faSVijay Khemka         const auto& p1 = p.first;
58abcc94faSVijay Khemka         const auto& p2 = p.second;
59abcc94faSVijay Khemka         auto val = p2->getParamValue();
60abcc94faSVijay Khemka         std::cout << p1 << " = " << val << "\n";
61abcc94faSVijay Khemka     }
62abcc94faSVijay Khemka }
63abcc94faSVijay Khemka 
64abcc94faSVijay Khemka double SensorParam::getParamValue()
65abcc94faSVijay Khemka {
66abcc94faSVijay Khemka     switch (paramType)
67abcc94faSVijay Khemka     {
68abcc94faSVijay Khemka         case constParam:
69abcc94faSVijay Khemka             return value;
70abcc94faSVijay Khemka             break;
717452a867SVijay Khemka         case dbusParam:
727452a867SVijay Khemka             return dbusSensor->getSensorValue();
737452a867SVijay Khemka             break;
74abcc94faSVijay Khemka         default:
75abcc94faSVijay Khemka             throw std::invalid_argument("param type not supported");
76abcc94faSVijay Khemka     }
77abcc94faSVijay Khemka }
78abcc94faSVijay Khemka 
790fcf0e1cSLei YU using AssociationList =
800fcf0e1cSLei YU     std::vector<std::tuple<std::string, std::string, std::string>>;
810fcf0e1cSLei YU 
820fcf0e1cSLei YU AssociationList getAssociationsFromJson(const Json& j)
830fcf0e1cSLei YU {
840fcf0e1cSLei YU     AssociationList assocs{};
850fcf0e1cSLei YU     try
860fcf0e1cSLei YU     {
870fcf0e1cSLei YU         j.get_to(assocs);
880fcf0e1cSLei YU     }
890fcf0e1cSLei YU     catch (const std::exception& ex)
900fcf0e1cSLei YU     {
91*82b39c66SPatrick Williams         error("Failed to parse association: {ERROR}", "ERROR", ex);
920fcf0e1cSLei YU     }
930fcf0e1cSLei YU     return assocs;
940fcf0e1cSLei YU }
950fcf0e1cSLei YU 
96e7efe135SRashmica Gupta template <typename U>
97e7efe135SRashmica Gupta struct VariantToNumber
98e7efe135SRashmica Gupta {
99e7efe135SRashmica Gupta     template <typename T>
100e7efe135SRashmica Gupta     U operator()(const T& t) const
101e7efe135SRashmica Gupta     {
102e7efe135SRashmica Gupta         if constexpr (std::is_convertible<T, U>::value)
103e7efe135SRashmica Gupta         {
104e7efe135SRashmica Gupta             return static_cast<U>(t);
105e7efe135SRashmica Gupta         }
106e7efe135SRashmica Gupta         throw std::invalid_argument("Invalid number type in config\n");
107e7efe135SRashmica Gupta     }
108e7efe135SRashmica Gupta };
109e7efe135SRashmica Gupta 
110e7efe135SRashmica Gupta template <typename U>
111e7efe135SRashmica Gupta U getNumberFromConfig(const PropertyMap& map, const std::string& name,
112e7efe135SRashmica Gupta                       bool required)
113e7efe135SRashmica Gupta {
114e7efe135SRashmica Gupta     if (auto itr = map.find(name); itr != map.end())
115e7efe135SRashmica Gupta     {
116e7efe135SRashmica Gupta         return std::visit(VariantToNumber<U>(), itr->second);
117e7efe135SRashmica Gupta     }
118e7efe135SRashmica Gupta     else if (required)
119e7efe135SRashmica Gupta     {
120*82b39c66SPatrick Williams         error("Required field {NAME} missing in config", "NAME", name);
121e7efe135SRashmica Gupta         throw std::invalid_argument("Required field missing in config");
122e7efe135SRashmica Gupta     }
123e7efe135SRashmica Gupta     return std::numeric_limits<U>::quiet_NaN();
124e7efe135SRashmica Gupta }
125e7efe135SRashmica Gupta 
126e7efe135SRashmica Gupta bool isCalculationType(const std::string& interface)
127e7efe135SRashmica Gupta {
128e7efe135SRashmica Gupta     auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
129e7efe135SRashmica Gupta                          interface);
130e7efe135SRashmica Gupta     if (itr != calculationIfaces.end())
131e7efe135SRashmica Gupta     {
132e7efe135SRashmica Gupta         return true;
133e7efe135SRashmica Gupta     }
134e7efe135SRashmica Gupta     return false;
135e7efe135SRashmica Gupta }
136e7efe135SRashmica Gupta 
137e7efe135SRashmica Gupta const std::string getThresholdType(const std::string& direction,
138e7efe135SRashmica Gupta                                    uint64_t severity)
139e7efe135SRashmica Gupta {
140e7efe135SRashmica Gupta     std::string threshold;
141e7efe135SRashmica Gupta     std::string suffix;
142e7efe135SRashmica Gupta     static const std::array thresholdTypes{"Warning", "Critical",
143e7efe135SRashmica Gupta                                            "PerformanceLoss", "SoftShutdown",
144e7efe135SRashmica Gupta                                            "HardShutdown"};
145e7efe135SRashmica Gupta 
146e7efe135SRashmica Gupta     if (severity >= thresholdTypes.size())
147e7efe135SRashmica Gupta     {
148e7efe135SRashmica Gupta         throw std::invalid_argument(
149e7efe135SRashmica Gupta             "Invalid threshold severity specified in entity manager");
150e7efe135SRashmica Gupta     }
151e7efe135SRashmica Gupta     threshold = thresholdTypes[severity];
152e7efe135SRashmica Gupta 
153e7efe135SRashmica Gupta     if (direction == "less than")
154e7efe135SRashmica Gupta     {
155e7efe135SRashmica Gupta         suffix = "Low";
156e7efe135SRashmica Gupta     }
157e7efe135SRashmica Gupta     else if (direction == "greater than")
158e7efe135SRashmica Gupta     {
159e7efe135SRashmica Gupta         suffix = "High";
160e7efe135SRashmica Gupta     }
161e7efe135SRashmica Gupta     else
162e7efe135SRashmica Gupta     {
163e7efe135SRashmica Gupta         throw std::invalid_argument(
164e7efe135SRashmica Gupta             "Invalid threshold direction specified in entity manager");
165e7efe135SRashmica Gupta     }
166e7efe135SRashmica Gupta     return threshold + suffix;
167e7efe135SRashmica Gupta }
168e7efe135SRashmica Gupta 
169e7efe135SRashmica Gupta void parseThresholds(Json& thresholds, const PropertyMap& propertyMap)
170e7efe135SRashmica Gupta {
171e7efe135SRashmica Gupta     std::string direction;
172e7efe135SRashmica Gupta 
173e7efe135SRashmica Gupta     auto severity =
174e7efe135SRashmica Gupta         getNumberFromConfig<uint64_t>(propertyMap, "Severity", true);
175e7efe135SRashmica Gupta     auto value = getNumberFromConfig<double>(propertyMap, "Value", true);
176e7efe135SRashmica Gupta 
177e7efe135SRashmica Gupta     auto itr = propertyMap.find("Direction");
178e7efe135SRashmica Gupta     if (itr != propertyMap.end())
179e7efe135SRashmica Gupta     {
180e7efe135SRashmica Gupta         direction = std::get<std::string>(itr->second);
181e7efe135SRashmica Gupta     }
182e7efe135SRashmica Gupta 
183e7efe135SRashmica Gupta     auto threshold = getThresholdType(direction, severity);
184e7efe135SRashmica Gupta     thresholds[threshold] = value;
1851dff7dceSRashmica Gupta 
1861dff7dceSRashmica Gupta     auto hysteresis =
1871dff7dceSRashmica Gupta         getNumberFromConfig<double>(propertyMap, "Hysteresis", false);
1881dff7dceSRashmica Gupta     if (hysteresis != std::numeric_limits<double>::quiet_NaN())
1891dff7dceSRashmica Gupta     {
1901dff7dceSRashmica Gupta         thresholds[threshold + "Hysteresis"] = hysteresis;
1911dff7dceSRashmica Gupta     }
192e7efe135SRashmica Gupta }
193e7efe135SRashmica Gupta 
194e7efe135SRashmica Gupta void VirtualSensor::parseConfigInterface(const PropertyMap& propertyMap,
195e7efe135SRashmica Gupta                                          const std::string& sensorType,
196e7efe135SRashmica Gupta                                          const std::string& interface)
197e7efe135SRashmica Gupta {
198e7efe135SRashmica Gupta     /* Parse sensors / DBus params */
199e7efe135SRashmica Gupta     if (auto itr = propertyMap.find("Sensors"); itr != propertyMap.end())
200e7efe135SRashmica Gupta     {
201e7efe135SRashmica Gupta         auto sensors = std::get<std::vector<std::string>>(itr->second);
202e7efe135SRashmica Gupta         for (auto sensor : sensors)
203e7efe135SRashmica Gupta         {
204e7efe135SRashmica Gupta             std::replace(sensor.begin(), sensor.end(), ' ', '_');
205e7efe135SRashmica Gupta             auto sensorObjPath = sensorDbusPath + sensorType + "/" + sensor;
206e7efe135SRashmica Gupta 
207e7efe135SRashmica Gupta             auto paramPtr =
208e7efe135SRashmica Gupta                 std::make_unique<SensorParam>(bus, sensorObjPath, this);
209e7efe135SRashmica Gupta             symbols.create_variable(sensor);
210e7efe135SRashmica Gupta             paramMap.emplace(std::move(sensor), std::move(paramPtr));
211e7efe135SRashmica Gupta         }
212e7efe135SRashmica Gupta     }
213e7efe135SRashmica Gupta     /* Get expression string */
214e7efe135SRashmica Gupta     if (!isCalculationType(interface))
215e7efe135SRashmica Gupta     {
216e7efe135SRashmica Gupta         throw std::invalid_argument("Invalid expression in interface");
217e7efe135SRashmica Gupta     }
218e7efe135SRashmica Gupta     exprStr = interface;
219e7efe135SRashmica Gupta 
220e7efe135SRashmica Gupta     /* Get optional min and max input and output values */
221e7efe135SRashmica Gupta     ValueIface::maxValue(
222e7efe135SRashmica Gupta         getNumberFromConfig<double>(propertyMap, "MaxValue", false));
223e7efe135SRashmica Gupta     ValueIface::minValue(
224e7efe135SRashmica Gupta         getNumberFromConfig<double>(propertyMap, "MinValue", false));
225e7efe135SRashmica Gupta     maxValidInput =
226e7efe135SRashmica Gupta         getNumberFromConfig<double>(propertyMap, "MaxValidInput", false);
227e7efe135SRashmica Gupta     minValidInput =
228e7efe135SRashmica Gupta         getNumberFromConfig<double>(propertyMap, "MinValidInput", false);
229e7efe135SRashmica Gupta }
230e7efe135SRashmica Gupta 
231ce675228SMatt Spinler void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
232ce675228SMatt Spinler                                       const std::string& objPath)
233abcc94faSVijay Khemka {
234abcc94faSVijay Khemka 
235abcc94faSVijay Khemka     static const Json empty{};
236abcc94faSVijay Khemka 
237abcc94faSVijay Khemka     /* Get threshold values if defined in config */
238abcc94faSVijay Khemka     auto threshold = sensorConfig.value("Threshold", empty);
239f15189e3SMatt Spinler 
2403e99919bSRashmica Gupta     createThresholds(threshold, objPath);
241abcc94faSVijay Khemka 
242f6443742SHarvey Wu     /* Get MaxValue, MinValue setting if defined in config */
243f6443742SHarvey Wu     auto confDesc = sensorConfig.value("Desc", empty);
244f6443742SHarvey Wu     if (auto maxConf = confDesc.find("MaxValue");
245f6443742SHarvey Wu         maxConf != confDesc.end() && maxConf->is_number())
246f6443742SHarvey Wu     {
247f6443742SHarvey Wu         ValueIface::maxValue(maxConf->get<double>());
248f6443742SHarvey Wu     }
249f6443742SHarvey Wu     if (auto minConf = confDesc.find("MinValue");
250f6443742SHarvey Wu         minConf != confDesc.end() && minConf->is_number())
251f6443742SHarvey Wu     {
252f6443742SHarvey Wu         ValueIface::minValue(minConf->get<double>());
253f6443742SHarvey Wu     }
254f6443742SHarvey Wu 
2550fcf0e1cSLei YU     /* Get optional association */
2560fcf0e1cSLei YU     auto assocJson = sensorConfig.value("Associations", empty);
2570fcf0e1cSLei YU     if (!assocJson.empty())
2580fcf0e1cSLei YU     {
2590fcf0e1cSLei YU         auto assocs = getAssociationsFromJson(assocJson);
2600fcf0e1cSLei YU         if (!assocs.empty())
2610fcf0e1cSLei YU         {
2620fcf0e1cSLei YU             associationIface =
2630fcf0e1cSLei YU                 std::make_unique<AssociationObject>(bus, objPath.c_str());
2640fcf0e1cSLei YU             associationIface->associations(assocs);
2650fcf0e1cSLei YU         }
2660fcf0e1cSLei YU     }
2670fcf0e1cSLei YU 
268abcc94faSVijay Khemka     /* Get expression string */
269abcc94faSVijay Khemka     exprStr = sensorConfig.value("Expression", "");
270abcc94faSVijay Khemka 
271abcc94faSVijay Khemka     /* Get all the parameter listed in configuration */
272abcc94faSVijay Khemka     auto params = sensorConfig.value("Params", empty);
273abcc94faSVijay Khemka 
274abcc94faSVijay Khemka     /* Check for constant parameter */
275abcc94faSVijay Khemka     const auto& consParams = params.value("ConstParam", empty);
276abcc94faSVijay Khemka     if (!consParams.empty())
277abcc94faSVijay Khemka     {
278abcc94faSVijay Khemka         for (auto& j : consParams)
279abcc94faSVijay Khemka         {
280abcc94faSVijay Khemka             if (j.find("ParamName") != j.end())
281abcc94faSVijay Khemka             {
282abcc94faSVijay Khemka                 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
2833ed9a516SVijay Khemka                 std::string name = j["ParamName"];
2843ed9a516SVijay Khemka                 symbols.create_variable(name);
2853ed9a516SVijay Khemka                 paramMap.emplace(std::move(name), std::move(paramPtr));
286abcc94faSVijay Khemka             }
287abcc94faSVijay Khemka             else
288abcc94faSVijay Khemka             {
289abcc94faSVijay Khemka                 /* Invalid configuration */
290abcc94faSVijay Khemka                 throw std::invalid_argument(
291abcc94faSVijay Khemka                     "ParamName not found in configuration");
292abcc94faSVijay Khemka             }
293abcc94faSVijay Khemka         }
294abcc94faSVijay Khemka     }
295abcc94faSVijay Khemka 
2967452a867SVijay Khemka     /* Check for dbus parameter */
2977452a867SVijay Khemka     auto dbusParams = params.value("DbusParam", empty);
2987452a867SVijay Khemka     if (!dbusParams.empty())
2997452a867SVijay Khemka     {
3007452a867SVijay Khemka         for (auto& j : dbusParams)
3017452a867SVijay Khemka         {
3027452a867SVijay Khemka             /* Get parameter dbus sensor descriptor */
3037452a867SVijay Khemka             auto desc = j.value("Desc", empty);
3047452a867SVijay Khemka             if ((!desc.empty()) && (j.find("ParamName") != j.end()))
3057452a867SVijay Khemka             {
3067452a867SVijay Khemka                 std::string sensorType = desc.value("SensorType", "");
3077452a867SVijay Khemka                 std::string name = desc.value("Name", "");
3087452a867SVijay Khemka 
3097452a867SVijay Khemka                 if (!sensorType.empty() && !name.empty())
3107452a867SVijay Khemka                 {
311862c3d1eSRashmica Gupta                     auto objPath = sensorDbusPath + sensorType + "/" + name;
3127452a867SVijay Khemka 
31351f898e2SVijay Khemka                     auto paramPtr =
31451f898e2SVijay Khemka                         std::make_unique<SensorParam>(bus, objPath, this);
3153ed9a516SVijay Khemka                     std::string name = j["ParamName"];
3163ed9a516SVijay Khemka                     symbols.create_variable(name);
3173ed9a516SVijay Khemka                     paramMap.emplace(std::move(name), std::move(paramPtr));
3187452a867SVijay Khemka                 }
3197452a867SVijay Khemka             }
3207452a867SVijay Khemka         }
3217452a867SVijay Khemka     }
322abcc94faSVijay Khemka 
3233ed9a516SVijay Khemka     symbols.add_constants();
3249f1ef4f5SMatt Spinler     symbols.add_package(vecopsPackage);
3253ed9a516SVijay Khemka     expression.register_symbol_table(symbols);
3263ed9a516SVijay Khemka 
3273ed9a516SVijay Khemka     /* parser from exprtk */
3283ed9a516SVijay Khemka     exprtk::parser<double> parser{};
329ddc6dcd6SMatt Spinler     if (!parser.compile(exprStr, expression))
330ddc6dcd6SMatt Spinler     {
331*82b39c66SPatrick Williams         error("Expression compilation failed");
332ddc6dcd6SMatt Spinler 
333ddc6dcd6SMatt Spinler         for (std::size_t i = 0; i < parser.error_count(); ++i)
334ddc6dcd6SMatt Spinler         {
335*82b39c66SPatrick Williams             auto err = parser.get_error(i);
336*82b39c66SPatrick Williams             error("Error parsing token at {POSITION}: {ERROR}", "POSITION",
337*82b39c66SPatrick Williams                   err.token.position, "TYPE",
338*82b39c66SPatrick Williams                   exprtk::parser_error::to_str(err.mode), "ERROR",
339*82b39c66SPatrick Williams                   err.diagnostic);
340ddc6dcd6SMatt Spinler         }
341ddc6dcd6SMatt Spinler         throw std::runtime_error("Expression compilation failed");
342ddc6dcd6SMatt Spinler     }
3433ed9a516SVijay Khemka 
344abcc94faSVijay Khemka     /* Print all parameters for debug purpose only */
345abcc94faSVijay Khemka     if (DEBUG)
346abcc94faSVijay Khemka         printParams(paramMap);
347abcc94faSVijay Khemka }
348abcc94faSVijay Khemka 
349e7efe135SRashmica Gupta void VirtualSensor::initVirtualSensor(const InterfaceMap& interfaceMap,
350e7efe135SRashmica Gupta                                       const std::string& objPath,
351e7efe135SRashmica Gupta                                       const std::string& sensorType,
352e7efe135SRashmica Gupta                                       const std::string& calculationIface)
353e7efe135SRashmica Gupta {
354e7efe135SRashmica Gupta     Json thresholds;
355e7efe135SRashmica Gupta     const std::string vsThresholdsIntf =
356e7efe135SRashmica Gupta         calculationIface + vsThresholdsIfaceSuffix;
357e7efe135SRashmica Gupta 
358e7efe135SRashmica Gupta     for (const auto& [interface, propertyMap] : interfaceMap)
359e7efe135SRashmica Gupta     {
360e7efe135SRashmica Gupta         /* Each threshold is on it's own interface with a number as a suffix
361e7efe135SRashmica Gupta          * eg xyz.openbmc_project.Configuration.ModifiedMedian.Thresholds1 */
362e7efe135SRashmica Gupta         if (interface.find(vsThresholdsIntf) != std::string::npos)
363e7efe135SRashmica Gupta         {
364e7efe135SRashmica Gupta             parseThresholds(thresholds, propertyMap);
365e7efe135SRashmica Gupta         }
366e7efe135SRashmica Gupta         else if (interface == calculationIface)
367e7efe135SRashmica Gupta         {
368e7efe135SRashmica Gupta             parseConfigInterface(propertyMap, sensorType, interface);
369e7efe135SRashmica Gupta         }
370e7efe135SRashmica Gupta     }
371e7efe135SRashmica Gupta 
372e7efe135SRashmica Gupta     createThresholds(thresholds, objPath);
373e7efe135SRashmica Gupta     symbols.add_constants();
374e7efe135SRashmica Gupta     symbols.add_package(vecopsPackage);
375e7efe135SRashmica Gupta     expression.register_symbol_table(symbols);
376e7efe135SRashmica Gupta 
377e7efe135SRashmica Gupta     /* Print all parameters for debug purpose only */
378e7efe135SRashmica Gupta     if (DEBUG)
379e7efe135SRashmica Gupta     {
380e7efe135SRashmica Gupta         printParams(paramMap);
381e7efe135SRashmica Gupta     }
382e7efe135SRashmica Gupta }
383e7efe135SRashmica Gupta 
384abcc94faSVijay Khemka void VirtualSensor::setSensorValue(double value)
385abcc94faSVijay Khemka {
386543bf668SPatrick Williams     value = std::clamp(value, ValueIface::minValue(), ValueIface::maxValue());
387abcc94faSVijay Khemka     ValueIface::value(value);
388abcc94faSVijay Khemka }
389abcc94faSVijay Khemka 
390304fd0e4SRashmica Gupta double VirtualSensor::calculateValue(const std::string& calculation,
391304fd0e4SRashmica Gupta                                      const VirtualSensor::ParamMap& paramMap)
392e7efe135SRashmica Gupta {
393304fd0e4SRashmica Gupta     auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
394304fd0e4SRashmica Gupta                          calculation);
395304fd0e4SRashmica Gupta     if (itr == calculationIfaces.end())
396304fd0e4SRashmica Gupta     {
397e7efe135SRashmica Gupta         return std::numeric_limits<double>::quiet_NaN();
398e7efe135SRashmica Gupta     }
399304fd0e4SRashmica Gupta     else if (calculation == "xyz.openbmc_project.Configuration.ModifiedMedian")
400304fd0e4SRashmica Gupta     {
401304fd0e4SRashmica Gupta         return calculateModifiedMedianValue(paramMap);
402304fd0e4SRashmica Gupta     }
403304fd0e4SRashmica Gupta     return std::numeric_limits<double>::quiet_NaN();
404304fd0e4SRashmica Gupta }
405304fd0e4SRashmica Gupta 
406304fd0e4SRashmica Gupta bool VirtualSensor::sensorInRange(double value)
407304fd0e4SRashmica Gupta {
408304fd0e4SRashmica Gupta     if (value <= this->maxValidInput && value >= this->minValidInput)
409304fd0e4SRashmica Gupta     {
410304fd0e4SRashmica Gupta         return true;
411304fd0e4SRashmica Gupta     }
412304fd0e4SRashmica Gupta     return false;
413304fd0e4SRashmica Gupta }
414e7efe135SRashmica Gupta 
415abcc94faSVijay Khemka void VirtualSensor::updateVirtualSensor()
4163ed9a516SVijay Khemka {
4173ed9a516SVijay Khemka     for (auto& param : paramMap)
4183ed9a516SVijay Khemka     {
4193ed9a516SVijay Khemka         auto& name = param.first;
4203ed9a516SVijay Khemka         auto& data = param.second;
4213ed9a516SVijay Khemka         if (auto var = symbols.get_variable(name))
4223ed9a516SVijay Khemka         {
4233ed9a516SVijay Khemka             var->ref() = data->getParamValue();
4243ed9a516SVijay Khemka         }
4253ed9a516SVijay Khemka         else
4263ed9a516SVijay Khemka         {
4273ed9a516SVijay Khemka             /* Invalid parameter */
4283ed9a516SVijay Khemka             throw std::invalid_argument("ParamName not found in symbols");
4293ed9a516SVijay Khemka         }
4303ed9a516SVijay Khemka     }
431e7efe135SRashmica Gupta     auto itr =
432e7efe135SRashmica Gupta         std::find(calculationIfaces.begin(), calculationIfaces.end(), exprStr);
433304fd0e4SRashmica Gupta     auto val = (itr == calculationIfaces.end())
434304fd0e4SRashmica Gupta                    ? expression.value()
435304fd0e4SRashmica Gupta                    : calculateValue(exprStr, paramMap);
43632a7156bSVijay Khemka 
43732a7156bSVijay Khemka     /* Set sensor value to dbus interface */
4383ed9a516SVijay Khemka     setSensorValue(val);
43932a7156bSVijay Khemka 
4403ed9a516SVijay Khemka     if (DEBUG)
441e7efe135SRashmica Gupta     {
4423ed9a516SVijay Khemka         std::cout << "Sensor value is " << val << "\n";
443e7efe135SRashmica Gupta     }
44432a7156bSVijay Khemka 
4458f5e6119SMatt Spinler     /* Check sensor thresholds and log required message */
446b306b03dSMatt Spinler     checkThresholds(val, perfLossIface);
447fdb826d5SPatrick Williams     checkThresholds(val, warningIface);
448fdb826d5SPatrick Williams     checkThresholds(val, criticalIface);
449fdb826d5SPatrick Williams     checkThresholds(val, softShutdownIface);
450fdb826d5SPatrick Williams     checkThresholds(val, hardShutdownIface);
4513ed9a516SVijay Khemka }
452abcc94faSVijay Khemka 
453304fd0e4SRashmica Gupta double VirtualSensor::calculateModifiedMedianValue(
454304fd0e4SRashmica Gupta     const VirtualSensor::ParamMap& paramMap)
455304fd0e4SRashmica Gupta {
456304fd0e4SRashmica Gupta     std::vector<double> values;
457304fd0e4SRashmica Gupta 
458304fd0e4SRashmica Gupta     for (auto& param : paramMap)
459304fd0e4SRashmica Gupta     {
460304fd0e4SRashmica Gupta         auto& name = param.first;
461304fd0e4SRashmica Gupta         if (auto var = symbols.get_variable(name))
462304fd0e4SRashmica Gupta         {
463304fd0e4SRashmica Gupta             if (!sensorInRange(var->ref()))
464304fd0e4SRashmica Gupta             {
465304fd0e4SRashmica Gupta                 continue;
466304fd0e4SRashmica Gupta             }
467304fd0e4SRashmica Gupta             values.push_back(var->ref());
468304fd0e4SRashmica Gupta         }
469304fd0e4SRashmica Gupta     }
470304fd0e4SRashmica Gupta 
471304fd0e4SRashmica Gupta     size_t size = values.size();
472304fd0e4SRashmica Gupta     std::sort(values.begin(), values.end());
473304fd0e4SRashmica Gupta     switch (size)
474304fd0e4SRashmica Gupta     {
475304fd0e4SRashmica Gupta         case 2:
476304fd0e4SRashmica Gupta             /* Choose biggest value */
477304fd0e4SRashmica Gupta             return values.at(1);
478304fd0e4SRashmica Gupta         case 0:
479304fd0e4SRashmica Gupta             return std::numeric_limits<double>::quiet_NaN();
480304fd0e4SRashmica Gupta         default:
481304fd0e4SRashmica Gupta             /* Choose median value */
482304fd0e4SRashmica Gupta             if (size % 2 == 0)
483304fd0e4SRashmica Gupta             {
484304fd0e4SRashmica Gupta                 // Average of the two middle values
485304fd0e4SRashmica Gupta                 return (values.at(size / 2) + values.at(size / 2 - 1)) / 2;
486304fd0e4SRashmica Gupta             }
487304fd0e4SRashmica Gupta             else
488304fd0e4SRashmica Gupta             {
489304fd0e4SRashmica Gupta                 return values.at((size - 1) / 2);
490304fd0e4SRashmica Gupta             }
491304fd0e4SRashmica Gupta     }
492304fd0e4SRashmica Gupta }
493304fd0e4SRashmica Gupta 
4943e99919bSRashmica Gupta void VirtualSensor::createThresholds(const Json& threshold,
4953e99919bSRashmica Gupta                                      const std::string& objPath)
4963e99919bSRashmica Gupta {
4973e99919bSRashmica Gupta     if (threshold.empty())
4983e99919bSRashmica Gupta     {
4993e99919bSRashmica Gupta         return;
5003e99919bSRashmica Gupta     }
5013e99919bSRashmica Gupta     // Only create the threshold interfaces if
5023e99919bSRashmica Gupta     // at least one of their values is present.
5033e99919bSRashmica Gupta     if (threshold.contains("CriticalHigh") || threshold.contains("CriticalLow"))
5043e99919bSRashmica Gupta     {
5053e99919bSRashmica Gupta         criticalIface =
5063e99919bSRashmica Gupta             std::make_unique<Threshold<CriticalObject>>(bus, objPath.c_str());
5073e99919bSRashmica Gupta 
5083e99919bSRashmica Gupta         criticalIface->criticalHigh(threshold.value(
5093e99919bSRashmica Gupta             "CriticalHigh", std::numeric_limits<double>::quiet_NaN()));
5103e99919bSRashmica Gupta         criticalIface->criticalLow(threshold.value(
5113e99919bSRashmica Gupta             "CriticalLow", std::numeric_limits<double>::quiet_NaN()));
5121dff7dceSRashmica Gupta         criticalIface->setHighHysteresis(
5131dff7dceSRashmica Gupta             threshold.value("CriticalHighHysteresis", defaultHysteresis));
5141dff7dceSRashmica Gupta         criticalIface->setLowHysteresis(
5151dff7dceSRashmica Gupta             threshold.value("CriticalLowHysteresis", defaultHysteresis));
5163e99919bSRashmica Gupta     }
5173e99919bSRashmica Gupta 
5183e99919bSRashmica Gupta     if (threshold.contains("WarningHigh") || threshold.contains("WarningLow"))
5193e99919bSRashmica Gupta     {
5203e99919bSRashmica Gupta         warningIface =
5213e99919bSRashmica Gupta             std::make_unique<Threshold<WarningObject>>(bus, objPath.c_str());
5223e99919bSRashmica Gupta 
5233e99919bSRashmica Gupta         warningIface->warningHigh(threshold.value(
5243e99919bSRashmica Gupta             "WarningHigh", std::numeric_limits<double>::quiet_NaN()));
5253e99919bSRashmica Gupta         warningIface->warningLow(threshold.value(
5263e99919bSRashmica Gupta             "WarningLow", std::numeric_limits<double>::quiet_NaN()));
5271dff7dceSRashmica Gupta         warningIface->setHighHysteresis(
5281dff7dceSRashmica Gupta             threshold.value("WarningHighHysteresis", defaultHysteresis));
5291dff7dceSRashmica Gupta         warningIface->setLowHysteresis(
5301dff7dceSRashmica Gupta             threshold.value("WarningLowHysteresis", defaultHysteresis));
5313e99919bSRashmica Gupta     }
5323e99919bSRashmica Gupta 
5333e99919bSRashmica Gupta     if (threshold.contains("HardShutdownHigh") ||
5343e99919bSRashmica Gupta         threshold.contains("HardShutdownLow"))
5353e99919bSRashmica Gupta     {
5363e99919bSRashmica Gupta         hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>(
5373e99919bSRashmica Gupta             bus, objPath.c_str());
5383e99919bSRashmica Gupta 
5393e99919bSRashmica Gupta         hardShutdownIface->hardShutdownHigh(threshold.value(
5403e99919bSRashmica Gupta             "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
5413e99919bSRashmica Gupta         hardShutdownIface->hardShutdownLow(threshold.value(
5423e99919bSRashmica Gupta             "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
5431dff7dceSRashmica Gupta         hardShutdownIface->setHighHysteresis(
5441dff7dceSRashmica Gupta             threshold.value("HardShutdownHighHysteresis", defaultHysteresis));
5451dff7dceSRashmica Gupta         hardShutdownIface->setLowHysteresis(
5461dff7dceSRashmica Gupta             threshold.value("HardShutdownLowHysteresis", defaultHysteresis));
5473e99919bSRashmica Gupta     }
5483e99919bSRashmica Gupta 
5493e99919bSRashmica Gupta     if (threshold.contains("SoftShutdownHigh") ||
5503e99919bSRashmica Gupta         threshold.contains("SoftShutdownLow"))
5513e99919bSRashmica Gupta     {
5523e99919bSRashmica Gupta         softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>(
5533e99919bSRashmica Gupta             bus, objPath.c_str());
5543e99919bSRashmica Gupta 
5553e99919bSRashmica Gupta         softShutdownIface->softShutdownHigh(threshold.value(
5563e99919bSRashmica Gupta             "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
5573e99919bSRashmica Gupta         softShutdownIface->softShutdownLow(threshold.value(
5583e99919bSRashmica Gupta             "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
5591dff7dceSRashmica Gupta         softShutdownIface->setHighHysteresis(
5601dff7dceSRashmica Gupta             threshold.value("SoftShutdownHighHysteresis", defaultHysteresis));
5611dff7dceSRashmica Gupta         softShutdownIface->setLowHysteresis(
5621dff7dceSRashmica Gupta             threshold.value("SoftShutdownLowHysteresis", defaultHysteresis));
5633e99919bSRashmica Gupta     }
5643e99919bSRashmica Gupta 
5653e99919bSRashmica Gupta     if (threshold.contains("PerformanceLossHigh") ||
5663e99919bSRashmica Gupta         threshold.contains("PerformanceLossLow"))
5673e99919bSRashmica Gupta     {
5683e99919bSRashmica Gupta         perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>(
5693e99919bSRashmica Gupta             bus, objPath.c_str());
5703e99919bSRashmica Gupta 
5713e99919bSRashmica Gupta         perfLossIface->performanceLossHigh(threshold.value(
5723e99919bSRashmica Gupta             "PerformanceLossHigh", std::numeric_limits<double>::quiet_NaN()));
5733e99919bSRashmica Gupta         perfLossIface->performanceLossLow(threshold.value(
5743e99919bSRashmica Gupta             "PerformanceLossLow", std::numeric_limits<double>::quiet_NaN()));
5751dff7dceSRashmica Gupta         perfLossIface->setHighHysteresis(threshold.value(
5761dff7dceSRashmica Gupta             "PerformanceLossHighHysteresis", defaultHysteresis));
5771dff7dceSRashmica Gupta         perfLossIface->setLowHysteresis(
5781dff7dceSRashmica Gupta             threshold.value("PerformanceLossLowHysteresis", defaultHysteresis));
5793e99919bSRashmica Gupta     }
5803e99919bSRashmica Gupta }
5813e99919bSRashmica Gupta 
582e7efe135SRashmica Gupta ManagedObjectType VirtualSensors::getObjectsFromDBus()
583e7efe135SRashmica Gupta {
584e7efe135SRashmica Gupta     ManagedObjectType objects;
585e7efe135SRashmica Gupta 
586e7efe135SRashmica Gupta     try
587e7efe135SRashmica Gupta     {
588e7efe135SRashmica Gupta         auto method = bus.new_method_call(entityManagerBusName, "/",
589e7efe135SRashmica Gupta                                           "org.freedesktop.DBus.ObjectManager",
590e7efe135SRashmica Gupta                                           "GetManagedObjects");
591e7efe135SRashmica Gupta         auto reply = bus.call(method);
592e7efe135SRashmica Gupta         reply.read(objects);
593e7efe135SRashmica Gupta     }
594e7efe135SRashmica Gupta     catch (const sdbusplus::exception::SdBusError& ex)
595e7efe135SRashmica Gupta     {
596e7efe135SRashmica Gupta         // If entity manager isn't running yet, keep going.
597e7efe135SRashmica Gupta         if (std::string("org.freedesktop.DBus.Error.ServiceUnknown") !=
598e7efe135SRashmica Gupta             ex.name())
599e7efe135SRashmica Gupta         {
600e7efe135SRashmica Gupta             throw ex.name();
601e7efe135SRashmica Gupta         }
602e7efe135SRashmica Gupta     }
603e7efe135SRashmica Gupta 
604e7efe135SRashmica Gupta     return objects;
605e7efe135SRashmica Gupta }
606e7efe135SRashmica Gupta 
607e7efe135SRashmica Gupta void VirtualSensors::propertiesChanged(sdbusplus::message::message& msg)
608e7efe135SRashmica Gupta {
609e7efe135SRashmica Gupta     std::string path;
610e7efe135SRashmica Gupta     PropertyMap properties;
611e7efe135SRashmica Gupta 
612e7efe135SRashmica Gupta     msg.read(path, properties);
613e7efe135SRashmica Gupta 
614e7efe135SRashmica Gupta     /* We get multiple callbacks for one sensor. 'Type' is a required field and
615e7efe135SRashmica Gupta      * is a unique label so use to to only proceed once per sensor */
616e7efe135SRashmica Gupta     if (properties.contains("Type"))
617e7efe135SRashmica Gupta     {
618e7efe135SRashmica Gupta         if (isCalculationType(path))
619e7efe135SRashmica Gupta         {
620e7efe135SRashmica Gupta             createVirtualSensorsFromDBus(path);
621e7efe135SRashmica Gupta         }
622e7efe135SRashmica Gupta     }
623e7efe135SRashmica Gupta }
624e7efe135SRashmica Gupta 
625abcc94faSVijay Khemka /** @brief Parsing Virtual Sensor config JSON file  */
626abcc94faSVijay Khemka Json VirtualSensors::parseConfigFile(const std::string configFile)
627abcc94faSVijay Khemka {
628abcc94faSVijay Khemka     std::ifstream jsonFile(configFile);
629abcc94faSVijay Khemka     if (!jsonFile.is_open())
630abcc94faSVijay Khemka     {
631*82b39c66SPatrick Williams         error("config JSON file {FILENAME} not found", "FILENAME", configFile);
632e7efe135SRashmica Gupta         return {};
633abcc94faSVijay Khemka     }
634abcc94faSVijay Khemka 
635abcc94faSVijay Khemka     auto data = Json::parse(jsonFile, nullptr, false);
636abcc94faSVijay Khemka     if (data.is_discarded())
637abcc94faSVijay Khemka     {
638*82b39c66SPatrick Williams         error("config readings JSON parser failure with {FILENAME}", "FILENAME",
639*82b39c66SPatrick Williams               configFile);
640abcc94faSVijay Khemka         throw std::exception{};
641abcc94faSVijay Khemka     }
642abcc94faSVijay Khemka 
643abcc94faSVijay Khemka     return data;
644abcc94faSVijay Khemka }
645abcc94faSVijay Khemka 
646e0d371e4SVijay Khemka std::map<std::string, ValueIface::Unit> unitMap = {
647e0d371e4SVijay Khemka     {"temperature", ValueIface::Unit::DegreesC},
648e0d371e4SVijay Khemka     {"fan_tach", ValueIface::Unit::RPMS},
649e0d371e4SVijay Khemka     {"voltage", ValueIface::Unit::Volts},
650e0d371e4SVijay Khemka     {"altitude", ValueIface::Unit::Meters},
651e0d371e4SVijay Khemka     {"current", ValueIface::Unit::Amperes},
652e0d371e4SVijay Khemka     {"power", ValueIface::Unit::Watts},
653e0d371e4SVijay Khemka     {"energy", ValueIface::Unit::Joules},
6542b56ddb3SKumar Thangavel     {"utilization", ValueIface::Unit::Percent},
6554ac7a7f2SRashmica Gupta     {"airflow", ValueIface::Unit::CFM},
6564ac7a7f2SRashmica Gupta     {"pressure", ValueIface::Unit::Pascals}};
657e0d371e4SVijay Khemka 
658e7efe135SRashmica Gupta const std::string getSensorTypeFromUnit(const std::string& unit)
659e7efe135SRashmica Gupta {
660e7efe135SRashmica Gupta     std::string unitPrefix = "xyz.openbmc_project.Sensor.Value.Unit.";
661e7efe135SRashmica Gupta     for (auto [type, unitObj] : unitMap)
662e7efe135SRashmica Gupta     {
663e7efe135SRashmica Gupta         auto unitPath = ValueIface::convertUnitToString(unitObj);
664e7efe135SRashmica Gupta         if (unitPath == (unitPrefix + unit))
665e7efe135SRashmica Gupta         {
666e7efe135SRashmica Gupta             return type;
667e7efe135SRashmica Gupta         }
668e7efe135SRashmica Gupta     }
669e7efe135SRashmica Gupta     return "";
670e7efe135SRashmica Gupta }
671e7efe135SRashmica Gupta 
672e7efe135SRashmica Gupta void VirtualSensors::setupMatches()
673e7efe135SRashmica Gupta {
674e7efe135SRashmica Gupta     /* Already setup */
675e7efe135SRashmica Gupta     if (!this->matches.empty())
676e7efe135SRashmica Gupta     {
677e7efe135SRashmica Gupta         return;
678e7efe135SRashmica Gupta     }
679e7efe135SRashmica Gupta 
680e7efe135SRashmica Gupta     /* Setup matches */
681e7efe135SRashmica Gupta     auto eventHandler = [this](sdbusplus::message::message& message) {
682e7efe135SRashmica Gupta         if (message.is_method_error())
683e7efe135SRashmica Gupta         {
684*82b39c66SPatrick Williams             error("Callback method error");
685e7efe135SRashmica Gupta             return;
686e7efe135SRashmica Gupta         }
687e7efe135SRashmica Gupta         this->propertiesChanged(message);
688e7efe135SRashmica Gupta     };
689e7efe135SRashmica Gupta 
690e7efe135SRashmica Gupta     for (const char* iface : calculationIfaces)
691e7efe135SRashmica Gupta     {
692e7efe135SRashmica Gupta         auto match = std::make_unique<sdbusplus::bus::match::match>(
693e7efe135SRashmica Gupta             bus,
694e7efe135SRashmica Gupta             sdbusplus::bus::match::rules::propertiesChangedNamespace(
695e7efe135SRashmica Gupta                 "/xyz/openbmc_project/inventory", iface),
696e7efe135SRashmica Gupta             eventHandler);
697e7efe135SRashmica Gupta         this->matches.emplace_back(std::move(match));
698e7efe135SRashmica Gupta     }
699e7efe135SRashmica Gupta }
700e7efe135SRashmica Gupta 
701e7efe135SRashmica Gupta void VirtualSensors::createVirtualSensorsFromDBus(
702e7efe135SRashmica Gupta     const std::string& calculationIface)
703e7efe135SRashmica Gupta {
704e7efe135SRashmica Gupta     if (calculationIface.empty())
705e7efe135SRashmica Gupta     {
706*82b39c66SPatrick Williams         error("No calculation type supplied");
707e7efe135SRashmica Gupta         return;
708e7efe135SRashmica Gupta     }
709e7efe135SRashmica Gupta     auto objects = getObjectsFromDBus();
710e7efe135SRashmica Gupta 
711e7efe135SRashmica Gupta     /* Get virtual sensors config data */
712e7efe135SRashmica Gupta     for (const auto& [path, interfaceMap] : objects)
713e7efe135SRashmica Gupta     {
714e7efe135SRashmica Gupta         auto objpath = static_cast<std::string>(path);
715e7efe135SRashmica Gupta         std::string name = path.filename();
716e7efe135SRashmica Gupta         std::string sensorType, sensorUnit;
717e7efe135SRashmica Gupta 
718e7efe135SRashmica Gupta         /* Find Virtual Sensor interfaces */
719e7efe135SRashmica Gupta         if (!interfaceMap.contains(calculationIface))
720e7efe135SRashmica Gupta         {
721e7efe135SRashmica Gupta             continue;
722e7efe135SRashmica Gupta         }
723e7efe135SRashmica Gupta         if (name.empty())
724e7efe135SRashmica Gupta         {
725*82b39c66SPatrick Williams             error("Virtual Sensor name not found in entity manager config");
726e7efe135SRashmica Gupta             continue;
727e7efe135SRashmica Gupta         }
728e7efe135SRashmica Gupta         if (virtualSensorsMap.contains(name))
729e7efe135SRashmica Gupta         {
730*82b39c66SPatrick Williams             error("A virtual sensor named {NAME} already exists", "NAME", name);
731e7efe135SRashmica Gupta             continue;
732e7efe135SRashmica Gupta         }
733e7efe135SRashmica Gupta 
734e7efe135SRashmica Gupta         /* Extract the virtual sensor type as we need this to initialize the
735e7efe135SRashmica Gupta          * sensor */
736e7efe135SRashmica Gupta         for (const auto& [interface, propertyMap] : interfaceMap)
737e7efe135SRashmica Gupta         {
738e7efe135SRashmica Gupta             if (interface != calculationIface)
739e7efe135SRashmica Gupta             {
740e7efe135SRashmica Gupta                 continue;
741e7efe135SRashmica Gupta             }
742e7efe135SRashmica Gupta             auto itr = propertyMap.find("Units");
743e7efe135SRashmica Gupta             if (itr != propertyMap.end())
744e7efe135SRashmica Gupta             {
745e7efe135SRashmica Gupta                 sensorUnit = std::get<std::string>(itr->second);
746e7efe135SRashmica Gupta                 break;
747e7efe135SRashmica Gupta             }
748e7efe135SRashmica Gupta         }
749e7efe135SRashmica Gupta         sensorType = getSensorTypeFromUnit(sensorUnit);
750e7efe135SRashmica Gupta         if (sensorType.empty())
751e7efe135SRashmica Gupta         {
752*82b39c66SPatrick Williams             error("Sensor unit type {TYPE} is not supported", "TYPE",
753*82b39c66SPatrick Williams                   sensorUnit);
754e7efe135SRashmica Gupta             continue;
755e7efe135SRashmica Gupta         }
756e7efe135SRashmica Gupta 
757e7efe135SRashmica Gupta         try
758e7efe135SRashmica Gupta         {
759e7efe135SRashmica Gupta             auto virtObjPath = sensorDbusPath + sensorType + "/" + name;
760e7efe135SRashmica Gupta 
761e7efe135SRashmica Gupta             auto virtualSensorPtr = std::make_unique<VirtualSensor>(
762e7efe135SRashmica Gupta                 bus, virtObjPath.c_str(), interfaceMap, name, sensorType,
763e7efe135SRashmica Gupta                 calculationIface);
764*82b39c66SPatrick Williams             info("Added a new virtual sensor: {NAME} {TYPE}", "NAME", name,
765*82b39c66SPatrick Williams                  "TYPE", sensorType);
766e7efe135SRashmica Gupta             virtualSensorPtr->updateVirtualSensor();
767e7efe135SRashmica Gupta 
768e7efe135SRashmica Gupta             /* Initialize unit value for virtual sensor */
769e7efe135SRashmica Gupta             virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
770e7efe135SRashmica Gupta             virtualSensorPtr->emit_object_added();
771e7efe135SRashmica Gupta 
772e7efe135SRashmica Gupta             virtualSensorsMap.emplace(name, std::move(virtualSensorPtr));
773e7efe135SRashmica Gupta 
774e7efe135SRashmica Gupta             /* Setup match for interfaces removed */
775e7efe135SRashmica Gupta             auto intfRemoved = [this, objpath,
776e7efe135SRashmica Gupta                                 name](sdbusplus::message::message& message) {
777e7efe135SRashmica Gupta                 if (!virtualSensorsMap.contains(name))
778e7efe135SRashmica Gupta                 {
779e7efe135SRashmica Gupta                     return;
780e7efe135SRashmica Gupta                 }
781e7efe135SRashmica Gupta                 sdbusplus::message::object_path path;
782e7efe135SRashmica Gupta                 message.read(path);
783e7efe135SRashmica Gupta                 if (static_cast<const std::string&>(path) == objpath)
784e7efe135SRashmica Gupta                 {
785*82b39c66SPatrick Williams                     info("Removed a virtual sensor: {NAME}", "NAME", name);
786e7efe135SRashmica Gupta                     virtualSensorsMap.erase(name);
787e7efe135SRashmica Gupta                 }
788e7efe135SRashmica Gupta             };
789e7efe135SRashmica Gupta             auto matchOnRemove = std::make_unique<sdbusplus::bus::match::match>(
790e7efe135SRashmica Gupta                 bus,
791e7efe135SRashmica Gupta                 sdbusplus::bus::match::rules::interfacesRemoved() +
792e7efe135SRashmica Gupta                     sdbusplus::bus::match::rules::argNpath(0, objpath),
793e7efe135SRashmica Gupta                 intfRemoved);
794e7efe135SRashmica Gupta             /* TODO: slight race condition here. Check that the config still
795e7efe135SRashmica Gupta              * exists */
796e7efe135SRashmica Gupta             this->matches.emplace_back(std::move(matchOnRemove));
797e7efe135SRashmica Gupta         }
798e7efe135SRashmica Gupta         catch (std::invalid_argument& ia)
799e7efe135SRashmica Gupta         {
800*82b39c66SPatrick Williams             error("Failed to set up virtual sensor: {ERROR}", "ERROR", ia);
801e7efe135SRashmica Gupta         }
802e7efe135SRashmica Gupta     }
803e7efe135SRashmica Gupta }
804e7efe135SRashmica Gupta 
805abcc94faSVijay Khemka void VirtualSensors::createVirtualSensors()
806abcc94faSVijay Khemka {
807abcc94faSVijay Khemka     static const Json empty{};
808abcc94faSVijay Khemka 
809abcc94faSVijay Khemka     auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE);
810e7efe135SRashmica Gupta 
811abcc94faSVijay Khemka     // print values
812abcc94faSVijay Khemka     if (DEBUG)
813e7efe135SRashmica Gupta     {
814abcc94faSVijay Khemka         std::cout << "Config json data:\n" << data << "\n\n";
815e7efe135SRashmica Gupta     }
816abcc94faSVijay Khemka 
817abcc94faSVijay Khemka     /* Get virtual sensors  config data */
818abcc94faSVijay Khemka     for (const auto& j : data)
819abcc94faSVijay Khemka     {
820abcc94faSVijay Khemka         auto desc = j.value("Desc", empty);
821abcc94faSVijay Khemka         if (!desc.empty())
822abcc94faSVijay Khemka         {
823e7efe135SRashmica Gupta             if (desc.value("Config", "") == "D-Bus")
824e7efe135SRashmica Gupta             {
825e7efe135SRashmica Gupta                 /* Look on D-Bus for a virtual sensor config. Set up matches
826e7efe135SRashmica Gupta                  * first because the configs may not be on D-Bus yet and we
827e7efe135SRashmica Gupta                  * don't want to miss them */
828e7efe135SRashmica Gupta                 setupMatches();
829e7efe135SRashmica Gupta 
830e7efe135SRashmica Gupta                 if (desc.contains("Type"))
831e7efe135SRashmica Gupta                 {
832*82b39c66SPatrick Williams                     auto type = desc.value("Type", "");
833*82b39c66SPatrick Williams                     auto path = "xyz.openbmc_project.Configuration." + type;
834*82b39c66SPatrick Williams 
835e7efe135SRashmica Gupta                     if (!isCalculationType(path))
836e7efe135SRashmica Gupta                     {
837*82b39c66SPatrick Williams                         error("Invalid calculation type {TYPE} supplied.",
838*82b39c66SPatrick Williams                               "TYPE", type);
839e7efe135SRashmica Gupta                         continue;
840e7efe135SRashmica Gupta                     }
841e7efe135SRashmica Gupta                     createVirtualSensorsFromDBus(path);
842e7efe135SRashmica Gupta                 }
843e7efe135SRashmica Gupta                 continue;
844e7efe135SRashmica Gupta             }
845e7efe135SRashmica Gupta 
846abcc94faSVijay Khemka             std::string sensorType = desc.value("SensorType", "");
847abcc94faSVijay Khemka             std::string name = desc.value("Name", "");
848665a0a29SRashmica Gupta             std::replace(name.begin(), name.end(), ' ', '_');
849abcc94faSVijay Khemka 
850abcc94faSVijay Khemka             if (!name.empty() && !sensorType.empty())
851abcc94faSVijay Khemka             {
852e0d371e4SVijay Khemka                 if (unitMap.find(sensorType) == unitMap.end())
853e0d371e4SVijay Khemka                 {
854*82b39c66SPatrick Williams                     error("Sensor type {TYPE} is not supported", "TYPE",
855*82b39c66SPatrick Williams                           sensorType);
856e0d371e4SVijay Khemka                 }
857e0d371e4SVijay Khemka                 else
858e0d371e4SVijay Khemka                 {
85967d8b9d2SRashmica Gupta                     if (virtualSensorsMap.find(name) != virtualSensorsMap.end())
86067d8b9d2SRashmica Gupta                     {
861*82b39c66SPatrick Williams                         error("A virtual sensor named {NAME} already exists",
862*82b39c66SPatrick Williams                               "NAME", name);
86367d8b9d2SRashmica Gupta                         continue;
86467d8b9d2SRashmica Gupta                     }
865862c3d1eSRashmica Gupta                     auto objPath = sensorDbusPath + sensorType + "/" + name;
866abcc94faSVijay Khemka 
86732a7156bSVijay Khemka                     auto virtualSensorPtr = std::make_unique<VirtualSensor>(
86832a7156bSVijay Khemka                         bus, objPath.c_str(), j, name);
869abcc94faSVijay Khemka 
870*82b39c66SPatrick Williams                     info("Added a new virtual sensor: {NAME}", "NAME", name);
8713ed9a516SVijay Khemka                     virtualSensorPtr->updateVirtualSensor();
872e0d371e4SVijay Khemka 
873e0d371e4SVijay Khemka                     /* Initialize unit value for virtual sensor */
874e0d371e4SVijay Khemka                     virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
875a2fa63a6SRashmica Gupta                     virtualSensorPtr->emit_object_added();
876e0d371e4SVijay Khemka 
8773ed9a516SVijay Khemka                     virtualSensorsMap.emplace(std::move(name),
8783ed9a516SVijay Khemka                                               std::move(virtualSensorPtr));
879abcc94faSVijay Khemka                 }
880e0d371e4SVijay Khemka             }
881abcc94faSVijay Khemka             else
882abcc94faSVijay Khemka             {
883*82b39c66SPatrick Williams                 error(
884*82b39c66SPatrick Williams                     "Sensor type ({TYPE}) or name ({NAME}) not found in config file",
885*82b39c66SPatrick Williams                     "NAME", name, "TYPE", sensorType);
886abcc94faSVijay Khemka             }
887abcc94faSVijay Khemka         }
888abcc94faSVijay Khemka         else
889abcc94faSVijay Khemka         {
890*82b39c66SPatrick Williams             error("Descriptor for new virtual sensor not found in config file");
891abcc94faSVijay Khemka         }
892abcc94faSVijay Khemka     }
893abcc94faSVijay Khemka }
894abcc94faSVijay Khemka 
895abcc94faSVijay Khemka } // namespace virtualSensor
896abcc94faSVijay Khemka } // namespace phosphor
897abcc94faSVijay Khemka 
898abcc94faSVijay Khemka /**
899abcc94faSVijay Khemka  * @brief Main
900abcc94faSVijay Khemka  */
901abcc94faSVijay Khemka int main()
902abcc94faSVijay Khemka {
903abcc94faSVijay Khemka 
904abcc94faSVijay Khemka     // Get a default event loop
905abcc94faSVijay Khemka     auto event = sdeventplus::Event::get_default();
906abcc94faSVijay Khemka 
907abcc94faSVijay Khemka     // Get a handle to system dbus
908abcc94faSVijay Khemka     auto bus = sdbusplus::bus::new_default();
909abcc94faSVijay Khemka 
9106c19e7d2SMatt Spinler     // Add the ObjectManager interface
9116c19e7d2SMatt Spinler     sdbusplus::server::manager::manager objManager(bus, "/");
9126c19e7d2SMatt Spinler 
913abcc94faSVijay Khemka     // Create an virtual sensors object
914abcc94faSVijay Khemka     phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
915abcc94faSVijay Khemka 
916abcc94faSVijay Khemka     // Request service bus name
917abcc94faSVijay Khemka     bus.request_name(busName);
918abcc94faSVijay Khemka 
919abcc94faSVijay Khemka     // Attach the bus to sd_event to service user requests
920abcc94faSVijay Khemka     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
921abcc94faSVijay Khemka     event.loop();
922abcc94faSVijay Khemka 
923abcc94faSVijay Khemka     return 0;
924abcc94faSVijay Khemka }
925