1abcc94faSVijay Khemka #include "virtualSensor.hpp"
2abcc94faSVijay Khemka 
382b39c66SPatrick Williams #include <phosphor-logging/lg2.hpp>
4abcc94faSVijay Khemka 
5abcc94faSVijay Khemka #include <fstream>
6abcc94faSVijay Khemka 
7abcc94faSVijay Khemka static constexpr bool DEBUG = false;
8abcc94faSVijay Khemka static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/";
9e7efe135SRashmica Gupta static constexpr auto vsThresholdsIfaceSuffix = ".Thresholds";
10f6b7e0a4STao Lin static constexpr std::array<const char*, 2> calculationIfaces = {
11f6b7e0a4STao Lin     "xyz.openbmc_project.Configuration.ModifiedMedian",
12f6b7e0a4STao Lin     "xyz.openbmc_project.Configuration.Maximum"};
131dff7dceSRashmica Gupta static constexpr auto defaultHysteresis = 0;
14abcc94faSVijay Khemka 
1582b39c66SPatrick Williams PHOSPHOR_LOG2_USING_WITH_FLAGS;
16abcc94faSVijay Khemka 
1751f898e2SVijay Khemka int handleDbusSignal(sd_bus_message* msg, void* usrData, sd_bus_error*)
1851f898e2SVijay Khemka {
1951f898e2SVijay Khemka     if (usrData == nullptr)
2051f898e2SVijay Khemka     {
2151f898e2SVijay Khemka         throw std::runtime_error("Invalid match");
2251f898e2SVijay Khemka     }
2351f898e2SVijay Khemka 
248e11cccbSPatrick Williams     auto sdbpMsg = sdbusplus::message_t(msg);
2551f898e2SVijay Khemka     std::string msgIfce;
2651f898e2SVijay Khemka     std::map<std::string, std::variant<int64_t, double, bool>> msgData;
2751f898e2SVijay Khemka 
2851f898e2SVijay Khemka     sdbpMsg.read(msgIfce, msgData);
2951f898e2SVijay Khemka 
3051f898e2SVijay Khemka     if (msgData.find("Value") != msgData.end())
3151f898e2SVijay Khemka     {
3251f898e2SVijay Khemka         using namespace phosphor::virtualSensor;
3351f898e2SVijay Khemka         VirtualSensor* obj = static_cast<VirtualSensor*>(usrData);
3451f898e2SVijay Khemka         // TODO(openbmc/phosphor-virtual-sensor#1): updateVirtualSensor should
3551f898e2SVijay Khemka         // be changed to take the information we got from the signal, to avoid
3651f898e2SVijay Khemka         // having to do numerous dbus queries.
3751f898e2SVijay Khemka         obj->updateVirtualSensor();
3851f898e2SVijay Khemka     }
3951f898e2SVijay Khemka     return 0;
4051f898e2SVijay Khemka }
4151f898e2SVijay Khemka 
42abcc94faSVijay Khemka namespace phosphor
43abcc94faSVijay Khemka {
44abcc94faSVijay Khemka namespace virtualSensor
45abcc94faSVijay Khemka {
46abcc94faSVijay Khemka 
470ab9d838SLei YU FuncMaxIgnoreNaN<double> VirtualSensor::funcMaxIgnoreNaN;
48*87d35115SLei YU FuncSumIgnoreNaN<double> VirtualSensor::funcSumIgnoreNaN;
490ab9d838SLei YU 
50abcc94faSVijay Khemka void printParams(const VirtualSensor::ParamMap& paramMap)
51abcc94faSVijay Khemka {
52abcc94faSVijay Khemka     for (const auto& p : paramMap)
53abcc94faSVijay Khemka     {
54abcc94faSVijay Khemka         const auto& p1 = p.first;
55abcc94faSVijay Khemka         const auto& p2 = p.second;
56abcc94faSVijay Khemka         auto val = p2->getParamValue();
57fbd7145eSPatrick Williams         debug("Parameter: {PARAM} = {VALUE}", "PARAM", p1, "VALUE", val);
58abcc94faSVijay Khemka     }
59abcc94faSVijay Khemka }
60abcc94faSVijay Khemka 
61abcc94faSVijay Khemka double SensorParam::getParamValue()
62abcc94faSVijay Khemka {
63abcc94faSVijay Khemka     switch (paramType)
64abcc94faSVijay Khemka     {
65abcc94faSVijay Khemka         case constParam:
66abcc94faSVijay Khemka             return value;
67abcc94faSVijay Khemka             break;
687452a867SVijay Khemka         case dbusParam:
697452a867SVijay Khemka             return dbusSensor->getSensorValue();
707452a867SVijay Khemka             break;
71abcc94faSVijay Khemka         default:
72abcc94faSVijay Khemka             throw std::invalid_argument("param type not supported");
73abcc94faSVijay Khemka     }
74abcc94faSVijay Khemka }
75abcc94faSVijay Khemka 
760fcf0e1cSLei YU using AssociationList =
770fcf0e1cSLei YU     std::vector<std::tuple<std::string, std::string, std::string>>;
780fcf0e1cSLei YU 
790fcf0e1cSLei YU AssociationList getAssociationsFromJson(const Json& j)
800fcf0e1cSLei YU {
810fcf0e1cSLei YU     AssociationList assocs{};
820fcf0e1cSLei YU     try
830fcf0e1cSLei YU     {
840fcf0e1cSLei YU         j.get_to(assocs);
850fcf0e1cSLei YU     }
860fcf0e1cSLei YU     catch (const std::exception& ex)
870fcf0e1cSLei YU     {
8882b39c66SPatrick Williams         error("Failed to parse association: {ERROR}", "ERROR", ex);
890fcf0e1cSLei YU     }
900fcf0e1cSLei YU     return assocs;
910fcf0e1cSLei YU }
920fcf0e1cSLei YU 
93e7efe135SRashmica Gupta template <typename U>
94e7efe135SRashmica Gupta struct VariantToNumber
95e7efe135SRashmica Gupta {
96e7efe135SRashmica Gupta     template <typename T>
97e7efe135SRashmica Gupta     U operator()(const T& t) const
98e7efe135SRashmica Gupta     {
99e7efe135SRashmica Gupta         if constexpr (std::is_convertible<T, U>::value)
100e7efe135SRashmica Gupta         {
101e7efe135SRashmica Gupta             return static_cast<U>(t);
102e7efe135SRashmica Gupta         }
103e7efe135SRashmica Gupta         throw std::invalid_argument("Invalid number type in config\n");
104e7efe135SRashmica Gupta     }
105e7efe135SRashmica Gupta };
106e7efe135SRashmica Gupta 
107e7efe135SRashmica Gupta template <typename U>
108e7efe135SRashmica Gupta U getNumberFromConfig(const PropertyMap& map, const std::string& name,
109190f6d06SJiaqing Zhao                       bool required,
110190f6d06SJiaqing Zhao                       U defaultValue = std::numeric_limits<U>::quiet_NaN())
111e7efe135SRashmica Gupta {
112e7efe135SRashmica Gupta     if (auto itr = map.find(name); itr != map.end())
113e7efe135SRashmica Gupta     {
114e7efe135SRashmica Gupta         return std::visit(VariantToNumber<U>(), itr->second);
115e7efe135SRashmica Gupta     }
116e7efe135SRashmica Gupta     else if (required)
117e7efe135SRashmica Gupta     {
11882b39c66SPatrick Williams         error("Required field {NAME} missing in config", "NAME", name);
119e7efe135SRashmica Gupta         throw std::invalid_argument("Required field missing in config");
120e7efe135SRashmica Gupta     }
121190f6d06SJiaqing Zhao     return defaultValue;
122e7efe135SRashmica Gupta }
123e7efe135SRashmica Gupta 
124e7efe135SRashmica Gupta bool isCalculationType(const std::string& interface)
125e7efe135SRashmica Gupta {
126e7efe135SRashmica Gupta     auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
127e7efe135SRashmica Gupta                          interface);
128e7efe135SRashmica Gupta     if (itr != calculationIfaces.end())
129e7efe135SRashmica Gupta     {
130e7efe135SRashmica Gupta         return true;
131e7efe135SRashmica Gupta     }
132e7efe135SRashmica Gupta     return false;
133e7efe135SRashmica Gupta }
134e7efe135SRashmica Gupta 
135e7efe135SRashmica Gupta const std::string getThresholdType(const std::string& direction,
13605b1d417SRashmica Gupta                                    const std::string& severity)
137e7efe135SRashmica Gupta {
138e7efe135SRashmica Gupta     std::string suffix;
139e7efe135SRashmica Gupta 
140e7efe135SRashmica Gupta     if (direction == "less than")
141e7efe135SRashmica Gupta     {
142e7efe135SRashmica Gupta         suffix = "Low";
143e7efe135SRashmica Gupta     }
144e7efe135SRashmica Gupta     else if (direction == "greater than")
145e7efe135SRashmica Gupta     {
146e7efe135SRashmica Gupta         suffix = "High";
147e7efe135SRashmica Gupta     }
148e7efe135SRashmica Gupta     else
149e7efe135SRashmica Gupta     {
150e7efe135SRashmica Gupta         throw std::invalid_argument(
151e7efe135SRashmica Gupta             "Invalid threshold direction specified in entity manager");
152e7efe135SRashmica Gupta     }
15305b1d417SRashmica Gupta     return severity + suffix;
15405b1d417SRashmica Gupta }
15505b1d417SRashmica Gupta 
15605b1d417SRashmica Gupta std::string getSeverityField(const PropertyMap& propertyMap)
15705b1d417SRashmica Gupta {
15805b1d417SRashmica Gupta     static const std::array thresholdTypes{"Warning", "Critical",
15905b1d417SRashmica Gupta                                            "PerformanceLoss", "SoftShutdown",
16005b1d417SRashmica Gupta                                            "HardShutdown"};
16105b1d417SRashmica Gupta 
16205b1d417SRashmica Gupta     std::string severity;
16305b1d417SRashmica Gupta     if (auto itr = propertyMap.find("Severity"); itr != propertyMap.end())
16405b1d417SRashmica Gupta     {
16505b1d417SRashmica Gupta         /* Severity should be a string, but can be an unsigned int */
16605b1d417SRashmica Gupta         if (std::holds_alternative<std::string>(itr->second))
16705b1d417SRashmica Gupta         {
16805b1d417SRashmica Gupta             severity = std::get<std::string>(itr->second);
16905b1d417SRashmica Gupta             if (0 == std::ranges::count(thresholdTypes, severity))
17005b1d417SRashmica Gupta             {
17105b1d417SRashmica Gupta                 throw std::invalid_argument(
17205b1d417SRashmica Gupta                     "Invalid threshold severity specified in entity manager");
17305b1d417SRashmica Gupta             }
17405b1d417SRashmica Gupta         }
17505b1d417SRashmica Gupta         else
17605b1d417SRashmica Gupta         {
1771226f208SPatrick Williams             auto sev = getNumberFromConfig<uint64_t>(propertyMap, "Severity",
1781226f208SPatrick Williams                                                      true);
17905b1d417SRashmica Gupta             /* Checking bounds ourselves so we throw invalid argument on
18005b1d417SRashmica Gupta              * invalid user input */
18105b1d417SRashmica Gupta             if (sev >= thresholdTypes.size())
18205b1d417SRashmica Gupta             {
18305b1d417SRashmica Gupta                 throw std::invalid_argument(
18405b1d417SRashmica Gupta                     "Invalid threshold severity specified in entity manager");
18505b1d417SRashmica Gupta             }
18605b1d417SRashmica Gupta             severity = thresholdTypes.at(sev);
18705b1d417SRashmica Gupta         }
18805b1d417SRashmica Gupta     }
18905b1d417SRashmica Gupta     return severity;
190e7efe135SRashmica Gupta }
191e7efe135SRashmica Gupta 
19291799dbdSTao Lin void parseThresholds(Json& thresholds, const PropertyMap& propertyMap,
19391799dbdSTao Lin                      const std::string& entityInterface = "")
194e7efe135SRashmica Gupta {
195e7efe135SRashmica Gupta     std::string direction;
196e7efe135SRashmica Gupta 
197e7efe135SRashmica Gupta     auto value = getNumberFromConfig<double>(propertyMap, "Value", true);
198e7efe135SRashmica Gupta 
19905b1d417SRashmica Gupta     auto severity = getSeverityField(propertyMap);
20005b1d417SRashmica Gupta 
20105b1d417SRashmica Gupta     if (auto itr = propertyMap.find("Direction"); itr != propertyMap.end())
202e7efe135SRashmica Gupta     {
203e7efe135SRashmica Gupta         direction = std::get<std::string>(itr->second);
204e7efe135SRashmica Gupta     }
205e7efe135SRashmica Gupta 
206e7efe135SRashmica Gupta     auto threshold = getThresholdType(direction, severity);
207e7efe135SRashmica Gupta     thresholds[threshold] = value;
2081dff7dceSRashmica Gupta 
2091226f208SPatrick Williams     auto hysteresis = getNumberFromConfig<double>(propertyMap, "Hysteresis",
2101226f208SPatrick Williams                                                   false);
2111dff7dceSRashmica Gupta     if (hysteresis != std::numeric_limits<double>::quiet_NaN())
2121dff7dceSRashmica Gupta     {
2131dff7dceSRashmica Gupta         thresholds[threshold + "Hysteresis"] = hysteresis;
2141dff7dceSRashmica Gupta     }
21591799dbdSTao Lin 
21691799dbdSTao Lin     if (!entityInterface.empty())
21791799dbdSTao Lin     {
21891799dbdSTao Lin         thresholds[threshold + "Direction"] = entityInterface;
21991799dbdSTao Lin     }
220e7efe135SRashmica Gupta }
221e7efe135SRashmica Gupta 
222e7efe135SRashmica Gupta void VirtualSensor::parseConfigInterface(const PropertyMap& propertyMap,
223e7efe135SRashmica Gupta                                          const std::string& sensorType,
224e7efe135SRashmica Gupta                                          const std::string& interface)
225e7efe135SRashmica Gupta {
226e7efe135SRashmica Gupta     /* Parse sensors / DBus params */
227e7efe135SRashmica Gupta     if (auto itr = propertyMap.find("Sensors"); itr != propertyMap.end())
228e7efe135SRashmica Gupta     {
229e7efe135SRashmica Gupta         auto sensors = std::get<std::vector<std::string>>(itr->second);
230e7efe135SRashmica Gupta         for (auto sensor : sensors)
231e7efe135SRashmica Gupta         {
232e7efe135SRashmica Gupta             std::replace(sensor.begin(), sensor.end(), ' ', '_');
233e7efe135SRashmica Gupta             auto sensorObjPath = sensorDbusPath + sensorType + "/" + sensor;
234e7efe135SRashmica Gupta 
2351226f208SPatrick Williams             auto paramPtr = std::make_unique<SensorParam>(bus, sensorObjPath,
2361226f208SPatrick Williams                                                           this);
237e7efe135SRashmica Gupta             symbols.create_variable(sensor);
238e7efe135SRashmica Gupta             paramMap.emplace(std::move(sensor), std::move(paramPtr));
239e7efe135SRashmica Gupta         }
240e7efe135SRashmica Gupta     }
241e7efe135SRashmica Gupta     /* Get expression string */
242e7efe135SRashmica Gupta     if (!isCalculationType(interface))
243e7efe135SRashmica Gupta     {
244e7efe135SRashmica Gupta         throw std::invalid_argument("Invalid expression in interface");
245e7efe135SRashmica Gupta     }
246e7efe135SRashmica Gupta     exprStr = interface;
247e7efe135SRashmica Gupta 
248e7efe135SRashmica Gupta     /* Get optional min and max input and output values */
249e7efe135SRashmica Gupta     ValueIface::maxValue(
250e7efe135SRashmica Gupta         getNumberFromConfig<double>(propertyMap, "MaxValue", false));
251e7efe135SRashmica Gupta     ValueIface::minValue(
252e7efe135SRashmica Gupta         getNumberFromConfig<double>(propertyMap, "MinValue", false));
253e7efe135SRashmica Gupta     maxValidInput =
254190f6d06SJiaqing Zhao         getNumberFromConfig<double>(propertyMap, "MaxValidInput", false,
255190f6d06SJiaqing Zhao                                     std::numeric_limits<double>::infinity());
256e7efe135SRashmica Gupta     minValidInput =
257190f6d06SJiaqing Zhao         getNumberFromConfig<double>(propertyMap, "MinValidInput", false,
258190f6d06SJiaqing Zhao                                     -std::numeric_limits<double>::infinity());
259e7efe135SRashmica Gupta }
260e7efe135SRashmica Gupta 
261ce675228SMatt Spinler void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
262ce675228SMatt Spinler                                       const std::string& objPath)
263abcc94faSVijay Khemka {
264abcc94faSVijay Khemka     static const Json empty{};
265abcc94faSVijay Khemka 
266abcc94faSVijay Khemka     /* Get threshold values if defined in config */
267abcc94faSVijay Khemka     auto threshold = sensorConfig.value("Threshold", empty);
268f15189e3SMatt Spinler 
2693e99919bSRashmica Gupta     createThresholds(threshold, objPath);
270abcc94faSVijay Khemka 
271f6443742SHarvey Wu     /* Get MaxValue, MinValue setting if defined in config */
272f6443742SHarvey Wu     auto confDesc = sensorConfig.value("Desc", empty);
273f6443742SHarvey Wu     if (auto maxConf = confDesc.find("MaxValue");
274f6443742SHarvey Wu         maxConf != confDesc.end() && maxConf->is_number())
275f6443742SHarvey Wu     {
276f6443742SHarvey Wu         ValueIface::maxValue(maxConf->get<double>());
277f6443742SHarvey Wu     }
278f6443742SHarvey Wu     if (auto minConf = confDesc.find("MinValue");
279f6443742SHarvey Wu         minConf != confDesc.end() && minConf->is_number())
280f6443742SHarvey Wu     {
281f6443742SHarvey Wu         ValueIface::minValue(minConf->get<double>());
282f6443742SHarvey Wu     }
283f6443742SHarvey Wu 
2840fcf0e1cSLei YU     /* Get optional association */
2850fcf0e1cSLei YU     auto assocJson = sensorConfig.value("Associations", empty);
2860fcf0e1cSLei YU     if (!assocJson.empty())
2870fcf0e1cSLei YU     {
2880fcf0e1cSLei YU         auto assocs = getAssociationsFromJson(assocJson);
2890fcf0e1cSLei YU         if (!assocs.empty())
2900fcf0e1cSLei YU         {
2910fcf0e1cSLei YU             associationIface =
2920fcf0e1cSLei YU                 std::make_unique<AssociationObject>(bus, objPath.c_str());
2930fcf0e1cSLei YU             associationIface->associations(assocs);
2940fcf0e1cSLei YU         }
2950fcf0e1cSLei YU     }
2960fcf0e1cSLei YU 
297abcc94faSVijay Khemka     /* Get expression string */
29803c4c8e2SPatrick Williams     static constexpr auto exprKey = "Expression";
29903c4c8e2SPatrick Williams     if (sensorConfig.contains(exprKey))
30003c4c8e2SPatrick Williams     {
301a959678cSPatrick Williams         auto& ref = sensorConfig.at(exprKey);
30203c4c8e2SPatrick Williams         if (ref.is_array())
30303c4c8e2SPatrick Williams         {
30403c4c8e2SPatrick Williams             exprStr = std::string{};
30503c4c8e2SPatrick Williams             for (auto& s : ref)
30603c4c8e2SPatrick Williams             {
30703c4c8e2SPatrick Williams                 exprStr += s;
30803c4c8e2SPatrick Williams             }
30903c4c8e2SPatrick Williams         }
31003c4c8e2SPatrick Williams         else if (ref.is_string())
31103c4c8e2SPatrick Williams         {
31203c4c8e2SPatrick Williams             exprStr = std::string{ref};
31303c4c8e2SPatrick Williams         }
31403c4c8e2SPatrick Williams     }
315abcc94faSVijay Khemka 
316abcc94faSVijay Khemka     /* Get all the parameter listed in configuration */
317abcc94faSVijay Khemka     auto params = sensorConfig.value("Params", empty);
318abcc94faSVijay Khemka 
319abcc94faSVijay Khemka     /* Check for constant parameter */
320abcc94faSVijay Khemka     const auto& consParams = params.value("ConstParam", empty);
321abcc94faSVijay Khemka     if (!consParams.empty())
322abcc94faSVijay Khemka     {
323abcc94faSVijay Khemka         for (auto& j : consParams)
324abcc94faSVijay Khemka         {
325abcc94faSVijay Khemka             if (j.find("ParamName") != j.end())
326abcc94faSVijay Khemka             {
327abcc94faSVijay Khemka                 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
3283ed9a516SVijay Khemka                 std::string name = j["ParamName"];
3293ed9a516SVijay Khemka                 symbols.create_variable(name);
3303ed9a516SVijay Khemka                 paramMap.emplace(std::move(name), std::move(paramPtr));
331abcc94faSVijay Khemka             }
332abcc94faSVijay Khemka             else
333abcc94faSVijay Khemka             {
334abcc94faSVijay Khemka                 /* Invalid configuration */
335abcc94faSVijay Khemka                 throw std::invalid_argument(
336abcc94faSVijay Khemka                     "ParamName not found in configuration");
337abcc94faSVijay Khemka             }
338abcc94faSVijay Khemka         }
339abcc94faSVijay Khemka     }
340abcc94faSVijay Khemka 
3417452a867SVijay Khemka     /* Check for dbus parameter */
3427452a867SVijay Khemka     auto dbusParams = params.value("DbusParam", empty);
3437452a867SVijay Khemka     if (!dbusParams.empty())
3447452a867SVijay Khemka     {
3457452a867SVijay Khemka         for (auto& j : dbusParams)
3467452a867SVijay Khemka         {
3477452a867SVijay Khemka             /* Get parameter dbus sensor descriptor */
3487452a867SVijay Khemka             auto desc = j.value("Desc", empty);
3497452a867SVijay Khemka             if ((!desc.empty()) && (j.find("ParamName") != j.end()))
3507452a867SVijay Khemka             {
3517452a867SVijay Khemka                 std::string sensorType = desc.value("SensorType", "");
3527452a867SVijay Khemka                 std::string name = desc.value("Name", "");
3537452a867SVijay Khemka 
3547452a867SVijay Khemka                 if (!sensorType.empty() && !name.empty())
3557452a867SVijay Khemka                 {
3561204b433SGeorge Liu                     auto path = sensorDbusPath + sensorType + "/" + name;
3577452a867SVijay Khemka 
3581226f208SPatrick Williams                     auto paramPtr = std::make_unique<SensorParam>(bus, path,
3591226f208SPatrick Williams                                                                   this);
3601204b433SGeorge Liu                     std::string paramName = j["ParamName"];
3611204b433SGeorge Liu                     symbols.create_variable(paramName);
3621204b433SGeorge Liu                     paramMap.emplace(std::move(paramName), std::move(paramPtr));
3637452a867SVijay Khemka                 }
3647452a867SVijay Khemka             }
3657452a867SVijay Khemka         }
3667452a867SVijay Khemka     }
367abcc94faSVijay Khemka 
3683ed9a516SVijay Khemka     symbols.add_constants();
3699f1ef4f5SMatt Spinler     symbols.add_package(vecopsPackage);
3700ab9d838SLei YU     symbols.add_function("maxIgnoreNaN", funcMaxIgnoreNaN);
371*87d35115SLei YU     symbols.add_function("sumIgnoreNaN", funcSumIgnoreNaN);
3720ab9d838SLei YU 
3733ed9a516SVijay Khemka     expression.register_symbol_table(symbols);
3743ed9a516SVijay Khemka 
3753ed9a516SVijay Khemka     /* parser from exprtk */
3763ed9a516SVijay Khemka     exprtk::parser<double> parser{};
377ddc6dcd6SMatt Spinler     if (!parser.compile(exprStr, expression))
378ddc6dcd6SMatt Spinler     {
37982b39c66SPatrick Williams         error("Expression compilation failed");
380ddc6dcd6SMatt Spinler 
381ddc6dcd6SMatt Spinler         for (std::size_t i = 0; i < parser.error_count(); ++i)
382ddc6dcd6SMatt Spinler         {
38382b39c66SPatrick Williams             auto err = parser.get_error(i);
38482b39c66SPatrick Williams             error("Error parsing token at {POSITION}: {ERROR}", "POSITION",
38582b39c66SPatrick Williams                   err.token.position, "TYPE",
38682b39c66SPatrick Williams                   exprtk::parser_error::to_str(err.mode), "ERROR",
38782b39c66SPatrick Williams                   err.diagnostic);
388ddc6dcd6SMatt Spinler         }
389ddc6dcd6SMatt Spinler         throw std::runtime_error("Expression compilation failed");
390ddc6dcd6SMatt Spinler     }
3913ed9a516SVijay Khemka 
392abcc94faSVijay Khemka     /* Print all parameters for debug purpose only */
393abcc94faSVijay Khemka     if (DEBUG)
394abcc94faSVijay Khemka         printParams(paramMap);
395abcc94faSVijay Khemka }
396abcc94faSVijay Khemka 
397dc777015STao Lin void VirtualSensor::createAssociation(const std::string& objPath,
398dc777015STao Lin                                       const std::string& entityPath)
399dc777015STao Lin {
400dc777015STao Lin     if (objPath.empty() || entityPath.empty())
401dc777015STao Lin     {
402dc777015STao Lin         return;
403dc777015STao Lin     }
404dc777015STao Lin 
405dc777015STao Lin     std::filesystem::path p(entityPath);
406dc777015STao Lin     auto assocsDbus =
407dc777015STao Lin         AssociationList{{"chassis", "all_sensors", p.parent_path().string()}};
4081226f208SPatrick Williams     associationIface = std::make_unique<AssociationObject>(bus,
4091226f208SPatrick Williams                                                            objPath.c_str());
410dc777015STao Lin     associationIface->associations(assocsDbus);
411dc777015STao Lin }
412dc777015STao Lin 
413e7efe135SRashmica Gupta void VirtualSensor::initVirtualSensor(const InterfaceMap& interfaceMap,
414e7efe135SRashmica Gupta                                       const std::string& objPath,
415e7efe135SRashmica Gupta                                       const std::string& sensorType,
416e7efe135SRashmica Gupta                                       const std::string& calculationIface)
417e7efe135SRashmica Gupta {
418e7efe135SRashmica Gupta     Json thresholds;
4191226f208SPatrick Williams     const std::string vsThresholdsIntf = calculationIface +
4201226f208SPatrick Williams                                          vsThresholdsIfaceSuffix;
421e7efe135SRashmica Gupta 
422e7efe135SRashmica Gupta     for (const auto& [interface, propertyMap] : interfaceMap)
423e7efe135SRashmica Gupta     {
424e7efe135SRashmica Gupta         /* Each threshold is on it's own interface with a number as a suffix
425e7efe135SRashmica Gupta          * eg xyz.openbmc_project.Configuration.ModifiedMedian.Thresholds1 */
426e7efe135SRashmica Gupta         if (interface.find(vsThresholdsIntf) != std::string::npos)
427e7efe135SRashmica Gupta         {
42891799dbdSTao Lin             parseThresholds(thresholds, propertyMap, interface);
429e7efe135SRashmica Gupta         }
430e7efe135SRashmica Gupta         else if (interface == calculationIface)
431e7efe135SRashmica Gupta         {
432e7efe135SRashmica Gupta             parseConfigInterface(propertyMap, sensorType, interface);
433e7efe135SRashmica Gupta         }
434e7efe135SRashmica Gupta     }
435e7efe135SRashmica Gupta 
436e7efe135SRashmica Gupta     createThresholds(thresholds, objPath);
437e7efe135SRashmica Gupta     symbols.add_constants();
438e7efe135SRashmica Gupta     symbols.add_package(vecopsPackage);
439e7efe135SRashmica Gupta     expression.register_symbol_table(symbols);
440e7efe135SRashmica Gupta 
441dc777015STao Lin     createAssociation(objPath, entityPath);
442e7efe135SRashmica Gupta     /* Print all parameters for debug purpose only */
443e7efe135SRashmica Gupta     if (DEBUG)
444e7efe135SRashmica Gupta     {
445e7efe135SRashmica Gupta         printParams(paramMap);
446e7efe135SRashmica Gupta     }
447e7efe135SRashmica Gupta }
448e7efe135SRashmica Gupta 
449abcc94faSVijay Khemka void VirtualSensor::setSensorValue(double value)
450abcc94faSVijay Khemka {
451543bf668SPatrick Williams     value = std::clamp(value, ValueIface::minValue(), ValueIface::maxValue());
452abcc94faSVijay Khemka     ValueIface::value(value);
453abcc94faSVijay Khemka }
454abcc94faSVijay Khemka 
455304fd0e4SRashmica Gupta double VirtualSensor::calculateValue(const std::string& calculation,
456304fd0e4SRashmica Gupta                                      const VirtualSensor::ParamMap& paramMap)
457e7efe135SRashmica Gupta {
458304fd0e4SRashmica Gupta     auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
459304fd0e4SRashmica Gupta                          calculation);
460304fd0e4SRashmica Gupta     if (itr == calculationIfaces.end())
461304fd0e4SRashmica Gupta     {
462e7efe135SRashmica Gupta         return std::numeric_limits<double>::quiet_NaN();
463e7efe135SRashmica Gupta     }
464304fd0e4SRashmica Gupta     else if (calculation == "xyz.openbmc_project.Configuration.ModifiedMedian")
465304fd0e4SRashmica Gupta     {
466304fd0e4SRashmica Gupta         return calculateModifiedMedianValue(paramMap);
467304fd0e4SRashmica Gupta     }
468f6b7e0a4STao Lin     else if (calculation == "xyz.openbmc_project.Configuration.Maximum")
469f6b7e0a4STao Lin     {
470f6b7e0a4STao Lin         return calculateMaximumValue(paramMap);
471f6b7e0a4STao Lin     }
472304fd0e4SRashmica Gupta     return std::numeric_limits<double>::quiet_NaN();
473304fd0e4SRashmica Gupta }
474304fd0e4SRashmica Gupta 
475304fd0e4SRashmica Gupta bool VirtualSensor::sensorInRange(double value)
476304fd0e4SRashmica Gupta {
477304fd0e4SRashmica Gupta     if (value <= this->maxValidInput && value >= this->minValidInput)
478304fd0e4SRashmica Gupta     {
479304fd0e4SRashmica Gupta         return true;
480304fd0e4SRashmica Gupta     }
481304fd0e4SRashmica Gupta     return false;
482304fd0e4SRashmica Gupta }
483e7efe135SRashmica Gupta 
484abcc94faSVijay Khemka void VirtualSensor::updateVirtualSensor()
4853ed9a516SVijay Khemka {
4863ed9a516SVijay Khemka     for (auto& param : paramMap)
4873ed9a516SVijay Khemka     {
4883ed9a516SVijay Khemka         auto& name = param.first;
4893ed9a516SVijay Khemka         auto& data = param.second;
4903ed9a516SVijay Khemka         if (auto var = symbols.get_variable(name))
4913ed9a516SVijay Khemka         {
4923ed9a516SVijay Khemka             var->ref() = data->getParamValue();
4933ed9a516SVijay Khemka         }
4943ed9a516SVijay Khemka         else
4953ed9a516SVijay Khemka         {
4963ed9a516SVijay Khemka             /* Invalid parameter */
4973ed9a516SVijay Khemka             throw std::invalid_argument("ParamName not found in symbols");
4983ed9a516SVijay Khemka         }
4993ed9a516SVijay Khemka     }
5001226f208SPatrick Williams     auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
5011226f208SPatrick Williams                          exprStr);
502304fd0e4SRashmica Gupta     auto val = (itr == calculationIfaces.end())
503304fd0e4SRashmica Gupta                    ? expression.value()
504304fd0e4SRashmica Gupta                    : calculateValue(exprStr, paramMap);
50532a7156bSVijay Khemka 
50632a7156bSVijay Khemka     /* Set sensor value to dbus interface */
5073ed9a516SVijay Khemka     setSensorValue(val);
50832a7156bSVijay Khemka 
5093ed9a516SVijay Khemka     if (DEBUG)
510e7efe135SRashmica Gupta     {
511fbd7145eSPatrick Williams         debug("Sensor {NAME} = {VALUE}", "NAME", this->name, "VALUE", val);
512e7efe135SRashmica Gupta     }
51332a7156bSVijay Khemka 
5148f5e6119SMatt Spinler     /* Check sensor thresholds and log required message */
515b306b03dSMatt Spinler     checkThresholds(val, perfLossIface);
516fdb826d5SPatrick Williams     checkThresholds(val, warningIface);
517fdb826d5SPatrick Williams     checkThresholds(val, criticalIface);
518fdb826d5SPatrick Williams     checkThresholds(val, softShutdownIface);
519fdb826d5SPatrick Williams     checkThresholds(val, hardShutdownIface);
5203ed9a516SVijay Khemka }
521abcc94faSVijay Khemka 
522304fd0e4SRashmica Gupta double VirtualSensor::calculateModifiedMedianValue(
523304fd0e4SRashmica Gupta     const VirtualSensor::ParamMap& paramMap)
524304fd0e4SRashmica Gupta {
525304fd0e4SRashmica Gupta     std::vector<double> values;
526304fd0e4SRashmica Gupta 
527304fd0e4SRashmica Gupta     for (auto& param : paramMap)
528304fd0e4SRashmica Gupta     {
529304fd0e4SRashmica Gupta         auto& name = param.first;
530304fd0e4SRashmica Gupta         if (auto var = symbols.get_variable(name))
531304fd0e4SRashmica Gupta         {
532304fd0e4SRashmica Gupta             if (!sensorInRange(var->ref()))
533304fd0e4SRashmica Gupta             {
534304fd0e4SRashmica Gupta                 continue;
535304fd0e4SRashmica Gupta             }
536304fd0e4SRashmica Gupta             values.push_back(var->ref());
537304fd0e4SRashmica Gupta         }
538304fd0e4SRashmica Gupta     }
539304fd0e4SRashmica Gupta 
540304fd0e4SRashmica Gupta     size_t size = values.size();
541304fd0e4SRashmica Gupta     std::sort(values.begin(), values.end());
542304fd0e4SRashmica Gupta     switch (size)
543304fd0e4SRashmica Gupta     {
544304fd0e4SRashmica Gupta         case 2:
545304fd0e4SRashmica Gupta             /* Choose biggest value */
546304fd0e4SRashmica Gupta             return values.at(1);
547304fd0e4SRashmica Gupta         case 0:
548304fd0e4SRashmica Gupta             return std::numeric_limits<double>::quiet_NaN();
549304fd0e4SRashmica Gupta         default:
550304fd0e4SRashmica Gupta             /* Choose median value */
551304fd0e4SRashmica Gupta             if (size % 2 == 0)
552304fd0e4SRashmica Gupta             {
553304fd0e4SRashmica Gupta                 // Average of the two middle values
554304fd0e4SRashmica Gupta                 return (values.at(size / 2) + values.at(size / 2 - 1)) / 2;
555304fd0e4SRashmica Gupta             }
556304fd0e4SRashmica Gupta             else
557304fd0e4SRashmica Gupta             {
558304fd0e4SRashmica Gupta                 return values.at((size - 1) / 2);
559304fd0e4SRashmica Gupta             }
560304fd0e4SRashmica Gupta     }
561304fd0e4SRashmica Gupta }
562304fd0e4SRashmica Gupta 
563f6b7e0a4STao Lin double VirtualSensor::calculateMaximumValue(
564f6b7e0a4STao Lin     const VirtualSensor::ParamMap& paramMap)
565f6b7e0a4STao Lin {
566f6b7e0a4STao Lin     std::vector<double> values;
567f6b7e0a4STao Lin 
568f6b7e0a4STao Lin     for (auto& param : paramMap)
569f6b7e0a4STao Lin     {
570f6b7e0a4STao Lin         auto& name = param.first;
571f6b7e0a4STao Lin         if (auto var = symbols.get_variable(name))
572f6b7e0a4STao Lin         {
573f6b7e0a4STao Lin             if (!sensorInRange(var->ref()))
574f6b7e0a4STao Lin             {
575f6b7e0a4STao Lin                 continue;
576f6b7e0a4STao Lin             }
577f6b7e0a4STao Lin             values.push_back(var->ref());
578f6b7e0a4STao Lin         }
579f6b7e0a4STao Lin     }
580f6b7e0a4STao Lin     auto maxIt = std::max_element(values.begin(), values.end());
581f6b7e0a4STao Lin     if (maxIt == values.end())
582f6b7e0a4STao Lin     {
583f6b7e0a4STao Lin         return std::numeric_limits<double>::quiet_NaN();
584f6b7e0a4STao Lin     }
585f6b7e0a4STao Lin     return *maxIt;
586f6b7e0a4STao Lin }
587f6b7e0a4STao Lin 
5883e99919bSRashmica Gupta void VirtualSensor::createThresholds(const Json& threshold,
5893e99919bSRashmica Gupta                                      const std::string& objPath)
5903e99919bSRashmica Gupta {
5913e99919bSRashmica Gupta     if (threshold.empty())
5923e99919bSRashmica Gupta     {
5933e99919bSRashmica Gupta         return;
5943e99919bSRashmica Gupta     }
5953e99919bSRashmica Gupta     // Only create the threshold interfaces if
5963e99919bSRashmica Gupta     // at least one of their values is present.
5973e99919bSRashmica Gupta     if (threshold.contains("CriticalHigh") || threshold.contains("CriticalLow"))
5983e99919bSRashmica Gupta     {
5993e99919bSRashmica Gupta         criticalIface =
6003e99919bSRashmica Gupta             std::make_unique<Threshold<CriticalObject>>(bus, objPath.c_str());
6013e99919bSRashmica Gupta 
60291799dbdSTao Lin         if (threshold.contains("CriticalHigh"))
60391799dbdSTao Lin         {
60491799dbdSTao Lin             criticalIface->setEntityInterfaceHigh(
60591799dbdSTao Lin                 threshold.value("CriticalHighDirection", ""));
60691799dbdSTao Lin             if (DEBUG)
60791799dbdSTao Lin             {
60891799dbdSTao Lin                 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
60991799dbdSTao Lin                       "INTF", threshold.value("CriticalHighDirection", ""));
61091799dbdSTao Lin             }
61191799dbdSTao Lin         }
61291799dbdSTao Lin         if (threshold.contains("CriticalLow"))
61391799dbdSTao Lin         {
61491799dbdSTao Lin             criticalIface->setEntityInterfaceLow(
61591799dbdSTao Lin                 threshold.value("CriticalLowDirection", ""));
61691799dbdSTao Lin             if (DEBUG)
61791799dbdSTao Lin             {
61891799dbdSTao Lin                 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
61991799dbdSTao Lin                       "INTF", threshold.value("CriticalLowDirection", ""));
62091799dbdSTao Lin             }
62191799dbdSTao Lin         }
62291799dbdSTao Lin 
62391799dbdSTao Lin         criticalIface->setEntityPath(entityPath);
62491799dbdSTao Lin         if (DEBUG)
62591799dbdSTao Lin         {
62691799dbdSTao Lin             debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath,
62791799dbdSTao Lin                   "PATH", entityPath);
62891799dbdSTao Lin         }
629a291ce1aSMatt Spinler 
630a291ce1aSMatt Spinler         criticalIface->criticalHigh(threshold.value(
631a291ce1aSMatt Spinler             "CriticalHigh", std::numeric_limits<double>::quiet_NaN()));
632a291ce1aSMatt Spinler         criticalIface->criticalLow(threshold.value(
633a291ce1aSMatt Spinler             "CriticalLow", std::numeric_limits<double>::quiet_NaN()));
634a291ce1aSMatt Spinler         criticalIface->setHighHysteresis(
635a291ce1aSMatt Spinler             threshold.value("CriticalHighHysteresis", defaultHysteresis));
636a291ce1aSMatt Spinler         criticalIface->setLowHysteresis(
637a291ce1aSMatt Spinler             threshold.value("CriticalLowHysteresis", defaultHysteresis));
6383e99919bSRashmica Gupta     }
6393e99919bSRashmica Gupta 
6403e99919bSRashmica Gupta     if (threshold.contains("WarningHigh") || threshold.contains("WarningLow"))
6413e99919bSRashmica Gupta     {
6423e99919bSRashmica Gupta         warningIface =
6433e99919bSRashmica Gupta             std::make_unique<Threshold<WarningObject>>(bus, objPath.c_str());
6443e99919bSRashmica Gupta 
64591799dbdSTao Lin         if (threshold.contains("WarningHigh"))
64691799dbdSTao Lin         {
64791799dbdSTao Lin             warningIface->setEntityInterfaceHigh(
64891799dbdSTao Lin                 threshold.value("WarningHighDirection", ""));
64991799dbdSTao Lin             if (DEBUG)
65091799dbdSTao Lin             {
65191799dbdSTao Lin                 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
65291799dbdSTao Lin                       "INTF", threshold.value("WarningHighDirection", ""));
65391799dbdSTao Lin             }
65491799dbdSTao Lin         }
65591799dbdSTao Lin         if (threshold.contains("WarningLow"))
65691799dbdSTao Lin         {
65791799dbdSTao Lin             warningIface->setEntityInterfaceLow(
65891799dbdSTao Lin                 threshold.value("WarningLowDirection", ""));
65991799dbdSTao Lin             if (DEBUG)
66091799dbdSTao Lin             {
66191799dbdSTao Lin                 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
66291799dbdSTao Lin                       "INTF", threshold.value("WarningLowDirection", ""));
66391799dbdSTao Lin             }
66491799dbdSTao Lin         }
66591799dbdSTao Lin 
66691799dbdSTao Lin         warningIface->setEntityPath(entityPath);
66791799dbdSTao Lin         if (DEBUG)
66891799dbdSTao Lin         {
66991799dbdSTao Lin             debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath,
67091799dbdSTao Lin                   "PATH", entityPath);
67191799dbdSTao Lin         }
672a291ce1aSMatt Spinler 
673a291ce1aSMatt Spinler         warningIface->warningHigh(threshold.value(
674a291ce1aSMatt Spinler             "WarningHigh", std::numeric_limits<double>::quiet_NaN()));
675a291ce1aSMatt Spinler         warningIface->warningLow(threshold.value(
676a291ce1aSMatt Spinler             "WarningLow", std::numeric_limits<double>::quiet_NaN()));
677a291ce1aSMatt Spinler         warningIface->setHighHysteresis(
678a291ce1aSMatt Spinler             threshold.value("WarningHighHysteresis", defaultHysteresis));
679a291ce1aSMatt Spinler         warningIface->setLowHysteresis(
680a291ce1aSMatt Spinler             threshold.value("WarningLowHysteresis", defaultHysteresis));
6813e99919bSRashmica Gupta     }
6823e99919bSRashmica Gupta 
6833e99919bSRashmica Gupta     if (threshold.contains("HardShutdownHigh") ||
6843e99919bSRashmica Gupta         threshold.contains("HardShutdownLow"))
6853e99919bSRashmica Gupta     {
6863e99919bSRashmica Gupta         hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>(
6873e99919bSRashmica Gupta             bus, objPath.c_str());
6883e99919bSRashmica Gupta 
6893e99919bSRashmica Gupta         hardShutdownIface->hardShutdownHigh(threshold.value(
6903e99919bSRashmica Gupta             "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
6913e99919bSRashmica Gupta         hardShutdownIface->hardShutdownLow(threshold.value(
6923e99919bSRashmica Gupta             "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
6931dff7dceSRashmica Gupta         hardShutdownIface->setHighHysteresis(
6941dff7dceSRashmica Gupta             threshold.value("HardShutdownHighHysteresis", defaultHysteresis));
6951dff7dceSRashmica Gupta         hardShutdownIface->setLowHysteresis(
6961dff7dceSRashmica Gupta             threshold.value("HardShutdownLowHysteresis", defaultHysteresis));
6973e99919bSRashmica Gupta     }
6983e99919bSRashmica Gupta 
6993e99919bSRashmica Gupta     if (threshold.contains("SoftShutdownHigh") ||
7003e99919bSRashmica Gupta         threshold.contains("SoftShutdownLow"))
7013e99919bSRashmica Gupta     {
7023e99919bSRashmica Gupta         softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>(
7033e99919bSRashmica Gupta             bus, objPath.c_str());
7043e99919bSRashmica Gupta 
7053e99919bSRashmica Gupta         softShutdownIface->softShutdownHigh(threshold.value(
7063e99919bSRashmica Gupta             "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
7073e99919bSRashmica Gupta         softShutdownIface->softShutdownLow(threshold.value(
7083e99919bSRashmica Gupta             "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
7091dff7dceSRashmica Gupta         softShutdownIface->setHighHysteresis(
7101dff7dceSRashmica Gupta             threshold.value("SoftShutdownHighHysteresis", defaultHysteresis));
7111dff7dceSRashmica Gupta         softShutdownIface->setLowHysteresis(
7121dff7dceSRashmica Gupta             threshold.value("SoftShutdownLowHysteresis", defaultHysteresis));
7133e99919bSRashmica Gupta     }
7143e99919bSRashmica Gupta 
7153e99919bSRashmica Gupta     if (threshold.contains("PerformanceLossHigh") ||
7163e99919bSRashmica Gupta         threshold.contains("PerformanceLossLow"))
7173e99919bSRashmica Gupta     {
7183e99919bSRashmica Gupta         perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>(
7193e99919bSRashmica Gupta             bus, objPath.c_str());
7203e99919bSRashmica Gupta 
7213e99919bSRashmica Gupta         perfLossIface->performanceLossHigh(threshold.value(
7223e99919bSRashmica Gupta             "PerformanceLossHigh", std::numeric_limits<double>::quiet_NaN()));
7233e99919bSRashmica Gupta         perfLossIface->performanceLossLow(threshold.value(
7243e99919bSRashmica Gupta             "PerformanceLossLow", std::numeric_limits<double>::quiet_NaN()));
7251dff7dceSRashmica Gupta         perfLossIface->setHighHysteresis(threshold.value(
7261dff7dceSRashmica Gupta             "PerformanceLossHighHysteresis", defaultHysteresis));
7271dff7dceSRashmica Gupta         perfLossIface->setLowHysteresis(
7281dff7dceSRashmica Gupta             threshold.value("PerformanceLossLowHysteresis", defaultHysteresis));
7293e99919bSRashmica Gupta     }
7303e99919bSRashmica Gupta }
7313e99919bSRashmica Gupta 
732e7efe135SRashmica Gupta ManagedObjectType VirtualSensors::getObjectsFromDBus()
733e7efe135SRashmica Gupta {
734e7efe135SRashmica Gupta     ManagedObjectType objects;
735e7efe135SRashmica Gupta 
736e7efe135SRashmica Gupta     try
737e7efe135SRashmica Gupta     {
738f6825b91SNan Zhou         auto method = bus.new_method_call("xyz.openbmc_project.EntityManager",
739f6825b91SNan Zhou                                           "/xyz/openbmc_project/inventory",
740e7efe135SRashmica Gupta                                           "org.freedesktop.DBus.ObjectManager",
741e7efe135SRashmica Gupta                                           "GetManagedObjects");
742e7efe135SRashmica Gupta         auto reply = bus.call(method);
743e7efe135SRashmica Gupta         reply.read(objects);
744e7efe135SRashmica Gupta     }
7458e11cccbSPatrick Williams     catch (const sdbusplus::exception_t& ex)
746e7efe135SRashmica Gupta     {
747e7efe135SRashmica Gupta         // If entity manager isn't running yet, keep going.
748e7efe135SRashmica Gupta         if (std::string("org.freedesktop.DBus.Error.ServiceUnknown") !=
749e7efe135SRashmica Gupta             ex.name())
750e7efe135SRashmica Gupta         {
75171b9c116SMatt Spinler             error("Could not reach entity-manager: {ERROR}", "ERROR", ex);
75271b9c116SMatt Spinler             throw;
753e7efe135SRashmica Gupta         }
754e7efe135SRashmica Gupta     }
755e7efe135SRashmica Gupta 
756e7efe135SRashmica Gupta     return objects;
757e7efe135SRashmica Gupta }
758e7efe135SRashmica Gupta 
7598e11cccbSPatrick Williams void VirtualSensors::propertiesChanged(sdbusplus::message_t& msg)
760e7efe135SRashmica Gupta {
761e7efe135SRashmica Gupta     std::string path;
762e7efe135SRashmica Gupta     PropertyMap properties;
763e7efe135SRashmica Gupta 
764e7efe135SRashmica Gupta     msg.read(path, properties);
765e7efe135SRashmica Gupta 
766e7efe135SRashmica Gupta     /* We get multiple callbacks for one sensor. 'Type' is a required field and
767e7efe135SRashmica Gupta      * is a unique label so use to to only proceed once per sensor */
768e7efe135SRashmica Gupta     if (properties.contains("Type"))
769e7efe135SRashmica Gupta     {
770e7efe135SRashmica Gupta         if (isCalculationType(path))
771e7efe135SRashmica Gupta         {
772e7efe135SRashmica Gupta             createVirtualSensorsFromDBus(path);
773e7efe135SRashmica Gupta         }
774e7efe135SRashmica Gupta     }
775e7efe135SRashmica Gupta }
776e7efe135SRashmica Gupta 
777abcc94faSVijay Khemka /** @brief Parsing Virtual Sensor config JSON file  */
77832dff21bSPatrick Williams Json VirtualSensors::parseConfigFile()
779abcc94faSVijay Khemka {
78032dff21bSPatrick Williams     using path = std::filesystem::path;
78132dff21bSPatrick Williams     auto configFile = []() -> path {
78232dff21bSPatrick Williams         static constexpr auto name = "virtual_sensor_config.json";
78332dff21bSPatrick Williams 
78432dff21bSPatrick Williams         for (auto pathSeg : {std::filesystem::current_path(),
78532dff21bSPatrick Williams                              path{"/var/lib/phosphor-virtual-sensor"},
78632dff21bSPatrick Williams                              path{"/usr/share/phosphor-virtual-sensor"}})
78732dff21bSPatrick Williams         {
78832dff21bSPatrick Williams             auto file = pathSeg / name;
78932dff21bSPatrick Williams             if (std::filesystem::exists(file))
79032dff21bSPatrick Williams             {
79132dff21bSPatrick Williams                 return file;
79232dff21bSPatrick Williams             }
79332dff21bSPatrick Williams         }
79432dff21bSPatrick Williams         return name;
79532dff21bSPatrick Williams     }();
79632dff21bSPatrick Williams 
797abcc94faSVijay Khemka     std::ifstream jsonFile(configFile);
798abcc94faSVijay Khemka     if (!jsonFile.is_open())
799abcc94faSVijay Khemka     {
80082b39c66SPatrick Williams         error("config JSON file {FILENAME} not found", "FILENAME", configFile);
801e7efe135SRashmica Gupta         return {};
802abcc94faSVijay Khemka     }
803abcc94faSVijay Khemka 
804abcc94faSVijay Khemka     auto data = Json::parse(jsonFile, nullptr, false);
805abcc94faSVijay Khemka     if (data.is_discarded())
806abcc94faSVijay Khemka     {
80782b39c66SPatrick Williams         error("config readings JSON parser failure with {FILENAME}", "FILENAME",
80882b39c66SPatrick Williams               configFile);
809abcc94faSVijay Khemka         throw std::exception{};
810abcc94faSVijay Khemka     }
811abcc94faSVijay Khemka 
812abcc94faSVijay Khemka     return data;
813abcc94faSVijay Khemka }
814abcc94faSVijay Khemka 
815e0d371e4SVijay Khemka std::map<std::string, ValueIface::Unit> unitMap = {
816e0d371e4SVijay Khemka     {"temperature", ValueIface::Unit::DegreesC},
817e0d371e4SVijay Khemka     {"fan_tach", ValueIface::Unit::RPMS},
818e0d371e4SVijay Khemka     {"voltage", ValueIface::Unit::Volts},
819e0d371e4SVijay Khemka     {"altitude", ValueIface::Unit::Meters},
820e0d371e4SVijay Khemka     {"current", ValueIface::Unit::Amperes},
821e0d371e4SVijay Khemka     {"power", ValueIface::Unit::Watts},
822e0d371e4SVijay Khemka     {"energy", ValueIface::Unit::Joules},
8232b56ddb3SKumar Thangavel     {"utilization", ValueIface::Unit::Percent},
8244ac7a7f2SRashmica Gupta     {"airflow", ValueIface::Unit::CFM},
8254ac7a7f2SRashmica Gupta     {"pressure", ValueIface::Unit::Pascals}};
826e0d371e4SVijay Khemka 
827e7efe135SRashmica Gupta const std::string getSensorTypeFromUnit(const std::string& unit)
828e7efe135SRashmica Gupta {
829e7efe135SRashmica Gupta     std::string unitPrefix = "xyz.openbmc_project.Sensor.Value.Unit.";
830e7efe135SRashmica Gupta     for (auto [type, unitObj] : unitMap)
831e7efe135SRashmica Gupta     {
832e7efe135SRashmica Gupta         auto unitPath = ValueIface::convertUnitToString(unitObj);
833e7efe135SRashmica Gupta         if (unitPath == (unitPrefix + unit))
834e7efe135SRashmica Gupta         {
835e7efe135SRashmica Gupta             return type;
836e7efe135SRashmica Gupta         }
837e7efe135SRashmica Gupta     }
838e7efe135SRashmica Gupta     return "";
839e7efe135SRashmica Gupta }
840e7efe135SRashmica Gupta 
841e7efe135SRashmica Gupta void VirtualSensors::setupMatches()
842e7efe135SRashmica Gupta {
843e7efe135SRashmica Gupta     /* Already setup */
844e7efe135SRashmica Gupta     if (!this->matches.empty())
845e7efe135SRashmica Gupta     {
846e7efe135SRashmica Gupta         return;
847e7efe135SRashmica Gupta     }
848e7efe135SRashmica Gupta 
849e7efe135SRashmica Gupta     /* Setup matches */
8508e11cccbSPatrick Williams     auto eventHandler = [this](sdbusplus::message_t& message) {
851e7efe135SRashmica Gupta         if (message.is_method_error())
852e7efe135SRashmica Gupta         {
85382b39c66SPatrick Williams             error("Callback method error");
854e7efe135SRashmica Gupta             return;
855e7efe135SRashmica Gupta         }
856e7efe135SRashmica Gupta         this->propertiesChanged(message);
857e7efe135SRashmica Gupta     };
858e7efe135SRashmica Gupta 
859e7efe135SRashmica Gupta     for (const char* iface : calculationIfaces)
860e7efe135SRashmica Gupta     {
8618e11cccbSPatrick Williams         auto match = std::make_unique<sdbusplus::bus::match_t>(
862e7efe135SRashmica Gupta             bus,
863e7efe135SRashmica Gupta             sdbusplus::bus::match::rules::propertiesChangedNamespace(
864e7efe135SRashmica Gupta                 "/xyz/openbmc_project/inventory", iface),
865e7efe135SRashmica Gupta             eventHandler);
866e7efe135SRashmica Gupta         this->matches.emplace_back(std::move(match));
867e7efe135SRashmica Gupta     }
868e7efe135SRashmica Gupta }
869e7efe135SRashmica Gupta 
870e7efe135SRashmica Gupta void VirtualSensors::createVirtualSensorsFromDBus(
871e7efe135SRashmica Gupta     const std::string& calculationIface)
872e7efe135SRashmica Gupta {
873e7efe135SRashmica Gupta     if (calculationIface.empty())
874e7efe135SRashmica Gupta     {
87582b39c66SPatrick Williams         error("No calculation type supplied");
876e7efe135SRashmica Gupta         return;
877e7efe135SRashmica Gupta     }
878e7efe135SRashmica Gupta     auto objects = getObjectsFromDBus();
879e7efe135SRashmica Gupta 
880e7efe135SRashmica Gupta     /* Get virtual sensors config data */
881e7efe135SRashmica Gupta     for (const auto& [path, interfaceMap] : objects)
882e7efe135SRashmica Gupta     {
883e7efe135SRashmica Gupta         /* Find Virtual Sensor interfaces */
8842db8d41fSGeorge Liu         auto intfIter = interfaceMap.find(calculationIface);
8852db8d41fSGeorge Liu         if (intfIter == interfaceMap.end())
886e7efe135SRashmica Gupta         {
887e7efe135SRashmica Gupta             continue;
888e7efe135SRashmica Gupta         }
8892db8d41fSGeorge Liu 
8902db8d41fSGeorge Liu         std::string name = path.filename();
891e7efe135SRashmica Gupta         if (name.empty())
892e7efe135SRashmica Gupta         {
89382b39c66SPatrick Williams             error("Virtual Sensor name not found in entity manager config");
894e7efe135SRashmica Gupta             continue;
895e7efe135SRashmica Gupta         }
896e7efe135SRashmica Gupta         if (virtualSensorsMap.contains(name))
897e7efe135SRashmica Gupta         {
89882b39c66SPatrick Williams             error("A virtual sensor named {NAME} already exists", "NAME", name);
899e7efe135SRashmica Gupta             continue;
900e7efe135SRashmica Gupta         }
901e7efe135SRashmica Gupta 
902e7efe135SRashmica Gupta         /* Extract the virtual sensor type as we need this to initialize the
903e7efe135SRashmica Gupta          * sensor */
9042db8d41fSGeorge Liu         std::string sensorType, sensorUnit;
9052db8d41fSGeorge Liu         auto propertyMap = intfIter->second;
9062db8d41fSGeorge Liu         auto proIter = propertyMap.find("Units");
9072db8d41fSGeorge Liu         if (proIter != propertyMap.end())
908e7efe135SRashmica Gupta         {
9092db8d41fSGeorge Liu             sensorUnit = std::get<std::string>(proIter->second);
910e7efe135SRashmica Gupta         }
911e7efe135SRashmica Gupta         sensorType = getSensorTypeFromUnit(sensorUnit);
912e7efe135SRashmica Gupta         if (sensorType.empty())
913e7efe135SRashmica Gupta         {
91482b39c66SPatrick Williams             error("Sensor unit type {TYPE} is not supported", "TYPE",
91582b39c66SPatrick Williams                   sensorUnit);
916e7efe135SRashmica Gupta             continue;
917e7efe135SRashmica Gupta         }
918e7efe135SRashmica Gupta 
919e7efe135SRashmica Gupta         try
920e7efe135SRashmica Gupta         {
9212db8d41fSGeorge Liu             auto objpath = static_cast<std::string>(path);
922e7efe135SRashmica Gupta             auto virtObjPath = sensorDbusPath + sensorType + "/" + name;
923e7efe135SRashmica Gupta 
924e7efe135SRashmica Gupta             auto virtualSensorPtr = std::make_unique<VirtualSensor>(
925e7efe135SRashmica Gupta                 bus, virtObjPath.c_str(), interfaceMap, name, sensorType,
926dc777015STao Lin                 calculationIface, objpath);
92782b39c66SPatrick Williams             info("Added a new virtual sensor: {NAME} {TYPE}", "NAME", name,
92882b39c66SPatrick Williams                  "TYPE", sensorType);
929e7efe135SRashmica Gupta             virtualSensorPtr->updateVirtualSensor();
930e7efe135SRashmica Gupta 
931e7efe135SRashmica Gupta             /* Initialize unit value for virtual sensor */
932e7efe135SRashmica Gupta             virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
933e7efe135SRashmica Gupta             virtualSensorPtr->emit_object_added();
934e7efe135SRashmica Gupta 
935e7efe135SRashmica Gupta             virtualSensorsMap.emplace(name, std::move(virtualSensorPtr));
936e7efe135SRashmica Gupta 
937e7efe135SRashmica Gupta             /* Setup match for interfaces removed */
9381226f208SPatrick Williams             auto intfRemoved =
9391226f208SPatrick Williams                 [this, objpath, name](sdbusplus::message_t& message) {
940e7efe135SRashmica Gupta                 if (!virtualSensorsMap.contains(name))
941e7efe135SRashmica Gupta                 {
942e7efe135SRashmica Gupta                     return;
943e7efe135SRashmica Gupta                 }
944e7efe135SRashmica Gupta                 sdbusplus::message::object_path path;
945e7efe135SRashmica Gupta                 message.read(path);
946e7efe135SRashmica Gupta                 if (static_cast<const std::string&>(path) == objpath)
947e7efe135SRashmica Gupta                 {
94882b39c66SPatrick Williams                     info("Removed a virtual sensor: {NAME}", "NAME", name);
949e7efe135SRashmica Gupta                     virtualSensorsMap.erase(name);
950e7efe135SRashmica Gupta                 }
951e7efe135SRashmica Gupta             };
9528e11cccbSPatrick Williams             auto matchOnRemove = std::make_unique<sdbusplus::bus::match_t>(
953e7efe135SRashmica Gupta                 bus,
954e7efe135SRashmica Gupta                 sdbusplus::bus::match::rules::interfacesRemoved() +
955e7efe135SRashmica Gupta                     sdbusplus::bus::match::rules::argNpath(0, objpath),
956e7efe135SRashmica Gupta                 intfRemoved);
957e7efe135SRashmica Gupta             /* TODO: slight race condition here. Check that the config still
958e7efe135SRashmica Gupta              * exists */
959e7efe135SRashmica Gupta             this->matches.emplace_back(std::move(matchOnRemove));
960e7efe135SRashmica Gupta         }
961dac2663cSPatrick Williams         catch (const std::invalid_argument& ia)
962e7efe135SRashmica Gupta         {
96382b39c66SPatrick Williams             error("Failed to set up virtual sensor: {ERROR}", "ERROR", ia);
964e7efe135SRashmica Gupta         }
965e7efe135SRashmica Gupta     }
966e7efe135SRashmica Gupta }
967e7efe135SRashmica Gupta 
968abcc94faSVijay Khemka void VirtualSensors::createVirtualSensors()
969abcc94faSVijay Khemka {
970abcc94faSVijay Khemka     static const Json empty{};
971abcc94faSVijay Khemka 
97232dff21bSPatrick Williams     auto data = parseConfigFile();
973e7efe135SRashmica Gupta 
974abcc94faSVijay Khemka     // print values
975abcc94faSVijay Khemka     if (DEBUG)
976e7efe135SRashmica Gupta     {
977fbd7145eSPatrick Williams         debug("JSON: {JSON}", "JSON", data.dump());
978e7efe135SRashmica Gupta     }
979abcc94faSVijay Khemka 
980abcc94faSVijay Khemka     /* Get virtual sensors  config data */
981abcc94faSVijay Khemka     for (const auto& j : data)
982abcc94faSVijay Khemka     {
983abcc94faSVijay Khemka         auto desc = j.value("Desc", empty);
984abcc94faSVijay Khemka         if (!desc.empty())
985abcc94faSVijay Khemka         {
986e7efe135SRashmica Gupta             if (desc.value("Config", "") == "D-Bus")
987e7efe135SRashmica Gupta             {
988e7efe135SRashmica Gupta                 /* Look on D-Bus for a virtual sensor config. Set up matches
989e7efe135SRashmica Gupta                  * first because the configs may not be on D-Bus yet and we
990e7efe135SRashmica Gupta                  * don't want to miss them */
991e7efe135SRashmica Gupta                 setupMatches();
992e7efe135SRashmica Gupta 
993e7efe135SRashmica Gupta                 if (desc.contains("Type"))
994e7efe135SRashmica Gupta                 {
99582b39c66SPatrick Williams                     auto type = desc.value("Type", "");
99682b39c66SPatrick Williams                     auto path = "xyz.openbmc_project.Configuration." + type;
99782b39c66SPatrick Williams 
998e7efe135SRashmica Gupta                     if (!isCalculationType(path))
999e7efe135SRashmica Gupta                     {
100082b39c66SPatrick Williams                         error("Invalid calculation type {TYPE} supplied.",
100182b39c66SPatrick Williams                               "TYPE", type);
1002e7efe135SRashmica Gupta                         continue;
1003e7efe135SRashmica Gupta                     }
1004e7efe135SRashmica Gupta                     createVirtualSensorsFromDBus(path);
1005e7efe135SRashmica Gupta                 }
1006e7efe135SRashmica Gupta                 continue;
1007e7efe135SRashmica Gupta             }
1008e7efe135SRashmica Gupta 
1009abcc94faSVijay Khemka             std::string sensorType = desc.value("SensorType", "");
1010abcc94faSVijay Khemka             std::string name = desc.value("Name", "");
1011665a0a29SRashmica Gupta             std::replace(name.begin(), name.end(), ' ', '_');
1012abcc94faSVijay Khemka 
1013abcc94faSVijay Khemka             if (!name.empty() && !sensorType.empty())
1014abcc94faSVijay Khemka             {
1015e0d371e4SVijay Khemka                 if (unitMap.find(sensorType) == unitMap.end())
1016e0d371e4SVijay Khemka                 {
101782b39c66SPatrick Williams                     error("Sensor type {TYPE} is not supported", "TYPE",
101882b39c66SPatrick Williams                           sensorType);
1019e0d371e4SVijay Khemka                 }
1020e0d371e4SVijay Khemka                 else
1021e0d371e4SVijay Khemka                 {
102267d8b9d2SRashmica Gupta                     if (virtualSensorsMap.find(name) != virtualSensorsMap.end())
102367d8b9d2SRashmica Gupta                     {
102482b39c66SPatrick Williams                         error("A virtual sensor named {NAME} already exists",
102582b39c66SPatrick Williams                               "NAME", name);
102667d8b9d2SRashmica Gupta                         continue;
102767d8b9d2SRashmica Gupta                     }
1028862c3d1eSRashmica Gupta                     auto objPath = sensorDbusPath + sensorType + "/" + name;
1029abcc94faSVijay Khemka 
103032a7156bSVijay Khemka                     auto virtualSensorPtr = std::make_unique<VirtualSensor>(
103132a7156bSVijay Khemka                         bus, objPath.c_str(), j, name);
1032abcc94faSVijay Khemka 
103382b39c66SPatrick Williams                     info("Added a new virtual sensor: {NAME}", "NAME", name);
10343ed9a516SVijay Khemka                     virtualSensorPtr->updateVirtualSensor();
1035e0d371e4SVijay Khemka 
1036e0d371e4SVijay Khemka                     /* Initialize unit value for virtual sensor */
1037e0d371e4SVijay Khemka                     virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
1038a2fa63a6SRashmica Gupta                     virtualSensorPtr->emit_object_added();
1039e0d371e4SVijay Khemka 
10403ed9a516SVijay Khemka                     virtualSensorsMap.emplace(std::move(name),
10413ed9a516SVijay Khemka                                               std::move(virtualSensorPtr));
1042abcc94faSVijay Khemka                 }
1043e0d371e4SVijay Khemka             }
1044abcc94faSVijay Khemka             else
1045abcc94faSVijay Khemka             {
104682b39c66SPatrick Williams                 error(
104782b39c66SPatrick Williams                     "Sensor type ({TYPE}) or name ({NAME}) not found in config file",
104882b39c66SPatrick Williams                     "NAME", name, "TYPE", sensorType);
1049abcc94faSVijay Khemka             }
1050abcc94faSVijay Khemka         }
1051abcc94faSVijay Khemka         else
1052abcc94faSVijay Khemka         {
105382b39c66SPatrick Williams             error("Descriptor for new virtual sensor not found in config file");
1054abcc94faSVijay Khemka         }
1055abcc94faSVijay Khemka     }
1056abcc94faSVijay Khemka }
1057abcc94faSVijay Khemka 
1058abcc94faSVijay Khemka } // namespace virtualSensor
1059abcc94faSVijay Khemka } // namespace phosphor
1060abcc94faSVijay Khemka 
1061abcc94faSVijay Khemka /**
1062abcc94faSVijay Khemka  * @brief Main
1063abcc94faSVijay Khemka  */
1064abcc94faSVijay Khemka int main()
1065abcc94faSVijay Khemka {
1066abcc94faSVijay Khemka     // Get a handle to system dbus
1067abcc94faSVijay Khemka     auto bus = sdbusplus::bus::new_default();
1068abcc94faSVijay Khemka 
10696c19e7d2SMatt Spinler     // Add the ObjectManager interface
1070f7ec40aaSEd Tanous     sdbusplus::server::manager_t objManager(bus,
1071f7ec40aaSEd Tanous                                             "/xyz/openbmc_project/sensors");
10726c19e7d2SMatt Spinler 
1073abcc94faSVijay Khemka     // Create an virtual sensors object
1074abcc94faSVijay Khemka     phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
1075abcc94faSVijay Khemka 
1076abcc94faSVijay Khemka     // Request service bus name
107794921490SGeorge Liu     bus.request_name("xyz.openbmc_project.VirtualSensor");
1078abcc94faSVijay Khemka 
1079e667239dSPatrick Williams     // Run the dbus loop.
1080e667239dSPatrick Williams     bus.process_loop();
1081abcc94faSVijay Khemka 
1082abcc94faSVijay Khemka     return 0;
1083abcc94faSVijay Khemka }
1084