1abcc94faSVijay Khemka #include "virtualSensor.hpp"
2abcc94faSVijay Khemka 
3*7f41a0d4SGeorge Liu #include "calculate.hpp"
4*7f41a0d4SGeorge Liu 
582b39c66SPatrick Williams #include <phosphor-logging/lg2.hpp>
6abcc94faSVijay Khemka 
7abcc94faSVijay Khemka #include <fstream>
8abcc94faSVijay Khemka 
9abcc94faSVijay Khemka static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/";
10e7efe135SRashmica Gupta static constexpr auto vsThresholdsIfaceSuffix = ".Thresholds";
111dff7dceSRashmica Gupta static constexpr auto defaultHysteresis = 0;
12abcc94faSVijay Khemka 
1382b39c66SPatrick Williams PHOSPHOR_LOG2_USING_WITH_FLAGS;
14abcc94faSVijay Khemka 
15f2e94221STao Lin namespace phosphor::virtual_sensor
16abcc94faSVijay Khemka {
17abcc94faSVijay Khemka 
180ab9d838SLei YU FuncMaxIgnoreNaN<double> VirtualSensor::funcMaxIgnoreNaN;
1987d35115SLei YU FuncSumIgnoreNaN<double> VirtualSensor::funcSumIgnoreNaN;
20c77b6b3fSLei YU FuncIfNan<double> VirtualSensor::funcIfNan;
210ab9d838SLei YU 
printParams(const VirtualSensor::ParamMap & paramMap)22abcc94faSVijay Khemka void printParams(const VirtualSensor::ParamMap& paramMap)
23abcc94faSVijay Khemka {
24abcc94faSVijay Khemka     for (const auto& p : paramMap)
25abcc94faSVijay Khemka     {
26abcc94faSVijay Khemka         const auto& p1 = p.first;
27abcc94faSVijay Khemka         const auto& p2 = p.second;
28abcc94faSVijay Khemka         auto val = p2->getParamValue();
29fbd7145eSPatrick Williams         debug("Parameter: {PARAM} = {VALUE}", "PARAM", p1, "VALUE", val);
30abcc94faSVijay Khemka     }
31abcc94faSVijay Khemka }
32abcc94faSVijay Khemka 
getParamValue()33abcc94faSVijay Khemka double SensorParam::getParamValue()
34abcc94faSVijay Khemka {
35abcc94faSVijay Khemka     switch (paramType)
36abcc94faSVijay Khemka     {
37abcc94faSVijay Khemka         case constParam:
38abcc94faSVijay Khemka             return value;
39abcc94faSVijay Khemka             break;
407452a867SVijay Khemka         case dbusParam:
417452a867SVijay Khemka             return dbusSensor->getSensorValue();
427452a867SVijay Khemka             break;
43abcc94faSVijay Khemka         default:
44abcc94faSVijay Khemka             throw std::invalid_argument("param type not supported");
45abcc94faSVijay Khemka     }
46abcc94faSVijay Khemka }
47abcc94faSVijay Khemka 
480fcf0e1cSLei YU using AssociationList =
490fcf0e1cSLei YU     std::vector<std::tuple<std::string, std::string, std::string>>;
500fcf0e1cSLei YU 
getAssociationsFromJson(const Json & j)510fcf0e1cSLei YU AssociationList getAssociationsFromJson(const Json& j)
520fcf0e1cSLei YU {
530fcf0e1cSLei YU     AssociationList assocs{};
540fcf0e1cSLei YU     try
550fcf0e1cSLei YU     {
560fcf0e1cSLei YU         j.get_to(assocs);
570fcf0e1cSLei YU     }
580fcf0e1cSLei YU     catch (const std::exception& ex)
590fcf0e1cSLei YU     {
6082b39c66SPatrick Williams         error("Failed to parse association: {ERROR}", "ERROR", ex);
610fcf0e1cSLei YU     }
620fcf0e1cSLei YU     return assocs;
630fcf0e1cSLei YU }
640fcf0e1cSLei YU 
65e7efe135SRashmica Gupta template <typename U>
66e7efe135SRashmica Gupta struct VariantToNumber
67e7efe135SRashmica Gupta {
68e7efe135SRashmica Gupta     template <typename T>
operator ()phosphor::virtual_sensor::VariantToNumber69e7efe135SRashmica Gupta     U operator()(const T& t) const
70e7efe135SRashmica Gupta     {
71e7efe135SRashmica Gupta         if constexpr (std::is_convertible<T, U>::value)
72e7efe135SRashmica Gupta         {
73e7efe135SRashmica Gupta             return static_cast<U>(t);
74e7efe135SRashmica Gupta         }
75e7efe135SRashmica Gupta         throw std::invalid_argument("Invalid number type in config\n");
76e7efe135SRashmica Gupta     }
77e7efe135SRashmica Gupta };
78e7efe135SRashmica Gupta 
79e7efe135SRashmica Gupta template <typename U>
getNumberFromConfig(const PropertyMap & map,const std::string & name,bool required,U defaultValue=std::numeric_limits<U>::quiet_NaN ())80e7efe135SRashmica Gupta U getNumberFromConfig(const PropertyMap& map, const std::string& name,
81190f6d06SJiaqing Zhao                       bool required,
82190f6d06SJiaqing Zhao                       U defaultValue = std::numeric_limits<U>::quiet_NaN())
83e7efe135SRashmica Gupta {
84e7efe135SRashmica Gupta     if (auto itr = map.find(name); itr != map.end())
85e7efe135SRashmica Gupta     {
86e7efe135SRashmica Gupta         return std::visit(VariantToNumber<U>(), itr->second);
87e7efe135SRashmica Gupta     }
88e7efe135SRashmica Gupta     else if (required)
89e7efe135SRashmica Gupta     {
9082b39c66SPatrick Williams         error("Required field {NAME} missing in config", "NAME", name);
91e7efe135SRashmica Gupta         throw std::invalid_argument("Required field missing in config");
92e7efe135SRashmica Gupta     }
93190f6d06SJiaqing Zhao     return defaultValue;
94e7efe135SRashmica Gupta }
95e7efe135SRashmica Gupta 
getThresholdType(const std::string & direction,const std::string & severity)96e7efe135SRashmica Gupta const std::string getThresholdType(const std::string& direction,
9705b1d417SRashmica Gupta                                    const std::string& severity)
98e7efe135SRashmica Gupta {
99e7efe135SRashmica Gupta     std::string suffix;
100e7efe135SRashmica Gupta 
101e7efe135SRashmica Gupta     if (direction == "less than")
102e7efe135SRashmica Gupta     {
103e7efe135SRashmica Gupta         suffix = "Low";
104e7efe135SRashmica Gupta     }
105e7efe135SRashmica Gupta     else if (direction == "greater than")
106e7efe135SRashmica Gupta     {
107e7efe135SRashmica Gupta         suffix = "High";
108e7efe135SRashmica Gupta     }
109e7efe135SRashmica Gupta     else
110e7efe135SRashmica Gupta     {
111e7efe135SRashmica Gupta         throw std::invalid_argument(
112e7efe135SRashmica Gupta             "Invalid threshold direction specified in entity manager");
113e7efe135SRashmica Gupta     }
11405b1d417SRashmica Gupta     return severity + suffix;
11505b1d417SRashmica Gupta }
11605b1d417SRashmica Gupta 
getSeverityField(const PropertyMap & propertyMap)11705b1d417SRashmica Gupta std::string getSeverityField(const PropertyMap& propertyMap)
11805b1d417SRashmica Gupta {
119150d5f6aSPatrick Williams     static const std::array thresholdTypes{
120150d5f6aSPatrick Williams         "Warning", "Critical", "PerformanceLoss", "SoftShutdown",
12105b1d417SRashmica Gupta         "HardShutdown"};
12205b1d417SRashmica Gupta 
12305b1d417SRashmica Gupta     std::string severity;
12405b1d417SRashmica Gupta     if (auto itr = propertyMap.find("Severity"); itr != propertyMap.end())
12505b1d417SRashmica Gupta     {
12605b1d417SRashmica Gupta         /* Severity should be a string, but can be an unsigned int */
12705b1d417SRashmica Gupta         if (std::holds_alternative<std::string>(itr->second))
12805b1d417SRashmica Gupta         {
12905b1d417SRashmica Gupta             severity = std::get<std::string>(itr->second);
13005b1d417SRashmica Gupta             if (0 == std::ranges::count(thresholdTypes, severity))
13105b1d417SRashmica Gupta             {
13205b1d417SRashmica Gupta                 throw std::invalid_argument(
13305b1d417SRashmica Gupta                     "Invalid threshold severity specified in entity manager");
13405b1d417SRashmica Gupta             }
13505b1d417SRashmica Gupta         }
13605b1d417SRashmica Gupta         else
13705b1d417SRashmica Gupta         {
138150d5f6aSPatrick Williams             auto sev =
139150d5f6aSPatrick Williams                 getNumberFromConfig<uint64_t>(propertyMap, "Severity", true);
14005b1d417SRashmica Gupta             /* Checking bounds ourselves so we throw invalid argument on
14105b1d417SRashmica Gupta              * invalid user input */
14205b1d417SRashmica Gupta             if (sev >= thresholdTypes.size())
14305b1d417SRashmica Gupta             {
14405b1d417SRashmica Gupta                 throw std::invalid_argument(
14505b1d417SRashmica Gupta                     "Invalid threshold severity specified in entity manager");
14605b1d417SRashmica Gupta             }
14705b1d417SRashmica Gupta             severity = thresholdTypes.at(sev);
14805b1d417SRashmica Gupta         }
14905b1d417SRashmica Gupta     }
15005b1d417SRashmica Gupta     return severity;
151e7efe135SRashmica Gupta }
152e7efe135SRashmica Gupta 
parseThresholds(Json & thresholds,const PropertyMap & propertyMap,const std::string & entityInterface="")15391799dbdSTao Lin void parseThresholds(Json& thresholds, const PropertyMap& propertyMap,
15491799dbdSTao Lin                      const std::string& entityInterface = "")
155e7efe135SRashmica Gupta {
156e7efe135SRashmica Gupta     std::string direction;
157e7efe135SRashmica Gupta 
158e7efe135SRashmica Gupta     auto value = getNumberFromConfig<double>(propertyMap, "Value", true);
159e7efe135SRashmica Gupta 
16005b1d417SRashmica Gupta     auto severity = getSeverityField(propertyMap);
16105b1d417SRashmica Gupta 
16205b1d417SRashmica Gupta     if (auto itr = propertyMap.find("Direction"); itr != propertyMap.end())
163e7efe135SRashmica Gupta     {
164e7efe135SRashmica Gupta         direction = std::get<std::string>(itr->second);
165e7efe135SRashmica Gupta     }
166e7efe135SRashmica Gupta 
167e7efe135SRashmica Gupta     auto threshold = getThresholdType(direction, severity);
168e7efe135SRashmica Gupta     thresholds[threshold] = value;
1691dff7dceSRashmica Gupta 
170150d5f6aSPatrick Williams     auto hysteresis =
171150d5f6aSPatrick Williams         getNumberFromConfig<double>(propertyMap, "Hysteresis", false);
1721dff7dceSRashmica Gupta     if (hysteresis != std::numeric_limits<double>::quiet_NaN())
1731dff7dceSRashmica Gupta     {
1741dff7dceSRashmica Gupta         thresholds[threshold + "Hysteresis"] = hysteresis;
1751dff7dceSRashmica Gupta     }
17691799dbdSTao Lin 
17791799dbdSTao Lin     if (!entityInterface.empty())
17891799dbdSTao Lin     {
17991799dbdSTao Lin         thresholds[threshold + "Direction"] = entityInterface;
18091799dbdSTao Lin     }
181e7efe135SRashmica Gupta }
182e7efe135SRashmica Gupta 
parseConfigInterface(const PropertyMap & propertyMap,const std::string & sensorType,const std::string & interface)183e7efe135SRashmica Gupta void VirtualSensor::parseConfigInterface(const PropertyMap& propertyMap,
184e7efe135SRashmica Gupta                                          const std::string& sensorType,
185e7efe135SRashmica Gupta                                          const std::string& interface)
186e7efe135SRashmica Gupta {
187e7efe135SRashmica Gupta     /* Parse sensors / DBus params */
188e7efe135SRashmica Gupta     if (auto itr = propertyMap.find("Sensors"); itr != propertyMap.end())
189e7efe135SRashmica Gupta     {
190e7efe135SRashmica Gupta         auto sensors = std::get<std::vector<std::string>>(itr->second);
191e7efe135SRashmica Gupta         for (auto sensor : sensors)
192e7efe135SRashmica Gupta         {
193e7efe135SRashmica Gupta             std::replace(sensor.begin(), sensor.end(), ' ', '_');
194e7efe135SRashmica Gupta             auto sensorObjPath = sensorDbusPath + sensorType + "/" + sensor;
195e7efe135SRashmica Gupta 
196150d5f6aSPatrick Williams             auto paramPtr =
197150d5f6aSPatrick Williams                 std::make_unique<SensorParam>(bus, sensorObjPath, *this);
198e7efe135SRashmica Gupta             symbols.create_variable(sensor);
199e7efe135SRashmica Gupta             paramMap.emplace(std::move(sensor), std::move(paramPtr));
200e7efe135SRashmica Gupta         }
201e7efe135SRashmica Gupta     }
202e7efe135SRashmica Gupta     /* Get expression string */
203*7f41a0d4SGeorge Liu     if (!calculationIfaces.contains(interface))
204e7efe135SRashmica Gupta     {
205e7efe135SRashmica Gupta         throw std::invalid_argument("Invalid expression in interface");
206e7efe135SRashmica Gupta     }
207e7efe135SRashmica Gupta     exprStr = interface;
208e7efe135SRashmica Gupta 
209e7efe135SRashmica Gupta     /* Get optional min and max input and output values */
210e7efe135SRashmica Gupta     ValueIface::maxValue(
211e7efe135SRashmica Gupta         getNumberFromConfig<double>(propertyMap, "MaxValue", false));
212e7efe135SRashmica Gupta     ValueIface::minValue(
213e7efe135SRashmica Gupta         getNumberFromConfig<double>(propertyMap, "MinValue", false));
214e7efe135SRashmica Gupta     maxValidInput =
215190f6d06SJiaqing Zhao         getNumberFromConfig<double>(propertyMap, "MaxValidInput", false,
216190f6d06SJiaqing Zhao                                     std::numeric_limits<double>::infinity());
217e7efe135SRashmica Gupta     minValidInput =
218190f6d06SJiaqing Zhao         getNumberFromConfig<double>(propertyMap, "MinValidInput", false,
219190f6d06SJiaqing Zhao                                     -std::numeric_limits<double>::infinity());
220e7efe135SRashmica Gupta }
221e7efe135SRashmica Gupta 
initVirtualSensor(const Json & sensorConfig,const std::string & objPath)222ce675228SMatt Spinler void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
223ce675228SMatt Spinler                                       const std::string& objPath)
224abcc94faSVijay Khemka {
225abcc94faSVijay Khemka     static const Json empty{};
226abcc94faSVijay Khemka 
227abcc94faSVijay Khemka     /* Get threshold values if defined in config */
228abcc94faSVijay Khemka     auto threshold = sensorConfig.value("Threshold", empty);
229f15189e3SMatt Spinler 
2303e99919bSRashmica Gupta     createThresholds(threshold, objPath);
231abcc94faSVijay Khemka 
232f6443742SHarvey Wu     /* Get MaxValue, MinValue setting if defined in config */
233f6443742SHarvey Wu     auto confDesc = sensorConfig.value("Desc", empty);
234f6443742SHarvey Wu     if (auto maxConf = confDesc.find("MaxValue");
235f6443742SHarvey Wu         maxConf != confDesc.end() && maxConf->is_number())
236f6443742SHarvey Wu     {
237f6443742SHarvey Wu         ValueIface::maxValue(maxConf->get<double>());
238f6443742SHarvey Wu     }
239f6443742SHarvey Wu     if (auto minConf = confDesc.find("MinValue");
240f6443742SHarvey Wu         minConf != confDesc.end() && minConf->is_number())
241f6443742SHarvey Wu     {
242f6443742SHarvey Wu         ValueIface::minValue(minConf->get<double>());
243f6443742SHarvey Wu     }
244f6443742SHarvey Wu 
2450fcf0e1cSLei YU     /* Get optional association */
2460fcf0e1cSLei YU     auto assocJson = sensorConfig.value("Associations", empty);
2470fcf0e1cSLei YU     if (!assocJson.empty())
2480fcf0e1cSLei YU     {
2490fcf0e1cSLei YU         auto assocs = getAssociationsFromJson(assocJson);
2500fcf0e1cSLei YU         if (!assocs.empty())
2510fcf0e1cSLei YU         {
2520fcf0e1cSLei YU             associationIface =
2530fcf0e1cSLei YU                 std::make_unique<AssociationObject>(bus, objPath.c_str());
2540fcf0e1cSLei YU             associationIface->associations(assocs);
2550fcf0e1cSLei YU         }
2560fcf0e1cSLei YU     }
2570fcf0e1cSLei YU 
258abcc94faSVijay Khemka     /* Get expression string */
25903c4c8e2SPatrick Williams     static constexpr auto exprKey = "Expression";
26003c4c8e2SPatrick Williams     if (sensorConfig.contains(exprKey))
26103c4c8e2SPatrick Williams     {
262a959678cSPatrick Williams         auto& ref = sensorConfig.at(exprKey);
26303c4c8e2SPatrick Williams         if (ref.is_array())
26403c4c8e2SPatrick Williams         {
26503c4c8e2SPatrick Williams             exprStr = std::string{};
26603c4c8e2SPatrick Williams             for (auto& s : ref)
26703c4c8e2SPatrick Williams             {
26803c4c8e2SPatrick Williams                 exprStr += s;
26903c4c8e2SPatrick Williams             }
27003c4c8e2SPatrick Williams         }
27103c4c8e2SPatrick Williams         else if (ref.is_string())
27203c4c8e2SPatrick Williams         {
27303c4c8e2SPatrick Williams             exprStr = std::string{ref};
27403c4c8e2SPatrick Williams         }
27503c4c8e2SPatrick Williams     }
276abcc94faSVijay Khemka 
277abcc94faSVijay Khemka     /* Get all the parameter listed in configuration */
278abcc94faSVijay Khemka     auto params = sensorConfig.value("Params", empty);
279abcc94faSVijay Khemka 
280abcc94faSVijay Khemka     /* Check for constant parameter */
281abcc94faSVijay Khemka     const auto& consParams = params.value("ConstParam", empty);
282abcc94faSVijay Khemka     if (!consParams.empty())
283abcc94faSVijay Khemka     {
284abcc94faSVijay Khemka         for (auto& j : consParams)
285abcc94faSVijay Khemka         {
286abcc94faSVijay Khemka             if (j.find("ParamName") != j.end())
287abcc94faSVijay Khemka             {
288abcc94faSVijay Khemka                 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
2893ed9a516SVijay Khemka                 std::string name = j["ParamName"];
2903ed9a516SVijay Khemka                 symbols.create_variable(name);
2913ed9a516SVijay Khemka                 paramMap.emplace(std::move(name), std::move(paramPtr));
292abcc94faSVijay Khemka             }
293abcc94faSVijay Khemka             else
294abcc94faSVijay Khemka             {
295abcc94faSVijay Khemka                 /* Invalid configuration */
296abcc94faSVijay Khemka                 throw std::invalid_argument(
297abcc94faSVijay Khemka                     "ParamName not found in configuration");
298abcc94faSVijay Khemka             }
299abcc94faSVijay Khemka         }
300abcc94faSVijay Khemka     }
301abcc94faSVijay Khemka 
3027452a867SVijay Khemka     /* Check for dbus parameter */
3037452a867SVijay Khemka     auto dbusParams = params.value("DbusParam", empty);
3047452a867SVijay Khemka     if (!dbusParams.empty())
3057452a867SVijay Khemka     {
3067452a867SVijay Khemka         for (auto& j : dbusParams)
3077452a867SVijay Khemka         {
3087452a867SVijay Khemka             /* Get parameter dbus sensor descriptor */
3097452a867SVijay Khemka             auto desc = j.value("Desc", empty);
3107452a867SVijay Khemka             if ((!desc.empty()) && (j.find("ParamName") != j.end()))
3117452a867SVijay Khemka             {
3127452a867SVijay Khemka                 std::string sensorType = desc.value("SensorType", "");
3137452a867SVijay Khemka                 std::string name = desc.value("Name", "");
3147452a867SVijay Khemka 
3157452a867SVijay Khemka                 if (!sensorType.empty() && !name.empty())
3167452a867SVijay Khemka                 {
3171204b433SGeorge Liu                     auto path = sensorDbusPath + sensorType + "/" + name;
3187452a867SVijay Khemka 
319150d5f6aSPatrick Williams                     auto paramPtr =
320150d5f6aSPatrick Williams                         std::make_unique<SensorParam>(bus, path, *this);
3211204b433SGeorge Liu                     std::string paramName = j["ParamName"];
3221204b433SGeorge Liu                     symbols.create_variable(paramName);
3231204b433SGeorge Liu                     paramMap.emplace(std::move(paramName), std::move(paramPtr));
3247452a867SVijay Khemka                 }
3257452a867SVijay Khemka             }
3267452a867SVijay Khemka         }
3277452a867SVijay Khemka     }
328abcc94faSVijay Khemka 
3293ed9a516SVijay Khemka     symbols.add_constants();
3309f1ef4f5SMatt Spinler     symbols.add_package(vecopsPackage);
3310ab9d838SLei YU     symbols.add_function("maxIgnoreNaN", funcMaxIgnoreNaN);
33287d35115SLei YU     symbols.add_function("sumIgnoreNaN", funcSumIgnoreNaN);
333c77b6b3fSLei YU     symbols.add_function("ifNan", funcIfNan);
3340ab9d838SLei YU 
3353ed9a516SVijay Khemka     expression.register_symbol_table(symbols);
3363ed9a516SVijay Khemka 
3373ed9a516SVijay Khemka     /* parser from exprtk */
3383ed9a516SVijay Khemka     exprtk::parser<double> parser{};
339ddc6dcd6SMatt Spinler     if (!parser.compile(exprStr, expression))
340ddc6dcd6SMatt Spinler     {
34182b39c66SPatrick Williams         error("Expression compilation failed");
342ddc6dcd6SMatt Spinler 
343ddc6dcd6SMatt Spinler         for (std::size_t i = 0; i < parser.error_count(); ++i)
344ddc6dcd6SMatt Spinler         {
34582b39c66SPatrick Williams             auto err = parser.get_error(i);
34682b39c66SPatrick Williams             error("Error parsing token at {POSITION}: {ERROR}", "POSITION",
34782b39c66SPatrick Williams                   err.token.position, "TYPE",
34882b39c66SPatrick Williams                   exprtk::parser_error::to_str(err.mode), "ERROR",
34982b39c66SPatrick Williams                   err.diagnostic);
350ddc6dcd6SMatt Spinler         }
351ddc6dcd6SMatt Spinler         throw std::runtime_error("Expression compilation failed");
352ddc6dcd6SMatt Spinler     }
3533ed9a516SVijay Khemka 
354abcc94faSVijay Khemka     /* Print all parameters for debug purpose only */
355abcc94faSVijay Khemka     printParams(paramMap);
356abcc94faSVijay Khemka }
357abcc94faSVijay Khemka 
createAssociation(const std::string & objPath,const std::string & entityPath)358dc777015STao Lin void VirtualSensor::createAssociation(const std::string& objPath,
359dc777015STao Lin                                       const std::string& entityPath)
360dc777015STao Lin {
361dc777015STao Lin     if (objPath.empty() || entityPath.empty())
362dc777015STao Lin     {
363dc777015STao Lin         return;
364dc777015STao Lin     }
365dc777015STao Lin 
366dc777015STao Lin     std::filesystem::path p(entityPath);
367dc777015STao Lin     auto assocsDbus =
368dc777015STao Lin         AssociationList{{"chassis", "all_sensors", p.parent_path().string()}};
369150d5f6aSPatrick Williams     associationIface =
370150d5f6aSPatrick Williams         std::make_unique<AssociationObject>(bus, objPath.c_str());
371dc777015STao Lin     associationIface->associations(assocsDbus);
372dc777015STao Lin }
373dc777015STao Lin 
initVirtualSensor(const InterfaceMap & interfaceMap,const std::string & objPath,const std::string & sensorType,const std::string & calculationIface)374150d5f6aSPatrick Williams void VirtualSensor::initVirtualSensor(
375150d5f6aSPatrick Williams     const InterfaceMap& interfaceMap, const std::string& objPath,
376150d5f6aSPatrick Williams     const std::string& sensorType, const std::string& calculationIface)
377e7efe135SRashmica Gupta {
378e7efe135SRashmica Gupta     Json thresholds;
379150d5f6aSPatrick Williams     const std::string vsThresholdsIntf =
380150d5f6aSPatrick Williams         calculationIface + vsThresholdsIfaceSuffix;
381e7efe135SRashmica Gupta 
382e7efe135SRashmica Gupta     for (const auto& [interface, propertyMap] : interfaceMap)
383e7efe135SRashmica Gupta     {
384e7efe135SRashmica Gupta         /* Each threshold is on it's own interface with a number as a suffix
385e7efe135SRashmica Gupta          * eg xyz.openbmc_project.Configuration.ModifiedMedian.Thresholds1 */
386e7efe135SRashmica Gupta         if (interface.find(vsThresholdsIntf) != std::string::npos)
387e7efe135SRashmica Gupta         {
38891799dbdSTao Lin             parseThresholds(thresholds, propertyMap, interface);
389e7efe135SRashmica Gupta         }
390e7efe135SRashmica Gupta         else if (interface == calculationIface)
391e7efe135SRashmica Gupta         {
392e7efe135SRashmica Gupta             parseConfigInterface(propertyMap, sensorType, interface);
393e7efe135SRashmica Gupta         }
394e7efe135SRashmica Gupta     }
395e7efe135SRashmica Gupta 
396e7efe135SRashmica Gupta     createThresholds(thresholds, objPath);
397e7efe135SRashmica Gupta     symbols.add_constants();
398e7efe135SRashmica Gupta     symbols.add_package(vecopsPackage);
399e7efe135SRashmica Gupta     expression.register_symbol_table(symbols);
400e7efe135SRashmica Gupta 
401dc777015STao Lin     createAssociation(objPath, entityPath);
402e7efe135SRashmica Gupta     /* Print all parameters for debug purpose only */
403e7efe135SRashmica Gupta     printParams(paramMap);
404e7efe135SRashmica Gupta }
405e7efe135SRashmica Gupta 
setSensorValue(double value)406abcc94faSVijay Khemka void VirtualSensor::setSensorValue(double value)
407abcc94faSVijay Khemka {
408543bf668SPatrick Williams     value = std::clamp(value, ValueIface::minValue(), ValueIface::maxValue());
409abcc94faSVijay Khemka     ValueIface::value(value);
410abcc94faSVijay Khemka }
411abcc94faSVijay Khemka 
calculateValue(const std::string & calculation,const VirtualSensor::ParamMap & paramMap)412304fd0e4SRashmica Gupta double VirtualSensor::calculateValue(const std::string& calculation,
413304fd0e4SRashmica Gupta                                      const VirtualSensor::ParamMap& paramMap)
414e7efe135SRashmica Gupta {
415*7f41a0d4SGeorge Liu     auto iter = calculationIfaces.find(calculation);
416*7f41a0d4SGeorge Liu     if (iter == calculationIfaces.end())
417304fd0e4SRashmica Gupta     {
418e7efe135SRashmica Gupta         return std::numeric_limits<double>::quiet_NaN();
419e7efe135SRashmica Gupta     }
420*7f41a0d4SGeorge Liu 
421*7f41a0d4SGeorge Liu     std::vector<double> values;
422*7f41a0d4SGeorge Liu     for (auto& param : paramMap)
423304fd0e4SRashmica Gupta     {
424*7f41a0d4SGeorge Liu         auto& name = param.first;
425*7f41a0d4SGeorge Liu         if (auto var = symbols.get_variable(name))
426f6b7e0a4STao Lin         {
427*7f41a0d4SGeorge Liu             if (!sensorInRange(var->ref()))
428*7f41a0d4SGeorge Liu             {
429*7f41a0d4SGeorge Liu                 continue;
430f6b7e0a4STao Lin             }
431*7f41a0d4SGeorge Liu             values.push_back(var->ref());
432*7f41a0d4SGeorge Liu         }
433*7f41a0d4SGeorge Liu     }
434*7f41a0d4SGeorge Liu 
435*7f41a0d4SGeorge Liu     return iter->second(values);
436304fd0e4SRashmica Gupta }
437304fd0e4SRashmica Gupta 
sensorInRange(double value)438304fd0e4SRashmica Gupta bool VirtualSensor::sensorInRange(double value)
439304fd0e4SRashmica Gupta {
440304fd0e4SRashmica Gupta     if (value <= this->maxValidInput && value >= this->minValidInput)
441304fd0e4SRashmica Gupta     {
442304fd0e4SRashmica Gupta         return true;
443304fd0e4SRashmica Gupta     }
444304fd0e4SRashmica Gupta     return false;
445304fd0e4SRashmica Gupta }
446e7efe135SRashmica Gupta 
updateVirtualSensor()447abcc94faSVijay Khemka void VirtualSensor::updateVirtualSensor()
4483ed9a516SVijay Khemka {
4493ed9a516SVijay Khemka     for (auto& param : paramMap)
4503ed9a516SVijay Khemka     {
4513ed9a516SVijay Khemka         auto& name = param.first;
4523ed9a516SVijay Khemka         auto& data = param.second;
4533ed9a516SVijay Khemka         if (auto var = symbols.get_variable(name))
4543ed9a516SVijay Khemka         {
4553ed9a516SVijay Khemka             var->ref() = data->getParamValue();
4563ed9a516SVijay Khemka         }
4573ed9a516SVijay Khemka         else
4583ed9a516SVijay Khemka         {
4593ed9a516SVijay Khemka             /* Invalid parameter */
4603ed9a516SVijay Khemka             throw std::invalid_argument("ParamName not found in symbols");
4613ed9a516SVijay Khemka         }
4623ed9a516SVijay Khemka     }
463*7f41a0d4SGeorge Liu     auto val = (!calculationIfaces.contains(exprStr))
464304fd0e4SRashmica Gupta                    ? expression.value()
465304fd0e4SRashmica Gupta                    : calculateValue(exprStr, paramMap);
46632a7156bSVijay Khemka 
46732a7156bSVijay Khemka     /* Set sensor value to dbus interface */
4683ed9a516SVijay Khemka     setSensorValue(val);
469fbd7145eSPatrick Williams     debug("Sensor {NAME} = {VALUE}", "NAME", this->name, "VALUE", val);
47032a7156bSVijay Khemka 
4718f5e6119SMatt Spinler     /* Check sensor thresholds and log required message */
472b306b03dSMatt Spinler     checkThresholds(val, perfLossIface);
473fdb826d5SPatrick Williams     checkThresholds(val, warningIface);
474fdb826d5SPatrick Williams     checkThresholds(val, criticalIface);
475fdb826d5SPatrick Williams     checkThresholds(val, softShutdownIface);
476fdb826d5SPatrick Williams     checkThresholds(val, hardShutdownIface);
4773ed9a516SVijay Khemka }
478abcc94faSVijay Khemka 
createThresholds(const Json & threshold,const std::string & objPath)4793e99919bSRashmica Gupta void VirtualSensor::createThresholds(const Json& threshold,
4803e99919bSRashmica Gupta                                      const std::string& objPath)
4813e99919bSRashmica Gupta {
4823e99919bSRashmica Gupta     if (threshold.empty())
4833e99919bSRashmica Gupta     {
4843e99919bSRashmica Gupta         return;
4853e99919bSRashmica Gupta     }
4863e99919bSRashmica Gupta     // Only create the threshold interfaces if
4873e99919bSRashmica Gupta     // at least one of their values is present.
4883e99919bSRashmica Gupta     if (threshold.contains("CriticalHigh") || threshold.contains("CriticalLow"))
4893e99919bSRashmica Gupta     {
4903e99919bSRashmica Gupta         criticalIface =
4913e99919bSRashmica Gupta             std::make_unique<Threshold<CriticalObject>>(bus, objPath.c_str());
4923e99919bSRashmica Gupta 
49391799dbdSTao Lin         if (threshold.contains("CriticalHigh"))
49491799dbdSTao Lin         {
49591799dbdSTao Lin             criticalIface->setEntityInterfaceHigh(
49691799dbdSTao Lin                 threshold.value("CriticalHighDirection", ""));
49791799dbdSTao Lin             debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
49891799dbdSTao Lin                   "INTF", threshold.value("CriticalHighDirection", ""));
49991799dbdSTao Lin         }
50091799dbdSTao Lin         if (threshold.contains("CriticalLow"))
50191799dbdSTao Lin         {
50291799dbdSTao Lin             criticalIface->setEntityInterfaceLow(
50391799dbdSTao Lin                 threshold.value("CriticalLowDirection", ""));
50491799dbdSTao Lin             debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
50591799dbdSTao Lin                   "INTF", threshold.value("CriticalLowDirection", ""));
50691799dbdSTao Lin         }
50791799dbdSTao Lin 
50891799dbdSTao Lin         criticalIface->setEntityPath(entityPath);
509a630f081SGeorge Liu         debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath, "PATH",
510a630f081SGeorge Liu               entityPath);
511a291ce1aSMatt Spinler 
512a291ce1aSMatt Spinler         criticalIface->criticalHigh(threshold.value(
513a291ce1aSMatt Spinler             "CriticalHigh", std::numeric_limits<double>::quiet_NaN()));
514a291ce1aSMatt Spinler         criticalIface->criticalLow(threshold.value(
515a291ce1aSMatt Spinler             "CriticalLow", std::numeric_limits<double>::quiet_NaN()));
516a291ce1aSMatt Spinler         criticalIface->setHighHysteresis(
517a291ce1aSMatt Spinler             threshold.value("CriticalHighHysteresis", defaultHysteresis));
518a291ce1aSMatt Spinler         criticalIface->setLowHysteresis(
519a291ce1aSMatt Spinler             threshold.value("CriticalLowHysteresis", defaultHysteresis));
5203e99919bSRashmica Gupta     }
5213e99919bSRashmica Gupta 
5223e99919bSRashmica Gupta     if (threshold.contains("WarningHigh") || threshold.contains("WarningLow"))
5233e99919bSRashmica Gupta     {
5243e99919bSRashmica Gupta         warningIface =
5253e99919bSRashmica Gupta             std::make_unique<Threshold<WarningObject>>(bus, objPath.c_str());
5263e99919bSRashmica Gupta 
52791799dbdSTao Lin         if (threshold.contains("WarningHigh"))
52891799dbdSTao Lin         {
52991799dbdSTao Lin             warningIface->setEntityInterfaceHigh(
53091799dbdSTao Lin                 threshold.value("WarningHighDirection", ""));
53191799dbdSTao Lin             debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
53291799dbdSTao Lin                   "INTF", threshold.value("WarningHighDirection", ""));
53391799dbdSTao Lin         }
53491799dbdSTao Lin         if (threshold.contains("WarningLow"))
53591799dbdSTao Lin         {
53691799dbdSTao Lin             warningIface->setEntityInterfaceLow(
53791799dbdSTao Lin                 threshold.value("WarningLowDirection", ""));
53891799dbdSTao Lin             debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
53991799dbdSTao Lin                   "INTF", threshold.value("WarningLowDirection", ""));
54091799dbdSTao Lin         }
54191799dbdSTao Lin 
54291799dbdSTao Lin         warningIface->setEntityPath(entityPath);
543a630f081SGeorge Liu         debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath, "PATH",
544a630f081SGeorge Liu               entityPath);
545a291ce1aSMatt Spinler 
546a291ce1aSMatt Spinler         warningIface->warningHigh(threshold.value(
547a291ce1aSMatt Spinler             "WarningHigh", std::numeric_limits<double>::quiet_NaN()));
548a291ce1aSMatt Spinler         warningIface->warningLow(threshold.value(
549a291ce1aSMatt Spinler             "WarningLow", std::numeric_limits<double>::quiet_NaN()));
550a291ce1aSMatt Spinler         warningIface->setHighHysteresis(
551a291ce1aSMatt Spinler             threshold.value("WarningHighHysteresis", defaultHysteresis));
552a291ce1aSMatt Spinler         warningIface->setLowHysteresis(
553a291ce1aSMatt Spinler             threshold.value("WarningLowHysteresis", defaultHysteresis));
5543e99919bSRashmica Gupta     }
5553e99919bSRashmica Gupta 
5563e99919bSRashmica Gupta     if (threshold.contains("HardShutdownHigh") ||
5573e99919bSRashmica Gupta         threshold.contains("HardShutdownLow"))
5583e99919bSRashmica Gupta     {
5593e99919bSRashmica Gupta         hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>(
5603e99919bSRashmica Gupta             bus, objPath.c_str());
5613e99919bSRashmica Gupta 
5623e99919bSRashmica Gupta         hardShutdownIface->hardShutdownHigh(threshold.value(
5633e99919bSRashmica Gupta             "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
5643e99919bSRashmica Gupta         hardShutdownIface->hardShutdownLow(threshold.value(
5653e99919bSRashmica Gupta             "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
5661dff7dceSRashmica Gupta         hardShutdownIface->setHighHysteresis(
5671dff7dceSRashmica Gupta             threshold.value("HardShutdownHighHysteresis", defaultHysteresis));
5681dff7dceSRashmica Gupta         hardShutdownIface->setLowHysteresis(
5691dff7dceSRashmica Gupta             threshold.value("HardShutdownLowHysteresis", defaultHysteresis));
5703e99919bSRashmica Gupta     }
5713e99919bSRashmica Gupta 
5723e99919bSRashmica Gupta     if (threshold.contains("SoftShutdownHigh") ||
5733e99919bSRashmica Gupta         threshold.contains("SoftShutdownLow"))
5743e99919bSRashmica Gupta     {
5753e99919bSRashmica Gupta         softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>(
5763e99919bSRashmica Gupta             bus, objPath.c_str());
5773e99919bSRashmica Gupta 
5783e99919bSRashmica Gupta         softShutdownIface->softShutdownHigh(threshold.value(
5793e99919bSRashmica Gupta             "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
5803e99919bSRashmica Gupta         softShutdownIface->softShutdownLow(threshold.value(
5813e99919bSRashmica Gupta             "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
5821dff7dceSRashmica Gupta         softShutdownIface->setHighHysteresis(
5831dff7dceSRashmica Gupta             threshold.value("SoftShutdownHighHysteresis", defaultHysteresis));
5841dff7dceSRashmica Gupta         softShutdownIface->setLowHysteresis(
5851dff7dceSRashmica Gupta             threshold.value("SoftShutdownLowHysteresis", defaultHysteresis));
5863e99919bSRashmica Gupta     }
5873e99919bSRashmica Gupta 
5883e99919bSRashmica Gupta     if (threshold.contains("PerformanceLossHigh") ||
5893e99919bSRashmica Gupta         threshold.contains("PerformanceLossLow"))
5903e99919bSRashmica Gupta     {
5913e99919bSRashmica Gupta         perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>(
5923e99919bSRashmica Gupta             bus, objPath.c_str());
5933e99919bSRashmica Gupta 
5943e99919bSRashmica Gupta         perfLossIface->performanceLossHigh(threshold.value(
5953e99919bSRashmica Gupta             "PerformanceLossHigh", std::numeric_limits<double>::quiet_NaN()));
5963e99919bSRashmica Gupta         perfLossIface->performanceLossLow(threshold.value(
5973e99919bSRashmica Gupta             "PerformanceLossLow", std::numeric_limits<double>::quiet_NaN()));
5981dff7dceSRashmica Gupta         perfLossIface->setHighHysteresis(threshold.value(
5991dff7dceSRashmica Gupta             "PerformanceLossHighHysteresis", defaultHysteresis));
6001dff7dceSRashmica Gupta         perfLossIface->setLowHysteresis(
6011dff7dceSRashmica Gupta             threshold.value("PerformanceLossLowHysteresis", defaultHysteresis));
6023e99919bSRashmica Gupta     }
6033e99919bSRashmica Gupta }
6043e99919bSRashmica Gupta 
getObjectsFromDBus()605e7efe135SRashmica Gupta ManagedObjectType VirtualSensors::getObjectsFromDBus()
606e7efe135SRashmica Gupta {
607e7efe135SRashmica Gupta     ManagedObjectType objects;
608e7efe135SRashmica Gupta 
609e7efe135SRashmica Gupta     try
610e7efe135SRashmica Gupta     {
611150d5f6aSPatrick Williams         auto method = bus.new_method_call(
612150d5f6aSPatrick Williams             "xyz.openbmc_project.EntityManager",
613f6825b91SNan Zhou             "/xyz/openbmc_project/inventory",
614150d5f6aSPatrick Williams             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
615e7efe135SRashmica Gupta         auto reply = bus.call(method);
616e7efe135SRashmica Gupta         reply.read(objects);
617e7efe135SRashmica Gupta     }
6188e11cccbSPatrick Williams     catch (const sdbusplus::exception_t& ex)
619e7efe135SRashmica Gupta     {
620e7efe135SRashmica Gupta         // If entity manager isn't running yet, keep going.
621e7efe135SRashmica Gupta         if (std::string("org.freedesktop.DBus.Error.ServiceUnknown") !=
622e7efe135SRashmica Gupta             ex.name())
623e7efe135SRashmica Gupta         {
62471b9c116SMatt Spinler             error("Could not reach entity-manager: {ERROR}", "ERROR", ex);
62571b9c116SMatt Spinler             throw;
626e7efe135SRashmica Gupta         }
627e7efe135SRashmica Gupta     }
628e7efe135SRashmica Gupta 
629e7efe135SRashmica Gupta     return objects;
630e7efe135SRashmica Gupta }
631e7efe135SRashmica Gupta 
propertiesChanged(sdbusplus::message_t & msg)6328e11cccbSPatrick Williams void VirtualSensors::propertiesChanged(sdbusplus::message_t& msg)
633e7efe135SRashmica Gupta {
634*7f41a0d4SGeorge Liu     std::string interface;
635e7efe135SRashmica Gupta     PropertyMap properties;
636e7efe135SRashmica Gupta 
637*7f41a0d4SGeorge Liu     msg.read(interface, properties);
638e7efe135SRashmica Gupta 
639e7efe135SRashmica Gupta     /* We get multiple callbacks for one sensor. 'Type' is a required field and
640e7efe135SRashmica Gupta      * is a unique label so use to to only proceed once per sensor */
641e7efe135SRashmica Gupta     if (properties.contains("Type"))
642e7efe135SRashmica Gupta     {
643*7f41a0d4SGeorge Liu         if (calculationIfaces.contains(interface))
644e7efe135SRashmica Gupta         {
645*7f41a0d4SGeorge Liu             createVirtualSensorsFromDBus(interface);
646e7efe135SRashmica Gupta         }
647e7efe135SRashmica Gupta     }
648e7efe135SRashmica Gupta }
649e7efe135SRashmica Gupta 
650abcc94faSVijay Khemka /** @brief Parsing Virtual Sensor config JSON file  */
parseConfigFile()65132dff21bSPatrick Williams Json VirtualSensors::parseConfigFile()
652abcc94faSVijay Khemka {
65332dff21bSPatrick Williams     using path = std::filesystem::path;
65432dff21bSPatrick Williams     auto configFile = []() -> path {
65532dff21bSPatrick Williams         static constexpr auto name = "virtual_sensor_config.json";
65632dff21bSPatrick Williams 
65732dff21bSPatrick Williams         for (auto pathSeg : {std::filesystem::current_path(),
65832dff21bSPatrick Williams                              path{"/var/lib/phosphor-virtual-sensor"},
65932dff21bSPatrick Williams                              path{"/usr/share/phosphor-virtual-sensor"}})
66032dff21bSPatrick Williams         {
66132dff21bSPatrick Williams             auto file = pathSeg / name;
66232dff21bSPatrick Williams             if (std::filesystem::exists(file))
66332dff21bSPatrick Williams             {
66432dff21bSPatrick Williams                 return file;
66532dff21bSPatrick Williams             }
66632dff21bSPatrick Williams         }
66732dff21bSPatrick Williams         return name;
66832dff21bSPatrick Williams     }();
66932dff21bSPatrick Williams 
670abcc94faSVijay Khemka     std::ifstream jsonFile(configFile);
671abcc94faSVijay Khemka     if (!jsonFile.is_open())
672abcc94faSVijay Khemka     {
67382b39c66SPatrick Williams         error("config JSON file {FILENAME} not found", "FILENAME", configFile);
674e7efe135SRashmica Gupta         return {};
675abcc94faSVijay Khemka     }
676abcc94faSVijay Khemka 
677abcc94faSVijay Khemka     auto data = Json::parse(jsonFile, nullptr, false);
678abcc94faSVijay Khemka     if (data.is_discarded())
679abcc94faSVijay Khemka     {
68082b39c66SPatrick Williams         error("config readings JSON parser failure with {FILENAME}", "FILENAME",
68182b39c66SPatrick Williams               configFile);
682abcc94faSVijay Khemka         throw std::exception{};
683abcc94faSVijay Khemka     }
684abcc94faSVijay Khemka 
685abcc94faSVijay Khemka     return data;
686abcc94faSVijay Khemka }
687abcc94faSVijay Khemka 
688e0d371e4SVijay Khemka std::map<std::string, ValueIface::Unit> unitMap = {
689e0d371e4SVijay Khemka     {"temperature", ValueIface::Unit::DegreesC},
690e0d371e4SVijay Khemka     {"fan_tach", ValueIface::Unit::RPMS},
6919358f6bdSKonstantin Aladyshev     {"fan_pwm", ValueIface::Unit::Percent},
692e0d371e4SVijay Khemka     {"voltage", ValueIface::Unit::Volts},
693e0d371e4SVijay Khemka     {"altitude", ValueIface::Unit::Meters},
694e0d371e4SVijay Khemka     {"current", ValueIface::Unit::Amperes},
695e0d371e4SVijay Khemka     {"power", ValueIface::Unit::Watts},
696e0d371e4SVijay Khemka     {"energy", ValueIface::Unit::Joules},
6972b56ddb3SKumar Thangavel     {"utilization", ValueIface::Unit::Percent},
6984ac7a7f2SRashmica Gupta     {"airflow", ValueIface::Unit::CFM},
6994ac7a7f2SRashmica Gupta     {"pressure", ValueIface::Unit::Pascals}};
700e0d371e4SVijay Khemka 
getSensorTypeFromUnit(const std::string & unit)701e7efe135SRashmica Gupta const std::string getSensorTypeFromUnit(const std::string& unit)
702e7efe135SRashmica Gupta {
703e7efe135SRashmica Gupta     std::string unitPrefix = "xyz.openbmc_project.Sensor.Value.Unit.";
704e7efe135SRashmica Gupta     for (auto [type, unitObj] : unitMap)
705e7efe135SRashmica Gupta     {
706e7efe135SRashmica Gupta         auto unitPath = ValueIface::convertUnitToString(unitObj);
707e7efe135SRashmica Gupta         if (unitPath == (unitPrefix + unit))
708e7efe135SRashmica Gupta         {
709e7efe135SRashmica Gupta             return type;
710e7efe135SRashmica Gupta         }
711e7efe135SRashmica Gupta     }
712e7efe135SRashmica Gupta     return "";
713e7efe135SRashmica Gupta }
714e7efe135SRashmica Gupta 
setupMatches()715e7efe135SRashmica Gupta void VirtualSensors::setupMatches()
716e7efe135SRashmica Gupta {
717e7efe135SRashmica Gupta     /* Already setup */
718e7efe135SRashmica Gupta     if (!this->matches.empty())
719e7efe135SRashmica Gupta     {
720e7efe135SRashmica Gupta         return;
721e7efe135SRashmica Gupta     }
722e7efe135SRashmica Gupta 
723e7efe135SRashmica Gupta     /* Setup matches */
7248e11cccbSPatrick Williams     auto eventHandler = [this](sdbusplus::message_t& message) {
725e7efe135SRashmica Gupta         if (message.is_method_error())
726e7efe135SRashmica Gupta         {
72782b39c66SPatrick Williams             error("Callback method error");
728e7efe135SRashmica Gupta             return;
729e7efe135SRashmica Gupta         }
730e7efe135SRashmica Gupta         this->propertiesChanged(message);
731e7efe135SRashmica Gupta     };
732e7efe135SRashmica Gupta 
733*7f41a0d4SGeorge Liu     for (const auto& [iface, _] : calculationIfaces)
734e7efe135SRashmica Gupta     {
7358e11cccbSPatrick Williams         auto match = std::make_unique<sdbusplus::bus::match_t>(
736e7efe135SRashmica Gupta             bus,
737e7efe135SRashmica Gupta             sdbusplus::bus::match::rules::propertiesChangedNamespace(
738e7efe135SRashmica Gupta                 "/xyz/openbmc_project/inventory", iface),
739e7efe135SRashmica Gupta             eventHandler);
740e7efe135SRashmica Gupta         this->matches.emplace_back(std::move(match));
741e7efe135SRashmica Gupta     }
742e7efe135SRashmica Gupta }
743e7efe135SRashmica Gupta 
createVirtualSensorsFromDBus(const std::string & calculationIface)744e7efe135SRashmica Gupta void VirtualSensors::createVirtualSensorsFromDBus(
745e7efe135SRashmica Gupta     const std::string& calculationIface)
746e7efe135SRashmica Gupta {
747e7efe135SRashmica Gupta     if (calculationIface.empty())
748e7efe135SRashmica Gupta     {
74982b39c66SPatrick Williams         error("No calculation type supplied");
750e7efe135SRashmica Gupta         return;
751e7efe135SRashmica Gupta     }
752e7efe135SRashmica Gupta     auto objects = getObjectsFromDBus();
753e7efe135SRashmica Gupta 
754e7efe135SRashmica Gupta     /* Get virtual sensors config data */
755e7efe135SRashmica Gupta     for (const auto& [path, interfaceMap] : objects)
756e7efe135SRashmica Gupta     {
757e7efe135SRashmica Gupta         /* Find Virtual Sensor interfaces */
7582db8d41fSGeorge Liu         auto intfIter = interfaceMap.find(calculationIface);
7592db8d41fSGeorge Liu         if (intfIter == interfaceMap.end())
760e7efe135SRashmica Gupta         {
761e7efe135SRashmica Gupta             continue;
762e7efe135SRashmica Gupta         }
7632db8d41fSGeorge Liu 
7642db8d41fSGeorge Liu         std::string name = path.filename();
765e7efe135SRashmica Gupta         if (name.empty())
766e7efe135SRashmica Gupta         {
76782b39c66SPatrick Williams             error("Virtual Sensor name not found in entity manager config");
768e7efe135SRashmica Gupta             continue;
769e7efe135SRashmica Gupta         }
770e7efe135SRashmica Gupta         if (virtualSensorsMap.contains(name))
771e7efe135SRashmica Gupta         {
77282b39c66SPatrick Williams             error("A virtual sensor named {NAME} already exists", "NAME", name);
773e7efe135SRashmica Gupta             continue;
774e7efe135SRashmica Gupta         }
775e7efe135SRashmica Gupta 
776e7efe135SRashmica Gupta         /* Extract the virtual sensor type as we need this to initialize the
777e7efe135SRashmica Gupta          * sensor */
7782db8d41fSGeorge Liu         std::string sensorType, sensorUnit;
7792db8d41fSGeorge Liu         auto propertyMap = intfIter->second;
7802db8d41fSGeorge Liu         auto proIter = propertyMap.find("Units");
7812db8d41fSGeorge Liu         if (proIter != propertyMap.end())
782e7efe135SRashmica Gupta         {
7832db8d41fSGeorge Liu             sensorUnit = std::get<std::string>(proIter->second);
784e7efe135SRashmica Gupta         }
785e7efe135SRashmica Gupta         sensorType = getSensorTypeFromUnit(sensorUnit);
786e7efe135SRashmica Gupta         if (sensorType.empty())
787e7efe135SRashmica Gupta         {
78882b39c66SPatrick Williams             error("Sensor unit type {TYPE} is not supported", "TYPE",
78982b39c66SPatrick Williams                   sensorUnit);
790e7efe135SRashmica Gupta             continue;
791e7efe135SRashmica Gupta         }
792e7efe135SRashmica Gupta 
793e7efe135SRashmica Gupta         try
794e7efe135SRashmica Gupta         {
7952db8d41fSGeorge Liu             auto objpath = static_cast<std::string>(path);
796e7efe135SRashmica Gupta             auto virtObjPath = sensorDbusPath + sensorType + "/" + name;
797e7efe135SRashmica Gupta 
798e7efe135SRashmica Gupta             auto virtualSensorPtr = std::make_unique<VirtualSensor>(
799e7efe135SRashmica Gupta                 bus, virtObjPath.c_str(), interfaceMap, name, sensorType,
800dc777015STao Lin                 calculationIface, objpath);
80182b39c66SPatrick Williams             info("Added a new virtual sensor: {NAME} {TYPE}", "NAME", name,
80282b39c66SPatrick Williams                  "TYPE", sensorType);
803e7efe135SRashmica Gupta             virtualSensorPtr->updateVirtualSensor();
804e7efe135SRashmica Gupta 
805e7efe135SRashmica Gupta             /* Initialize unit value for virtual sensor */
806e7efe135SRashmica Gupta             virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
807e7efe135SRashmica Gupta             virtualSensorPtr->emit_object_added();
808e7efe135SRashmica Gupta 
809e7efe135SRashmica Gupta             virtualSensorsMap.emplace(name, std::move(virtualSensorPtr));
810e7efe135SRashmica Gupta 
811e7efe135SRashmica Gupta             /* Setup match for interfaces removed */
812ae10c529SPatrick Williams             auto intfRemoved = [this, objpath,
813ae10c529SPatrick Williams                                 name](sdbusplus::message_t& message) {
814e7efe135SRashmica Gupta                 if (!virtualSensorsMap.contains(name))
815e7efe135SRashmica Gupta                 {
816e7efe135SRashmica Gupta                     return;
817e7efe135SRashmica Gupta                 }
818e7efe135SRashmica Gupta                 sdbusplus::message::object_path path;
819e7efe135SRashmica Gupta                 message.read(path);
820e7efe135SRashmica Gupta                 if (static_cast<const std::string&>(path) == objpath)
821e7efe135SRashmica Gupta                 {
82282b39c66SPatrick Williams                     info("Removed a virtual sensor: {NAME}", "NAME", name);
823e7efe135SRashmica Gupta                     virtualSensorsMap.erase(name);
824e7efe135SRashmica Gupta                 }
825e7efe135SRashmica Gupta             };
8268e11cccbSPatrick Williams             auto matchOnRemove = std::make_unique<sdbusplus::bus::match_t>(
827e7efe135SRashmica Gupta                 bus,
828e7efe135SRashmica Gupta                 sdbusplus::bus::match::rules::interfacesRemoved() +
829e7efe135SRashmica Gupta                     sdbusplus::bus::match::rules::argNpath(0, objpath),
830e7efe135SRashmica Gupta                 intfRemoved);
831e7efe135SRashmica Gupta             /* TODO: slight race condition here. Check that the config still
832e7efe135SRashmica Gupta              * exists */
833e7efe135SRashmica Gupta             this->matches.emplace_back(std::move(matchOnRemove));
834e7efe135SRashmica Gupta         }
835dac2663cSPatrick Williams         catch (const std::invalid_argument& ia)
836e7efe135SRashmica Gupta         {
83782b39c66SPatrick Williams             error("Failed to set up virtual sensor: {ERROR}", "ERROR", ia);
838e7efe135SRashmica Gupta         }
839e7efe135SRashmica Gupta     }
840e7efe135SRashmica Gupta }
841e7efe135SRashmica Gupta 
createVirtualSensors()842abcc94faSVijay Khemka void VirtualSensors::createVirtualSensors()
843abcc94faSVijay Khemka {
844abcc94faSVijay Khemka     static const Json empty{};
845abcc94faSVijay Khemka 
84632dff21bSPatrick Williams     auto data = parseConfigFile();
847e7efe135SRashmica Gupta 
848abcc94faSVijay Khemka     // print values
849fbd7145eSPatrick Williams     debug("JSON: {JSON}", "JSON", data.dump());
850abcc94faSVijay Khemka 
851abcc94faSVijay Khemka     /* Get virtual sensors  config data */
852abcc94faSVijay Khemka     for (const auto& j : data)
853abcc94faSVijay Khemka     {
854abcc94faSVijay Khemka         auto desc = j.value("Desc", empty);
855abcc94faSVijay Khemka         if (!desc.empty())
856abcc94faSVijay Khemka         {
857e7efe135SRashmica Gupta             if (desc.value("Config", "") == "D-Bus")
858e7efe135SRashmica Gupta             {
859e7efe135SRashmica Gupta                 /* Look on D-Bus for a virtual sensor config. Set up matches
860e7efe135SRashmica Gupta                  * first because the configs may not be on D-Bus yet and we
861e7efe135SRashmica Gupta                  * don't want to miss them */
862e7efe135SRashmica Gupta                 setupMatches();
863e7efe135SRashmica Gupta 
864e7efe135SRashmica Gupta                 if (desc.contains("Type"))
865e7efe135SRashmica Gupta                 {
86682b39c66SPatrick Williams                     auto type = desc.value("Type", "");
867*7f41a0d4SGeorge Liu                     auto intf = "xyz.openbmc_project.Configuration." + type;
86882b39c66SPatrick Williams 
869*7f41a0d4SGeorge Liu                     if (!calculationIfaces.contains(intf))
870e7efe135SRashmica Gupta                     {
87182b39c66SPatrick Williams                         error("Invalid calculation type {TYPE} supplied.",
87282b39c66SPatrick Williams                               "TYPE", type);
873e7efe135SRashmica Gupta                         continue;
874e7efe135SRashmica Gupta                     }
875*7f41a0d4SGeorge Liu                     createVirtualSensorsFromDBus(intf);
876e7efe135SRashmica Gupta                 }
877e7efe135SRashmica Gupta                 continue;
878e7efe135SRashmica Gupta             }
879e7efe135SRashmica Gupta 
880abcc94faSVijay Khemka             std::string sensorType = desc.value("SensorType", "");
881abcc94faSVijay Khemka             std::string name = desc.value("Name", "");
882665a0a29SRashmica Gupta             std::replace(name.begin(), name.end(), ' ', '_');
883abcc94faSVijay Khemka 
884abcc94faSVijay Khemka             if (!name.empty() && !sensorType.empty())
885abcc94faSVijay Khemka             {
886e0d371e4SVijay Khemka                 if (unitMap.find(sensorType) == unitMap.end())
887e0d371e4SVijay Khemka                 {
88882b39c66SPatrick Williams                     error("Sensor type {TYPE} is not supported", "TYPE",
88982b39c66SPatrick Williams                           sensorType);
890e0d371e4SVijay Khemka                 }
891e0d371e4SVijay Khemka                 else
892e0d371e4SVijay Khemka                 {
89367d8b9d2SRashmica Gupta                     if (virtualSensorsMap.find(name) != virtualSensorsMap.end())
89467d8b9d2SRashmica Gupta                     {
89582b39c66SPatrick Williams                         error("A virtual sensor named {NAME} already exists",
89682b39c66SPatrick Williams                               "NAME", name);
89767d8b9d2SRashmica Gupta                         continue;
89867d8b9d2SRashmica Gupta                     }
899862c3d1eSRashmica Gupta                     auto objPath = sensorDbusPath + sensorType + "/" + name;
900abcc94faSVijay Khemka 
90132a7156bSVijay Khemka                     auto virtualSensorPtr = std::make_unique<VirtualSensor>(
90232a7156bSVijay Khemka                         bus, objPath.c_str(), j, name);
903abcc94faSVijay Khemka 
90482b39c66SPatrick Williams                     info("Added a new virtual sensor: {NAME}", "NAME", name);
9053ed9a516SVijay Khemka                     virtualSensorPtr->updateVirtualSensor();
906e0d371e4SVijay Khemka 
907e0d371e4SVijay Khemka                     /* Initialize unit value for virtual sensor */
908e0d371e4SVijay Khemka                     virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
909a2fa63a6SRashmica Gupta                     virtualSensorPtr->emit_object_added();
910e0d371e4SVijay Khemka 
9113ed9a516SVijay Khemka                     virtualSensorsMap.emplace(std::move(name),
9123ed9a516SVijay Khemka                                               std::move(virtualSensorPtr));
913abcc94faSVijay Khemka                 }
914e0d371e4SVijay Khemka             }
915abcc94faSVijay Khemka             else
916abcc94faSVijay Khemka             {
91782b39c66SPatrick Williams                 error(
91882b39c66SPatrick Williams                     "Sensor type ({TYPE}) or name ({NAME}) not found in config file",
91982b39c66SPatrick Williams                     "NAME", name, "TYPE", sensorType);
920abcc94faSVijay Khemka             }
921abcc94faSVijay Khemka         }
922abcc94faSVijay Khemka         else
923abcc94faSVijay Khemka         {
92482b39c66SPatrick Williams             error("Descriptor for new virtual sensor not found in config file");
925abcc94faSVijay Khemka         }
926abcc94faSVijay Khemka     }
927abcc94faSVijay Khemka }
928abcc94faSVijay Khemka 
929f2e94221STao Lin } // namespace phosphor::virtual_sensor
930abcc94faSVijay Khemka 
931abcc94faSVijay Khemka /**
932abcc94faSVijay Khemka  * @brief Main
933abcc94faSVijay Khemka  */
main()934abcc94faSVijay Khemka int main()
935abcc94faSVijay Khemka {
936abcc94faSVijay Khemka     // Get a handle to system dbus
937abcc94faSVijay Khemka     auto bus = sdbusplus::bus::new_default();
938abcc94faSVijay Khemka 
9396c19e7d2SMatt Spinler     // Add the ObjectManager interface
940f7ec40aaSEd Tanous     sdbusplus::server::manager_t objManager(bus,
941f7ec40aaSEd Tanous                                             "/xyz/openbmc_project/sensors");
9426c19e7d2SMatt Spinler 
943abcc94faSVijay Khemka     // Create an virtual sensors object
944f2e94221STao Lin     phosphor::virtual_sensor::VirtualSensors virtualSensors(bus);
945abcc94faSVijay Khemka 
946abcc94faSVijay Khemka     // Request service bus name
94794921490SGeorge Liu     bus.request_name("xyz.openbmc_project.VirtualSensor");
948abcc94faSVijay Khemka 
949e667239dSPatrick Williams     // Run the dbus loop.
950e667239dSPatrick Williams     bus.process_loop();
951abcc94faSVijay Khemka 
952abcc94faSVijay Khemka     return 0;
953abcc94faSVijay Khemka }
954