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 
17f2e94221STao Lin namespace phosphor::virtual_sensor
18abcc94faSVijay Khemka {
19abcc94faSVijay Khemka 
200ab9d838SLei YU FuncMaxIgnoreNaN<double> VirtualSensor::funcMaxIgnoreNaN;
2187d35115SLei YU FuncSumIgnoreNaN<double> VirtualSensor::funcSumIgnoreNaN;
22c77b6b3fSLei YU FuncIfNan<double> VirtualSensor::funcIfNan;
230ab9d838SLei YU 
24abcc94faSVijay Khemka void printParams(const VirtualSensor::ParamMap& paramMap)
25abcc94faSVijay Khemka {
26abcc94faSVijay Khemka     for (const auto& p : paramMap)
27abcc94faSVijay Khemka     {
28abcc94faSVijay Khemka         const auto& p1 = p.first;
29abcc94faSVijay Khemka         const auto& p2 = p.second;
30abcc94faSVijay Khemka         auto val = p2->getParamValue();
31fbd7145eSPatrick Williams         debug("Parameter: {PARAM} = {VALUE}", "PARAM", p1, "VALUE", val);
32abcc94faSVijay Khemka     }
33abcc94faSVijay Khemka }
34abcc94faSVijay Khemka 
35abcc94faSVijay Khemka double SensorParam::getParamValue()
36abcc94faSVijay Khemka {
37abcc94faSVijay Khemka     switch (paramType)
38abcc94faSVijay Khemka     {
39abcc94faSVijay Khemka         case constParam:
40abcc94faSVijay Khemka             return value;
41abcc94faSVijay Khemka             break;
427452a867SVijay Khemka         case dbusParam:
437452a867SVijay Khemka             return dbusSensor->getSensorValue();
447452a867SVijay Khemka             break;
45abcc94faSVijay Khemka         default:
46abcc94faSVijay Khemka             throw std::invalid_argument("param type not supported");
47abcc94faSVijay Khemka     }
48abcc94faSVijay Khemka }
49abcc94faSVijay Khemka 
500fcf0e1cSLei YU using AssociationList =
510fcf0e1cSLei YU     std::vector<std::tuple<std::string, std::string, std::string>>;
520fcf0e1cSLei YU 
530fcf0e1cSLei YU AssociationList getAssociationsFromJson(const Json& j)
540fcf0e1cSLei YU {
550fcf0e1cSLei YU     AssociationList assocs{};
560fcf0e1cSLei YU     try
570fcf0e1cSLei YU     {
580fcf0e1cSLei YU         j.get_to(assocs);
590fcf0e1cSLei YU     }
600fcf0e1cSLei YU     catch (const std::exception& ex)
610fcf0e1cSLei YU     {
6282b39c66SPatrick Williams         error("Failed to parse association: {ERROR}", "ERROR", ex);
630fcf0e1cSLei YU     }
640fcf0e1cSLei YU     return assocs;
650fcf0e1cSLei YU }
660fcf0e1cSLei YU 
67e7efe135SRashmica Gupta template <typename U>
68e7efe135SRashmica Gupta struct VariantToNumber
69e7efe135SRashmica Gupta {
70e7efe135SRashmica Gupta     template <typename T>
71e7efe135SRashmica Gupta     U operator()(const T& t) const
72e7efe135SRashmica Gupta     {
73e7efe135SRashmica Gupta         if constexpr (std::is_convertible<T, U>::value)
74e7efe135SRashmica Gupta         {
75e7efe135SRashmica Gupta             return static_cast<U>(t);
76e7efe135SRashmica Gupta         }
77e7efe135SRashmica Gupta         throw std::invalid_argument("Invalid number type in config\n");
78e7efe135SRashmica Gupta     }
79e7efe135SRashmica Gupta };
80e7efe135SRashmica Gupta 
81e7efe135SRashmica Gupta template <typename U>
82e7efe135SRashmica Gupta U getNumberFromConfig(const PropertyMap& map, const std::string& name,
83190f6d06SJiaqing Zhao                       bool required,
84190f6d06SJiaqing Zhao                       U defaultValue = std::numeric_limits<U>::quiet_NaN())
85e7efe135SRashmica Gupta {
86e7efe135SRashmica Gupta     if (auto itr = map.find(name); itr != map.end())
87e7efe135SRashmica Gupta     {
88e7efe135SRashmica Gupta         return std::visit(VariantToNumber<U>(), itr->second);
89e7efe135SRashmica Gupta     }
90e7efe135SRashmica Gupta     else if (required)
91e7efe135SRashmica Gupta     {
9282b39c66SPatrick Williams         error("Required field {NAME} missing in config", "NAME", name);
93e7efe135SRashmica Gupta         throw std::invalid_argument("Required field missing in config");
94e7efe135SRashmica Gupta     }
95190f6d06SJiaqing Zhao     return defaultValue;
96e7efe135SRashmica Gupta }
97e7efe135SRashmica Gupta 
98e7efe135SRashmica Gupta bool isCalculationType(const std::string& interface)
99e7efe135SRashmica Gupta {
100e7efe135SRashmica Gupta     auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
101e7efe135SRashmica Gupta                          interface);
102e7efe135SRashmica Gupta     if (itr != calculationIfaces.end())
103e7efe135SRashmica Gupta     {
104e7efe135SRashmica Gupta         return true;
105e7efe135SRashmica Gupta     }
106e7efe135SRashmica Gupta     return false;
107e7efe135SRashmica Gupta }
108e7efe135SRashmica Gupta 
109e7efe135SRashmica Gupta const std::string getThresholdType(const std::string& direction,
11005b1d417SRashmica Gupta                                    const std::string& severity)
111e7efe135SRashmica Gupta {
112e7efe135SRashmica Gupta     std::string suffix;
113e7efe135SRashmica Gupta 
114e7efe135SRashmica Gupta     if (direction == "less than")
115e7efe135SRashmica Gupta     {
116e7efe135SRashmica Gupta         suffix = "Low";
117e7efe135SRashmica Gupta     }
118e7efe135SRashmica Gupta     else if (direction == "greater than")
119e7efe135SRashmica Gupta     {
120e7efe135SRashmica Gupta         suffix = "High";
121e7efe135SRashmica Gupta     }
122e7efe135SRashmica Gupta     else
123e7efe135SRashmica Gupta     {
124e7efe135SRashmica Gupta         throw std::invalid_argument(
125e7efe135SRashmica Gupta             "Invalid threshold direction specified in entity manager");
126e7efe135SRashmica Gupta     }
12705b1d417SRashmica Gupta     return severity + suffix;
12805b1d417SRashmica Gupta }
12905b1d417SRashmica Gupta 
13005b1d417SRashmica Gupta std::string getSeverityField(const PropertyMap& propertyMap)
13105b1d417SRashmica Gupta {
13205b1d417SRashmica Gupta     static const std::array thresholdTypes{"Warning", "Critical",
13305b1d417SRashmica Gupta                                            "PerformanceLoss", "SoftShutdown",
13405b1d417SRashmica Gupta                                            "HardShutdown"};
13505b1d417SRashmica Gupta 
13605b1d417SRashmica Gupta     std::string severity;
13705b1d417SRashmica Gupta     if (auto itr = propertyMap.find("Severity"); itr != propertyMap.end())
13805b1d417SRashmica Gupta     {
13905b1d417SRashmica Gupta         /* Severity should be a string, but can be an unsigned int */
14005b1d417SRashmica Gupta         if (std::holds_alternative<std::string>(itr->second))
14105b1d417SRashmica Gupta         {
14205b1d417SRashmica Gupta             severity = std::get<std::string>(itr->second);
14305b1d417SRashmica Gupta             if (0 == std::ranges::count(thresholdTypes, severity))
14405b1d417SRashmica Gupta             {
14505b1d417SRashmica Gupta                 throw std::invalid_argument(
14605b1d417SRashmica Gupta                     "Invalid threshold severity specified in entity manager");
14705b1d417SRashmica Gupta             }
14805b1d417SRashmica Gupta         }
14905b1d417SRashmica Gupta         else
15005b1d417SRashmica Gupta         {
1511226f208SPatrick Williams             auto sev = getNumberFromConfig<uint64_t>(propertyMap, "Severity",
1521226f208SPatrick Williams                                                      true);
15305b1d417SRashmica Gupta             /* Checking bounds ourselves so we throw invalid argument on
15405b1d417SRashmica Gupta              * invalid user input */
15505b1d417SRashmica Gupta             if (sev >= thresholdTypes.size())
15605b1d417SRashmica Gupta             {
15705b1d417SRashmica Gupta                 throw std::invalid_argument(
15805b1d417SRashmica Gupta                     "Invalid threshold severity specified in entity manager");
15905b1d417SRashmica Gupta             }
16005b1d417SRashmica Gupta             severity = thresholdTypes.at(sev);
16105b1d417SRashmica Gupta         }
16205b1d417SRashmica Gupta     }
16305b1d417SRashmica Gupta     return severity;
164e7efe135SRashmica Gupta }
165e7efe135SRashmica Gupta 
16691799dbdSTao Lin void parseThresholds(Json& thresholds, const PropertyMap& propertyMap,
16791799dbdSTao Lin                      const std::string& entityInterface = "")
168e7efe135SRashmica Gupta {
169e7efe135SRashmica Gupta     std::string direction;
170e7efe135SRashmica Gupta 
171e7efe135SRashmica Gupta     auto value = getNumberFromConfig<double>(propertyMap, "Value", true);
172e7efe135SRashmica Gupta 
17305b1d417SRashmica Gupta     auto severity = getSeverityField(propertyMap);
17405b1d417SRashmica Gupta 
17505b1d417SRashmica Gupta     if (auto itr = propertyMap.find("Direction"); itr != propertyMap.end())
176e7efe135SRashmica Gupta     {
177e7efe135SRashmica Gupta         direction = std::get<std::string>(itr->second);
178e7efe135SRashmica Gupta     }
179e7efe135SRashmica Gupta 
180e7efe135SRashmica Gupta     auto threshold = getThresholdType(direction, severity);
181e7efe135SRashmica Gupta     thresholds[threshold] = value;
1821dff7dceSRashmica Gupta 
1831226f208SPatrick Williams     auto hysteresis = getNumberFromConfig<double>(propertyMap, "Hysteresis",
1841226f208SPatrick Williams                                                   false);
1851dff7dceSRashmica Gupta     if (hysteresis != std::numeric_limits<double>::quiet_NaN())
1861dff7dceSRashmica Gupta     {
1871dff7dceSRashmica Gupta         thresholds[threshold + "Hysteresis"] = hysteresis;
1881dff7dceSRashmica Gupta     }
18991799dbdSTao Lin 
19091799dbdSTao Lin     if (!entityInterface.empty())
19191799dbdSTao Lin     {
19291799dbdSTao Lin         thresholds[threshold + "Direction"] = entityInterface;
19391799dbdSTao Lin     }
194e7efe135SRashmica Gupta }
195e7efe135SRashmica Gupta 
196e7efe135SRashmica Gupta void VirtualSensor::parseConfigInterface(const PropertyMap& propertyMap,
197e7efe135SRashmica Gupta                                          const std::string& sensorType,
198e7efe135SRashmica Gupta                                          const std::string& interface)
199e7efe135SRashmica Gupta {
200e7efe135SRashmica Gupta     /* Parse sensors / DBus params */
201e7efe135SRashmica Gupta     if (auto itr = propertyMap.find("Sensors"); itr != propertyMap.end())
202e7efe135SRashmica Gupta     {
203e7efe135SRashmica Gupta         auto sensors = std::get<std::vector<std::string>>(itr->second);
204e7efe135SRashmica Gupta         for (auto sensor : sensors)
205e7efe135SRashmica Gupta         {
206e7efe135SRashmica Gupta             std::replace(sensor.begin(), sensor.end(), ' ', '_');
207e7efe135SRashmica Gupta             auto sensorObjPath = sensorDbusPath + sensorType + "/" + sensor;
208e7efe135SRashmica Gupta 
2091226f208SPatrick Williams             auto paramPtr = std::make_unique<SensorParam>(bus, sensorObjPath,
210f2e94221STao Lin                                                           *this);
211e7efe135SRashmica Gupta             symbols.create_variable(sensor);
212e7efe135SRashmica Gupta             paramMap.emplace(std::move(sensor), std::move(paramPtr));
213e7efe135SRashmica Gupta         }
214e7efe135SRashmica Gupta     }
215e7efe135SRashmica Gupta     /* Get expression string */
216e7efe135SRashmica Gupta     if (!isCalculationType(interface))
217e7efe135SRashmica Gupta     {
218e7efe135SRashmica Gupta         throw std::invalid_argument("Invalid expression in interface");
219e7efe135SRashmica Gupta     }
220e7efe135SRashmica Gupta     exprStr = interface;
221e7efe135SRashmica Gupta 
222e7efe135SRashmica Gupta     /* Get optional min and max input and output values */
223e7efe135SRashmica Gupta     ValueIface::maxValue(
224e7efe135SRashmica Gupta         getNumberFromConfig<double>(propertyMap, "MaxValue", false));
225e7efe135SRashmica Gupta     ValueIface::minValue(
226e7efe135SRashmica Gupta         getNumberFromConfig<double>(propertyMap, "MinValue", false));
227e7efe135SRashmica Gupta     maxValidInput =
228190f6d06SJiaqing Zhao         getNumberFromConfig<double>(propertyMap, "MaxValidInput", false,
229190f6d06SJiaqing Zhao                                     std::numeric_limits<double>::infinity());
230e7efe135SRashmica Gupta     minValidInput =
231190f6d06SJiaqing Zhao         getNumberFromConfig<double>(propertyMap, "MinValidInput", false,
232190f6d06SJiaqing Zhao                                     -std::numeric_limits<double>::infinity());
233e7efe135SRashmica Gupta }
234e7efe135SRashmica Gupta 
235ce675228SMatt Spinler void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
236ce675228SMatt Spinler                                       const std::string& objPath)
237abcc94faSVijay Khemka {
238abcc94faSVijay Khemka     static const Json empty{};
239abcc94faSVijay Khemka 
240abcc94faSVijay Khemka     /* Get threshold values if defined in config */
241abcc94faSVijay Khemka     auto threshold = sensorConfig.value("Threshold", empty);
242f15189e3SMatt Spinler 
2433e99919bSRashmica Gupta     createThresholds(threshold, objPath);
244abcc94faSVijay Khemka 
245f6443742SHarvey Wu     /* Get MaxValue, MinValue setting if defined in config */
246f6443742SHarvey Wu     auto confDesc = sensorConfig.value("Desc", empty);
247f6443742SHarvey Wu     if (auto maxConf = confDesc.find("MaxValue");
248f6443742SHarvey Wu         maxConf != confDesc.end() && maxConf->is_number())
249f6443742SHarvey Wu     {
250f6443742SHarvey Wu         ValueIface::maxValue(maxConf->get<double>());
251f6443742SHarvey Wu     }
252f6443742SHarvey Wu     if (auto minConf = confDesc.find("MinValue");
253f6443742SHarvey Wu         minConf != confDesc.end() && minConf->is_number())
254f6443742SHarvey Wu     {
255f6443742SHarvey Wu         ValueIface::minValue(minConf->get<double>());
256f6443742SHarvey Wu     }
257f6443742SHarvey Wu 
2580fcf0e1cSLei YU     /* Get optional association */
2590fcf0e1cSLei YU     auto assocJson = sensorConfig.value("Associations", empty);
2600fcf0e1cSLei YU     if (!assocJson.empty())
2610fcf0e1cSLei YU     {
2620fcf0e1cSLei YU         auto assocs = getAssociationsFromJson(assocJson);
2630fcf0e1cSLei YU         if (!assocs.empty())
2640fcf0e1cSLei YU         {
2650fcf0e1cSLei YU             associationIface =
2660fcf0e1cSLei YU                 std::make_unique<AssociationObject>(bus, objPath.c_str());
2670fcf0e1cSLei YU             associationIface->associations(assocs);
2680fcf0e1cSLei YU         }
2690fcf0e1cSLei YU     }
2700fcf0e1cSLei YU 
271abcc94faSVijay Khemka     /* Get expression string */
27203c4c8e2SPatrick Williams     static constexpr auto exprKey = "Expression";
27303c4c8e2SPatrick Williams     if (sensorConfig.contains(exprKey))
27403c4c8e2SPatrick Williams     {
275a959678cSPatrick Williams         auto& ref = sensorConfig.at(exprKey);
27603c4c8e2SPatrick Williams         if (ref.is_array())
27703c4c8e2SPatrick Williams         {
27803c4c8e2SPatrick Williams             exprStr = std::string{};
27903c4c8e2SPatrick Williams             for (auto& s : ref)
28003c4c8e2SPatrick Williams             {
28103c4c8e2SPatrick Williams                 exprStr += s;
28203c4c8e2SPatrick Williams             }
28303c4c8e2SPatrick Williams         }
28403c4c8e2SPatrick Williams         else if (ref.is_string())
28503c4c8e2SPatrick Williams         {
28603c4c8e2SPatrick Williams             exprStr = std::string{ref};
28703c4c8e2SPatrick Williams         }
28803c4c8e2SPatrick Williams     }
289abcc94faSVijay Khemka 
290abcc94faSVijay Khemka     /* Get all the parameter listed in configuration */
291abcc94faSVijay Khemka     auto params = sensorConfig.value("Params", empty);
292abcc94faSVijay Khemka 
293abcc94faSVijay Khemka     /* Check for constant parameter */
294abcc94faSVijay Khemka     const auto& consParams = params.value("ConstParam", empty);
295abcc94faSVijay Khemka     if (!consParams.empty())
296abcc94faSVijay Khemka     {
297abcc94faSVijay Khemka         for (auto& j : consParams)
298abcc94faSVijay Khemka         {
299abcc94faSVijay Khemka             if (j.find("ParamName") != j.end())
300abcc94faSVijay Khemka             {
301abcc94faSVijay Khemka                 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
3023ed9a516SVijay Khemka                 std::string name = j["ParamName"];
3033ed9a516SVijay Khemka                 symbols.create_variable(name);
3043ed9a516SVijay Khemka                 paramMap.emplace(std::move(name), std::move(paramPtr));
305abcc94faSVijay Khemka             }
306abcc94faSVijay Khemka             else
307abcc94faSVijay Khemka             {
308abcc94faSVijay Khemka                 /* Invalid configuration */
309abcc94faSVijay Khemka                 throw std::invalid_argument(
310abcc94faSVijay Khemka                     "ParamName not found in configuration");
311abcc94faSVijay Khemka             }
312abcc94faSVijay Khemka         }
313abcc94faSVijay Khemka     }
314abcc94faSVijay Khemka 
3157452a867SVijay Khemka     /* Check for dbus parameter */
3167452a867SVijay Khemka     auto dbusParams = params.value("DbusParam", empty);
3177452a867SVijay Khemka     if (!dbusParams.empty())
3187452a867SVijay Khemka     {
3197452a867SVijay Khemka         for (auto& j : dbusParams)
3207452a867SVijay Khemka         {
3217452a867SVijay Khemka             /* Get parameter dbus sensor descriptor */
3227452a867SVijay Khemka             auto desc = j.value("Desc", empty);
3237452a867SVijay Khemka             if ((!desc.empty()) && (j.find("ParamName") != j.end()))
3247452a867SVijay Khemka             {
3257452a867SVijay Khemka                 std::string sensorType = desc.value("SensorType", "");
3267452a867SVijay Khemka                 std::string name = desc.value("Name", "");
3277452a867SVijay Khemka 
3287452a867SVijay Khemka                 if (!sensorType.empty() && !name.empty())
3297452a867SVijay Khemka                 {
3301204b433SGeorge Liu                     auto path = sensorDbusPath + sensorType + "/" + name;
3317452a867SVijay Khemka 
3321226f208SPatrick Williams                     auto paramPtr = std::make_unique<SensorParam>(bus, path,
333f2e94221STao Lin                                                                   *this);
3341204b433SGeorge Liu                     std::string paramName = j["ParamName"];
3351204b433SGeorge Liu                     symbols.create_variable(paramName);
3361204b433SGeorge Liu                     paramMap.emplace(std::move(paramName), std::move(paramPtr));
3377452a867SVijay Khemka                 }
3387452a867SVijay Khemka             }
3397452a867SVijay Khemka         }
3407452a867SVijay Khemka     }
341abcc94faSVijay Khemka 
3423ed9a516SVijay Khemka     symbols.add_constants();
3439f1ef4f5SMatt Spinler     symbols.add_package(vecopsPackage);
3440ab9d838SLei YU     symbols.add_function("maxIgnoreNaN", funcMaxIgnoreNaN);
34587d35115SLei YU     symbols.add_function("sumIgnoreNaN", funcSumIgnoreNaN);
346c77b6b3fSLei YU     symbols.add_function("ifNan", funcIfNan);
3470ab9d838SLei YU 
3483ed9a516SVijay Khemka     expression.register_symbol_table(symbols);
3493ed9a516SVijay Khemka 
3503ed9a516SVijay Khemka     /* parser from exprtk */
3513ed9a516SVijay Khemka     exprtk::parser<double> parser{};
352ddc6dcd6SMatt Spinler     if (!parser.compile(exprStr, expression))
353ddc6dcd6SMatt Spinler     {
35482b39c66SPatrick Williams         error("Expression compilation failed");
355ddc6dcd6SMatt Spinler 
356ddc6dcd6SMatt Spinler         for (std::size_t i = 0; i < parser.error_count(); ++i)
357ddc6dcd6SMatt Spinler         {
35882b39c66SPatrick Williams             auto err = parser.get_error(i);
35982b39c66SPatrick Williams             error("Error parsing token at {POSITION}: {ERROR}", "POSITION",
36082b39c66SPatrick Williams                   err.token.position, "TYPE",
36182b39c66SPatrick Williams                   exprtk::parser_error::to_str(err.mode), "ERROR",
36282b39c66SPatrick Williams                   err.diagnostic);
363ddc6dcd6SMatt Spinler         }
364ddc6dcd6SMatt Spinler         throw std::runtime_error("Expression compilation failed");
365ddc6dcd6SMatt Spinler     }
3663ed9a516SVijay Khemka 
367abcc94faSVijay Khemka     /* Print all parameters for debug purpose only */
368abcc94faSVijay Khemka     if (DEBUG)
369abcc94faSVijay Khemka         printParams(paramMap);
370abcc94faSVijay Khemka }
371abcc94faSVijay Khemka 
372dc777015STao Lin void VirtualSensor::createAssociation(const std::string& objPath,
373dc777015STao Lin                                       const std::string& entityPath)
374dc777015STao Lin {
375dc777015STao Lin     if (objPath.empty() || entityPath.empty())
376dc777015STao Lin     {
377dc777015STao Lin         return;
378dc777015STao Lin     }
379dc777015STao Lin 
380dc777015STao Lin     std::filesystem::path p(entityPath);
381dc777015STao Lin     auto assocsDbus =
382dc777015STao Lin         AssociationList{{"chassis", "all_sensors", p.parent_path().string()}};
3831226f208SPatrick Williams     associationIface = std::make_unique<AssociationObject>(bus,
3841226f208SPatrick Williams                                                            objPath.c_str());
385dc777015STao Lin     associationIface->associations(assocsDbus);
386dc777015STao Lin }
387dc777015STao Lin 
388e7efe135SRashmica Gupta void VirtualSensor::initVirtualSensor(const InterfaceMap& interfaceMap,
389e7efe135SRashmica Gupta                                       const std::string& objPath,
390e7efe135SRashmica Gupta                                       const std::string& sensorType,
391e7efe135SRashmica Gupta                                       const std::string& calculationIface)
392e7efe135SRashmica Gupta {
393e7efe135SRashmica Gupta     Json thresholds;
3941226f208SPatrick Williams     const std::string vsThresholdsIntf = calculationIface +
3951226f208SPatrick Williams                                          vsThresholdsIfaceSuffix;
396e7efe135SRashmica Gupta 
397e7efe135SRashmica Gupta     for (const auto& [interface, propertyMap] : interfaceMap)
398e7efe135SRashmica Gupta     {
399e7efe135SRashmica Gupta         /* Each threshold is on it's own interface with a number as a suffix
400e7efe135SRashmica Gupta          * eg xyz.openbmc_project.Configuration.ModifiedMedian.Thresholds1 */
401e7efe135SRashmica Gupta         if (interface.find(vsThresholdsIntf) != std::string::npos)
402e7efe135SRashmica Gupta         {
40391799dbdSTao Lin             parseThresholds(thresholds, propertyMap, interface);
404e7efe135SRashmica Gupta         }
405e7efe135SRashmica Gupta         else if (interface == calculationIface)
406e7efe135SRashmica Gupta         {
407e7efe135SRashmica Gupta             parseConfigInterface(propertyMap, sensorType, interface);
408e7efe135SRashmica Gupta         }
409e7efe135SRashmica Gupta     }
410e7efe135SRashmica Gupta 
411e7efe135SRashmica Gupta     createThresholds(thresholds, objPath);
412e7efe135SRashmica Gupta     symbols.add_constants();
413e7efe135SRashmica Gupta     symbols.add_package(vecopsPackage);
414e7efe135SRashmica Gupta     expression.register_symbol_table(symbols);
415e7efe135SRashmica Gupta 
416dc777015STao Lin     createAssociation(objPath, entityPath);
417e7efe135SRashmica Gupta     /* Print all parameters for debug purpose only */
418e7efe135SRashmica Gupta     if (DEBUG)
419e7efe135SRashmica Gupta     {
420e7efe135SRashmica Gupta         printParams(paramMap);
421e7efe135SRashmica Gupta     }
422e7efe135SRashmica Gupta }
423e7efe135SRashmica Gupta 
424abcc94faSVijay Khemka void VirtualSensor::setSensorValue(double value)
425abcc94faSVijay Khemka {
426543bf668SPatrick Williams     value = std::clamp(value, ValueIface::minValue(), ValueIface::maxValue());
427abcc94faSVijay Khemka     ValueIface::value(value);
428abcc94faSVijay Khemka }
429abcc94faSVijay Khemka 
430304fd0e4SRashmica Gupta double VirtualSensor::calculateValue(const std::string& calculation,
431304fd0e4SRashmica Gupta                                      const VirtualSensor::ParamMap& paramMap)
432e7efe135SRashmica Gupta {
433304fd0e4SRashmica Gupta     auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
434304fd0e4SRashmica Gupta                          calculation);
435304fd0e4SRashmica Gupta     if (itr == calculationIfaces.end())
436304fd0e4SRashmica Gupta     {
437e7efe135SRashmica Gupta         return std::numeric_limits<double>::quiet_NaN();
438e7efe135SRashmica Gupta     }
439304fd0e4SRashmica Gupta     else if (calculation == "xyz.openbmc_project.Configuration.ModifiedMedian")
440304fd0e4SRashmica Gupta     {
441304fd0e4SRashmica Gupta         return calculateModifiedMedianValue(paramMap);
442304fd0e4SRashmica Gupta     }
443f6b7e0a4STao Lin     else if (calculation == "xyz.openbmc_project.Configuration.Maximum")
444f6b7e0a4STao Lin     {
445f6b7e0a4STao Lin         return calculateMaximumValue(paramMap);
446f6b7e0a4STao Lin     }
447304fd0e4SRashmica Gupta     return std::numeric_limits<double>::quiet_NaN();
448304fd0e4SRashmica Gupta }
449304fd0e4SRashmica Gupta 
450304fd0e4SRashmica Gupta bool VirtualSensor::sensorInRange(double value)
451304fd0e4SRashmica Gupta {
452304fd0e4SRashmica Gupta     if (value <= this->maxValidInput && value >= this->minValidInput)
453304fd0e4SRashmica Gupta     {
454304fd0e4SRashmica Gupta         return true;
455304fd0e4SRashmica Gupta     }
456304fd0e4SRashmica Gupta     return false;
457304fd0e4SRashmica Gupta }
458e7efe135SRashmica Gupta 
459abcc94faSVijay Khemka void VirtualSensor::updateVirtualSensor()
4603ed9a516SVijay Khemka {
4613ed9a516SVijay Khemka     for (auto& param : paramMap)
4623ed9a516SVijay Khemka     {
4633ed9a516SVijay Khemka         auto& name = param.first;
4643ed9a516SVijay Khemka         auto& data = param.second;
4653ed9a516SVijay Khemka         if (auto var = symbols.get_variable(name))
4663ed9a516SVijay Khemka         {
4673ed9a516SVijay Khemka             var->ref() = data->getParamValue();
4683ed9a516SVijay Khemka         }
4693ed9a516SVijay Khemka         else
4703ed9a516SVijay Khemka         {
4713ed9a516SVijay Khemka             /* Invalid parameter */
4723ed9a516SVijay Khemka             throw std::invalid_argument("ParamName not found in symbols");
4733ed9a516SVijay Khemka         }
4743ed9a516SVijay Khemka     }
4751226f208SPatrick Williams     auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
4761226f208SPatrick Williams                          exprStr);
477304fd0e4SRashmica Gupta     auto val = (itr == calculationIfaces.end())
478304fd0e4SRashmica Gupta                    ? expression.value()
479304fd0e4SRashmica Gupta                    : calculateValue(exprStr, paramMap);
48032a7156bSVijay Khemka 
48132a7156bSVijay Khemka     /* Set sensor value to dbus interface */
4823ed9a516SVijay Khemka     setSensorValue(val);
48332a7156bSVijay Khemka 
4843ed9a516SVijay Khemka     if (DEBUG)
485e7efe135SRashmica Gupta     {
486fbd7145eSPatrick Williams         debug("Sensor {NAME} = {VALUE}", "NAME", this->name, "VALUE", val);
487e7efe135SRashmica Gupta     }
48832a7156bSVijay Khemka 
4898f5e6119SMatt Spinler     /* Check sensor thresholds and log required message */
490b306b03dSMatt Spinler     checkThresholds(val, perfLossIface);
491fdb826d5SPatrick Williams     checkThresholds(val, warningIface);
492fdb826d5SPatrick Williams     checkThresholds(val, criticalIface);
493fdb826d5SPatrick Williams     checkThresholds(val, softShutdownIface);
494fdb826d5SPatrick Williams     checkThresholds(val, hardShutdownIface);
4953ed9a516SVijay Khemka }
496abcc94faSVijay Khemka 
497304fd0e4SRashmica Gupta double VirtualSensor::calculateModifiedMedianValue(
498304fd0e4SRashmica Gupta     const VirtualSensor::ParamMap& paramMap)
499304fd0e4SRashmica Gupta {
500304fd0e4SRashmica Gupta     std::vector<double> values;
501304fd0e4SRashmica Gupta 
502304fd0e4SRashmica Gupta     for (auto& param : paramMap)
503304fd0e4SRashmica Gupta     {
504304fd0e4SRashmica Gupta         auto& name = param.first;
505304fd0e4SRashmica Gupta         if (auto var = symbols.get_variable(name))
506304fd0e4SRashmica Gupta         {
507304fd0e4SRashmica Gupta             if (!sensorInRange(var->ref()))
508304fd0e4SRashmica Gupta             {
509304fd0e4SRashmica Gupta                 continue;
510304fd0e4SRashmica Gupta             }
511304fd0e4SRashmica Gupta             values.push_back(var->ref());
512304fd0e4SRashmica Gupta         }
513304fd0e4SRashmica Gupta     }
514304fd0e4SRashmica Gupta 
515304fd0e4SRashmica Gupta     size_t size = values.size();
516304fd0e4SRashmica Gupta     std::sort(values.begin(), values.end());
517304fd0e4SRashmica Gupta     switch (size)
518304fd0e4SRashmica Gupta     {
519304fd0e4SRashmica Gupta         case 2:
520304fd0e4SRashmica Gupta             /* Choose biggest value */
521304fd0e4SRashmica Gupta             return values.at(1);
522304fd0e4SRashmica Gupta         case 0:
523304fd0e4SRashmica Gupta             return std::numeric_limits<double>::quiet_NaN();
524304fd0e4SRashmica Gupta         default:
525304fd0e4SRashmica Gupta             /* Choose median value */
526304fd0e4SRashmica Gupta             if (size % 2 == 0)
527304fd0e4SRashmica Gupta             {
528304fd0e4SRashmica Gupta                 // Average of the two middle values
529304fd0e4SRashmica Gupta                 return (values.at(size / 2) + values.at(size / 2 - 1)) / 2;
530304fd0e4SRashmica Gupta             }
531304fd0e4SRashmica Gupta             else
532304fd0e4SRashmica Gupta             {
533304fd0e4SRashmica Gupta                 return values.at((size - 1) / 2);
534304fd0e4SRashmica Gupta             }
535304fd0e4SRashmica Gupta     }
536304fd0e4SRashmica Gupta }
537304fd0e4SRashmica Gupta 
538f6b7e0a4STao Lin double VirtualSensor::calculateMaximumValue(
539f6b7e0a4STao Lin     const VirtualSensor::ParamMap& paramMap)
540f6b7e0a4STao Lin {
541f6b7e0a4STao Lin     std::vector<double> values;
542f6b7e0a4STao Lin 
543f6b7e0a4STao Lin     for (auto& param : paramMap)
544f6b7e0a4STao Lin     {
545f6b7e0a4STao Lin         auto& name = param.first;
546f6b7e0a4STao Lin         if (auto var = symbols.get_variable(name))
547f6b7e0a4STao Lin         {
548f6b7e0a4STao Lin             if (!sensorInRange(var->ref()))
549f6b7e0a4STao Lin             {
550f6b7e0a4STao Lin                 continue;
551f6b7e0a4STao Lin             }
552f6b7e0a4STao Lin             values.push_back(var->ref());
553f6b7e0a4STao Lin         }
554f6b7e0a4STao Lin     }
555f6b7e0a4STao Lin     auto maxIt = std::max_element(values.begin(), values.end());
556f6b7e0a4STao Lin     if (maxIt == values.end())
557f6b7e0a4STao Lin     {
558f6b7e0a4STao Lin         return std::numeric_limits<double>::quiet_NaN();
559f6b7e0a4STao Lin     }
560f6b7e0a4STao Lin     return *maxIt;
561f6b7e0a4STao Lin }
562f6b7e0a4STao Lin 
5633e99919bSRashmica Gupta void VirtualSensor::createThresholds(const Json& threshold,
5643e99919bSRashmica Gupta                                      const std::string& objPath)
5653e99919bSRashmica Gupta {
5663e99919bSRashmica Gupta     if (threshold.empty())
5673e99919bSRashmica Gupta     {
5683e99919bSRashmica Gupta         return;
5693e99919bSRashmica Gupta     }
5703e99919bSRashmica Gupta     // Only create the threshold interfaces if
5713e99919bSRashmica Gupta     // at least one of their values is present.
5723e99919bSRashmica Gupta     if (threshold.contains("CriticalHigh") || threshold.contains("CriticalLow"))
5733e99919bSRashmica Gupta     {
5743e99919bSRashmica Gupta         criticalIface =
5753e99919bSRashmica Gupta             std::make_unique<Threshold<CriticalObject>>(bus, objPath.c_str());
5763e99919bSRashmica Gupta 
57791799dbdSTao Lin         if (threshold.contains("CriticalHigh"))
57891799dbdSTao Lin         {
57991799dbdSTao Lin             criticalIface->setEntityInterfaceHigh(
58091799dbdSTao Lin                 threshold.value("CriticalHighDirection", ""));
58191799dbdSTao Lin             if (DEBUG)
58291799dbdSTao Lin             {
58391799dbdSTao Lin                 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
58491799dbdSTao Lin                       "INTF", threshold.value("CriticalHighDirection", ""));
58591799dbdSTao Lin             }
58691799dbdSTao Lin         }
58791799dbdSTao Lin         if (threshold.contains("CriticalLow"))
58891799dbdSTao Lin         {
58991799dbdSTao Lin             criticalIface->setEntityInterfaceLow(
59091799dbdSTao Lin                 threshold.value("CriticalLowDirection", ""));
59191799dbdSTao Lin             if (DEBUG)
59291799dbdSTao Lin             {
59391799dbdSTao Lin                 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
59491799dbdSTao Lin                       "INTF", threshold.value("CriticalLowDirection", ""));
59591799dbdSTao Lin             }
59691799dbdSTao Lin         }
59791799dbdSTao Lin 
59891799dbdSTao Lin         criticalIface->setEntityPath(entityPath);
59991799dbdSTao Lin         if (DEBUG)
60091799dbdSTao Lin         {
60191799dbdSTao Lin             debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath,
60291799dbdSTao Lin                   "PATH", entityPath);
60391799dbdSTao Lin         }
604a291ce1aSMatt Spinler 
605a291ce1aSMatt Spinler         criticalIface->criticalHigh(threshold.value(
606a291ce1aSMatt Spinler             "CriticalHigh", std::numeric_limits<double>::quiet_NaN()));
607a291ce1aSMatt Spinler         criticalIface->criticalLow(threshold.value(
608a291ce1aSMatt Spinler             "CriticalLow", std::numeric_limits<double>::quiet_NaN()));
609a291ce1aSMatt Spinler         criticalIface->setHighHysteresis(
610a291ce1aSMatt Spinler             threshold.value("CriticalHighHysteresis", defaultHysteresis));
611a291ce1aSMatt Spinler         criticalIface->setLowHysteresis(
612a291ce1aSMatt Spinler             threshold.value("CriticalLowHysteresis", defaultHysteresis));
6133e99919bSRashmica Gupta     }
6143e99919bSRashmica Gupta 
6153e99919bSRashmica Gupta     if (threshold.contains("WarningHigh") || threshold.contains("WarningLow"))
6163e99919bSRashmica Gupta     {
6173e99919bSRashmica Gupta         warningIface =
6183e99919bSRashmica Gupta             std::make_unique<Threshold<WarningObject>>(bus, objPath.c_str());
6193e99919bSRashmica Gupta 
62091799dbdSTao Lin         if (threshold.contains("WarningHigh"))
62191799dbdSTao Lin         {
62291799dbdSTao Lin             warningIface->setEntityInterfaceHigh(
62391799dbdSTao Lin                 threshold.value("WarningHighDirection", ""));
62491799dbdSTao Lin             if (DEBUG)
62591799dbdSTao Lin             {
62691799dbdSTao Lin                 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
62791799dbdSTao Lin                       "INTF", threshold.value("WarningHighDirection", ""));
62891799dbdSTao Lin             }
62991799dbdSTao Lin         }
63091799dbdSTao Lin         if (threshold.contains("WarningLow"))
63191799dbdSTao Lin         {
63291799dbdSTao Lin             warningIface->setEntityInterfaceLow(
63391799dbdSTao Lin                 threshold.value("WarningLowDirection", ""));
63491799dbdSTao Lin             if (DEBUG)
63591799dbdSTao Lin             {
63691799dbdSTao Lin                 debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
63791799dbdSTao Lin                       "INTF", threshold.value("WarningLowDirection", ""));
63891799dbdSTao Lin             }
63991799dbdSTao Lin         }
64091799dbdSTao Lin 
64191799dbdSTao Lin         warningIface->setEntityPath(entityPath);
64291799dbdSTao Lin         if (DEBUG)
64391799dbdSTao Lin         {
64491799dbdSTao Lin             debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath,
64591799dbdSTao Lin                   "PATH", entityPath);
64691799dbdSTao Lin         }
647a291ce1aSMatt Spinler 
648a291ce1aSMatt Spinler         warningIface->warningHigh(threshold.value(
649a291ce1aSMatt Spinler             "WarningHigh", std::numeric_limits<double>::quiet_NaN()));
650a291ce1aSMatt Spinler         warningIface->warningLow(threshold.value(
651a291ce1aSMatt Spinler             "WarningLow", std::numeric_limits<double>::quiet_NaN()));
652a291ce1aSMatt Spinler         warningIface->setHighHysteresis(
653a291ce1aSMatt Spinler             threshold.value("WarningHighHysteresis", defaultHysteresis));
654a291ce1aSMatt Spinler         warningIface->setLowHysteresis(
655a291ce1aSMatt Spinler             threshold.value("WarningLowHysteresis", defaultHysteresis));
6563e99919bSRashmica Gupta     }
6573e99919bSRashmica Gupta 
6583e99919bSRashmica Gupta     if (threshold.contains("HardShutdownHigh") ||
6593e99919bSRashmica Gupta         threshold.contains("HardShutdownLow"))
6603e99919bSRashmica Gupta     {
6613e99919bSRashmica Gupta         hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>(
6623e99919bSRashmica Gupta             bus, objPath.c_str());
6633e99919bSRashmica Gupta 
6643e99919bSRashmica Gupta         hardShutdownIface->hardShutdownHigh(threshold.value(
6653e99919bSRashmica Gupta             "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
6663e99919bSRashmica Gupta         hardShutdownIface->hardShutdownLow(threshold.value(
6673e99919bSRashmica Gupta             "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
6681dff7dceSRashmica Gupta         hardShutdownIface->setHighHysteresis(
6691dff7dceSRashmica Gupta             threshold.value("HardShutdownHighHysteresis", defaultHysteresis));
6701dff7dceSRashmica Gupta         hardShutdownIface->setLowHysteresis(
6711dff7dceSRashmica Gupta             threshold.value("HardShutdownLowHysteresis", defaultHysteresis));
6723e99919bSRashmica Gupta     }
6733e99919bSRashmica Gupta 
6743e99919bSRashmica Gupta     if (threshold.contains("SoftShutdownHigh") ||
6753e99919bSRashmica Gupta         threshold.contains("SoftShutdownLow"))
6763e99919bSRashmica Gupta     {
6773e99919bSRashmica Gupta         softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>(
6783e99919bSRashmica Gupta             bus, objPath.c_str());
6793e99919bSRashmica Gupta 
6803e99919bSRashmica Gupta         softShutdownIface->softShutdownHigh(threshold.value(
6813e99919bSRashmica Gupta             "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
6823e99919bSRashmica Gupta         softShutdownIface->softShutdownLow(threshold.value(
6833e99919bSRashmica Gupta             "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
6841dff7dceSRashmica Gupta         softShutdownIface->setHighHysteresis(
6851dff7dceSRashmica Gupta             threshold.value("SoftShutdownHighHysteresis", defaultHysteresis));
6861dff7dceSRashmica Gupta         softShutdownIface->setLowHysteresis(
6871dff7dceSRashmica Gupta             threshold.value("SoftShutdownLowHysteresis", defaultHysteresis));
6883e99919bSRashmica Gupta     }
6893e99919bSRashmica Gupta 
6903e99919bSRashmica Gupta     if (threshold.contains("PerformanceLossHigh") ||
6913e99919bSRashmica Gupta         threshold.contains("PerformanceLossLow"))
6923e99919bSRashmica Gupta     {
6933e99919bSRashmica Gupta         perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>(
6943e99919bSRashmica Gupta             bus, objPath.c_str());
6953e99919bSRashmica Gupta 
6963e99919bSRashmica Gupta         perfLossIface->performanceLossHigh(threshold.value(
6973e99919bSRashmica Gupta             "PerformanceLossHigh", std::numeric_limits<double>::quiet_NaN()));
6983e99919bSRashmica Gupta         perfLossIface->performanceLossLow(threshold.value(
6993e99919bSRashmica Gupta             "PerformanceLossLow", std::numeric_limits<double>::quiet_NaN()));
7001dff7dceSRashmica Gupta         perfLossIface->setHighHysteresis(threshold.value(
7011dff7dceSRashmica Gupta             "PerformanceLossHighHysteresis", defaultHysteresis));
7021dff7dceSRashmica Gupta         perfLossIface->setLowHysteresis(
7031dff7dceSRashmica Gupta             threshold.value("PerformanceLossLowHysteresis", defaultHysteresis));
7043e99919bSRashmica Gupta     }
7053e99919bSRashmica Gupta }
7063e99919bSRashmica Gupta 
707e7efe135SRashmica Gupta ManagedObjectType VirtualSensors::getObjectsFromDBus()
708e7efe135SRashmica Gupta {
709e7efe135SRashmica Gupta     ManagedObjectType objects;
710e7efe135SRashmica Gupta 
711e7efe135SRashmica Gupta     try
712e7efe135SRashmica Gupta     {
713f6825b91SNan Zhou         auto method = bus.new_method_call("xyz.openbmc_project.EntityManager",
714f6825b91SNan Zhou                                           "/xyz/openbmc_project/inventory",
715e7efe135SRashmica Gupta                                           "org.freedesktop.DBus.ObjectManager",
716e7efe135SRashmica Gupta                                           "GetManagedObjects");
717e7efe135SRashmica Gupta         auto reply = bus.call(method);
718e7efe135SRashmica Gupta         reply.read(objects);
719e7efe135SRashmica Gupta     }
7208e11cccbSPatrick Williams     catch (const sdbusplus::exception_t& ex)
721e7efe135SRashmica Gupta     {
722e7efe135SRashmica Gupta         // If entity manager isn't running yet, keep going.
723e7efe135SRashmica Gupta         if (std::string("org.freedesktop.DBus.Error.ServiceUnknown") !=
724e7efe135SRashmica Gupta             ex.name())
725e7efe135SRashmica Gupta         {
72671b9c116SMatt Spinler             error("Could not reach entity-manager: {ERROR}", "ERROR", ex);
72771b9c116SMatt Spinler             throw;
728e7efe135SRashmica Gupta         }
729e7efe135SRashmica Gupta     }
730e7efe135SRashmica Gupta 
731e7efe135SRashmica Gupta     return objects;
732e7efe135SRashmica Gupta }
733e7efe135SRashmica Gupta 
7348e11cccbSPatrick Williams void VirtualSensors::propertiesChanged(sdbusplus::message_t& msg)
735e7efe135SRashmica Gupta {
736e7efe135SRashmica Gupta     std::string path;
737e7efe135SRashmica Gupta     PropertyMap properties;
738e7efe135SRashmica Gupta 
739e7efe135SRashmica Gupta     msg.read(path, properties);
740e7efe135SRashmica Gupta 
741e7efe135SRashmica Gupta     /* We get multiple callbacks for one sensor. 'Type' is a required field and
742e7efe135SRashmica Gupta      * is a unique label so use to to only proceed once per sensor */
743e7efe135SRashmica Gupta     if (properties.contains("Type"))
744e7efe135SRashmica Gupta     {
745e7efe135SRashmica Gupta         if (isCalculationType(path))
746e7efe135SRashmica Gupta         {
747e7efe135SRashmica Gupta             createVirtualSensorsFromDBus(path);
748e7efe135SRashmica Gupta         }
749e7efe135SRashmica Gupta     }
750e7efe135SRashmica Gupta }
751e7efe135SRashmica Gupta 
752abcc94faSVijay Khemka /** @brief Parsing Virtual Sensor config JSON file  */
75332dff21bSPatrick Williams Json VirtualSensors::parseConfigFile()
754abcc94faSVijay Khemka {
75532dff21bSPatrick Williams     using path = std::filesystem::path;
75632dff21bSPatrick Williams     auto configFile = []() -> path {
75732dff21bSPatrick Williams         static constexpr auto name = "virtual_sensor_config.json";
75832dff21bSPatrick Williams 
75932dff21bSPatrick Williams         for (auto pathSeg : {std::filesystem::current_path(),
76032dff21bSPatrick Williams                              path{"/var/lib/phosphor-virtual-sensor"},
76132dff21bSPatrick Williams                              path{"/usr/share/phosphor-virtual-sensor"}})
76232dff21bSPatrick Williams         {
76332dff21bSPatrick Williams             auto file = pathSeg / name;
76432dff21bSPatrick Williams             if (std::filesystem::exists(file))
76532dff21bSPatrick Williams             {
76632dff21bSPatrick Williams                 return file;
76732dff21bSPatrick Williams             }
76832dff21bSPatrick Williams         }
76932dff21bSPatrick Williams         return name;
77032dff21bSPatrick Williams     }();
77132dff21bSPatrick Williams 
772abcc94faSVijay Khemka     std::ifstream jsonFile(configFile);
773abcc94faSVijay Khemka     if (!jsonFile.is_open())
774abcc94faSVijay Khemka     {
77582b39c66SPatrick Williams         error("config JSON file {FILENAME} not found", "FILENAME", configFile);
776e7efe135SRashmica Gupta         return {};
777abcc94faSVijay Khemka     }
778abcc94faSVijay Khemka 
779abcc94faSVijay Khemka     auto data = Json::parse(jsonFile, nullptr, false);
780abcc94faSVijay Khemka     if (data.is_discarded())
781abcc94faSVijay Khemka     {
78282b39c66SPatrick Williams         error("config readings JSON parser failure with {FILENAME}", "FILENAME",
78382b39c66SPatrick Williams               configFile);
784abcc94faSVijay Khemka         throw std::exception{};
785abcc94faSVijay Khemka     }
786abcc94faSVijay Khemka 
787abcc94faSVijay Khemka     return data;
788abcc94faSVijay Khemka }
789abcc94faSVijay Khemka 
790e0d371e4SVijay Khemka std::map<std::string, ValueIface::Unit> unitMap = {
791e0d371e4SVijay Khemka     {"temperature", ValueIface::Unit::DegreesC},
792e0d371e4SVijay Khemka     {"fan_tach", ValueIface::Unit::RPMS},
793*9358f6bdSKonstantin Aladyshev     {"fan_pwm", ValueIface::Unit::Percent},
794e0d371e4SVijay Khemka     {"voltage", ValueIface::Unit::Volts},
795e0d371e4SVijay Khemka     {"altitude", ValueIface::Unit::Meters},
796e0d371e4SVijay Khemka     {"current", ValueIface::Unit::Amperes},
797e0d371e4SVijay Khemka     {"power", ValueIface::Unit::Watts},
798e0d371e4SVijay Khemka     {"energy", ValueIface::Unit::Joules},
7992b56ddb3SKumar Thangavel     {"utilization", ValueIface::Unit::Percent},
8004ac7a7f2SRashmica Gupta     {"airflow", ValueIface::Unit::CFM},
8014ac7a7f2SRashmica Gupta     {"pressure", ValueIface::Unit::Pascals}};
802e0d371e4SVijay Khemka 
803e7efe135SRashmica Gupta const std::string getSensorTypeFromUnit(const std::string& unit)
804e7efe135SRashmica Gupta {
805e7efe135SRashmica Gupta     std::string unitPrefix = "xyz.openbmc_project.Sensor.Value.Unit.";
806e7efe135SRashmica Gupta     for (auto [type, unitObj] : unitMap)
807e7efe135SRashmica Gupta     {
808e7efe135SRashmica Gupta         auto unitPath = ValueIface::convertUnitToString(unitObj);
809e7efe135SRashmica Gupta         if (unitPath == (unitPrefix + unit))
810e7efe135SRashmica Gupta         {
811e7efe135SRashmica Gupta             return type;
812e7efe135SRashmica Gupta         }
813e7efe135SRashmica Gupta     }
814e7efe135SRashmica Gupta     return "";
815e7efe135SRashmica Gupta }
816e7efe135SRashmica Gupta 
817e7efe135SRashmica Gupta void VirtualSensors::setupMatches()
818e7efe135SRashmica Gupta {
819e7efe135SRashmica Gupta     /* Already setup */
820e7efe135SRashmica Gupta     if (!this->matches.empty())
821e7efe135SRashmica Gupta     {
822e7efe135SRashmica Gupta         return;
823e7efe135SRashmica Gupta     }
824e7efe135SRashmica Gupta 
825e7efe135SRashmica Gupta     /* Setup matches */
8268e11cccbSPatrick Williams     auto eventHandler = [this](sdbusplus::message_t& message) {
827e7efe135SRashmica Gupta         if (message.is_method_error())
828e7efe135SRashmica Gupta         {
82982b39c66SPatrick Williams             error("Callback method error");
830e7efe135SRashmica Gupta             return;
831e7efe135SRashmica Gupta         }
832e7efe135SRashmica Gupta         this->propertiesChanged(message);
833e7efe135SRashmica Gupta     };
834e7efe135SRashmica Gupta 
835e7efe135SRashmica Gupta     for (const char* iface : calculationIfaces)
836e7efe135SRashmica Gupta     {
8378e11cccbSPatrick Williams         auto match = std::make_unique<sdbusplus::bus::match_t>(
838e7efe135SRashmica Gupta             bus,
839e7efe135SRashmica Gupta             sdbusplus::bus::match::rules::propertiesChangedNamespace(
840e7efe135SRashmica Gupta                 "/xyz/openbmc_project/inventory", iface),
841e7efe135SRashmica Gupta             eventHandler);
842e7efe135SRashmica Gupta         this->matches.emplace_back(std::move(match));
843e7efe135SRashmica Gupta     }
844e7efe135SRashmica Gupta }
845e7efe135SRashmica Gupta 
846e7efe135SRashmica Gupta void VirtualSensors::createVirtualSensorsFromDBus(
847e7efe135SRashmica Gupta     const std::string& calculationIface)
848e7efe135SRashmica Gupta {
849e7efe135SRashmica Gupta     if (calculationIface.empty())
850e7efe135SRashmica Gupta     {
85182b39c66SPatrick Williams         error("No calculation type supplied");
852e7efe135SRashmica Gupta         return;
853e7efe135SRashmica Gupta     }
854e7efe135SRashmica Gupta     auto objects = getObjectsFromDBus();
855e7efe135SRashmica Gupta 
856e7efe135SRashmica Gupta     /* Get virtual sensors config data */
857e7efe135SRashmica Gupta     for (const auto& [path, interfaceMap] : objects)
858e7efe135SRashmica Gupta     {
859e7efe135SRashmica Gupta         /* Find Virtual Sensor interfaces */
8602db8d41fSGeorge Liu         auto intfIter = interfaceMap.find(calculationIface);
8612db8d41fSGeorge Liu         if (intfIter == interfaceMap.end())
862e7efe135SRashmica Gupta         {
863e7efe135SRashmica Gupta             continue;
864e7efe135SRashmica Gupta         }
8652db8d41fSGeorge Liu 
8662db8d41fSGeorge Liu         std::string name = path.filename();
867e7efe135SRashmica Gupta         if (name.empty())
868e7efe135SRashmica Gupta         {
86982b39c66SPatrick Williams             error("Virtual Sensor name not found in entity manager config");
870e7efe135SRashmica Gupta             continue;
871e7efe135SRashmica Gupta         }
872e7efe135SRashmica Gupta         if (virtualSensorsMap.contains(name))
873e7efe135SRashmica Gupta         {
87482b39c66SPatrick Williams             error("A virtual sensor named {NAME} already exists", "NAME", name);
875e7efe135SRashmica Gupta             continue;
876e7efe135SRashmica Gupta         }
877e7efe135SRashmica Gupta 
878e7efe135SRashmica Gupta         /* Extract the virtual sensor type as we need this to initialize the
879e7efe135SRashmica Gupta          * sensor */
8802db8d41fSGeorge Liu         std::string sensorType, sensorUnit;
8812db8d41fSGeorge Liu         auto propertyMap = intfIter->second;
8822db8d41fSGeorge Liu         auto proIter = propertyMap.find("Units");
8832db8d41fSGeorge Liu         if (proIter != propertyMap.end())
884e7efe135SRashmica Gupta         {
8852db8d41fSGeorge Liu             sensorUnit = std::get<std::string>(proIter->second);
886e7efe135SRashmica Gupta         }
887e7efe135SRashmica Gupta         sensorType = getSensorTypeFromUnit(sensorUnit);
888e7efe135SRashmica Gupta         if (sensorType.empty())
889e7efe135SRashmica Gupta         {
89082b39c66SPatrick Williams             error("Sensor unit type {TYPE} is not supported", "TYPE",
89182b39c66SPatrick Williams                   sensorUnit);
892e7efe135SRashmica Gupta             continue;
893e7efe135SRashmica Gupta         }
894e7efe135SRashmica Gupta 
895e7efe135SRashmica Gupta         try
896e7efe135SRashmica Gupta         {
8972db8d41fSGeorge Liu             auto objpath = static_cast<std::string>(path);
898e7efe135SRashmica Gupta             auto virtObjPath = sensorDbusPath + sensorType + "/" + name;
899e7efe135SRashmica Gupta 
900e7efe135SRashmica Gupta             auto virtualSensorPtr = std::make_unique<VirtualSensor>(
901e7efe135SRashmica Gupta                 bus, virtObjPath.c_str(), interfaceMap, name, sensorType,
902dc777015STao Lin                 calculationIface, objpath);
90382b39c66SPatrick Williams             info("Added a new virtual sensor: {NAME} {TYPE}", "NAME", name,
90482b39c66SPatrick Williams                  "TYPE", sensorType);
905e7efe135SRashmica Gupta             virtualSensorPtr->updateVirtualSensor();
906e7efe135SRashmica Gupta 
907e7efe135SRashmica Gupta             /* Initialize unit value for virtual sensor */
908e7efe135SRashmica Gupta             virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
909e7efe135SRashmica Gupta             virtualSensorPtr->emit_object_added();
910e7efe135SRashmica Gupta 
911e7efe135SRashmica Gupta             virtualSensorsMap.emplace(name, std::move(virtualSensorPtr));
912e7efe135SRashmica Gupta 
913e7efe135SRashmica Gupta             /* Setup match for interfaces removed */
914ae10c529SPatrick Williams             auto intfRemoved = [this, objpath,
915ae10c529SPatrick Williams                                 name](sdbusplus::message_t& message) {
916e7efe135SRashmica Gupta                 if (!virtualSensorsMap.contains(name))
917e7efe135SRashmica Gupta                 {
918e7efe135SRashmica Gupta                     return;
919e7efe135SRashmica Gupta                 }
920e7efe135SRashmica Gupta                 sdbusplus::message::object_path path;
921e7efe135SRashmica Gupta                 message.read(path);
922e7efe135SRashmica Gupta                 if (static_cast<const std::string&>(path) == objpath)
923e7efe135SRashmica Gupta                 {
92482b39c66SPatrick Williams                     info("Removed a virtual sensor: {NAME}", "NAME", name);
925e7efe135SRashmica Gupta                     virtualSensorsMap.erase(name);
926e7efe135SRashmica Gupta                 }
927e7efe135SRashmica Gupta             };
9288e11cccbSPatrick Williams             auto matchOnRemove = std::make_unique<sdbusplus::bus::match_t>(
929e7efe135SRashmica Gupta                 bus,
930e7efe135SRashmica Gupta                 sdbusplus::bus::match::rules::interfacesRemoved() +
931e7efe135SRashmica Gupta                     sdbusplus::bus::match::rules::argNpath(0, objpath),
932e7efe135SRashmica Gupta                 intfRemoved);
933e7efe135SRashmica Gupta             /* TODO: slight race condition here. Check that the config still
934e7efe135SRashmica Gupta              * exists */
935e7efe135SRashmica Gupta             this->matches.emplace_back(std::move(matchOnRemove));
936e7efe135SRashmica Gupta         }
937dac2663cSPatrick Williams         catch (const std::invalid_argument& ia)
938e7efe135SRashmica Gupta         {
93982b39c66SPatrick Williams             error("Failed to set up virtual sensor: {ERROR}", "ERROR", ia);
940e7efe135SRashmica Gupta         }
941e7efe135SRashmica Gupta     }
942e7efe135SRashmica Gupta }
943e7efe135SRashmica Gupta 
944abcc94faSVijay Khemka void VirtualSensors::createVirtualSensors()
945abcc94faSVijay Khemka {
946abcc94faSVijay Khemka     static const Json empty{};
947abcc94faSVijay Khemka 
94832dff21bSPatrick Williams     auto data = parseConfigFile();
949e7efe135SRashmica Gupta 
950abcc94faSVijay Khemka     // print values
951abcc94faSVijay Khemka     if (DEBUG)
952e7efe135SRashmica Gupta     {
953fbd7145eSPatrick Williams         debug("JSON: {JSON}", "JSON", data.dump());
954e7efe135SRashmica Gupta     }
955abcc94faSVijay Khemka 
956abcc94faSVijay Khemka     /* Get virtual sensors  config data */
957abcc94faSVijay Khemka     for (const auto& j : data)
958abcc94faSVijay Khemka     {
959abcc94faSVijay Khemka         auto desc = j.value("Desc", empty);
960abcc94faSVijay Khemka         if (!desc.empty())
961abcc94faSVijay Khemka         {
962e7efe135SRashmica Gupta             if (desc.value("Config", "") == "D-Bus")
963e7efe135SRashmica Gupta             {
964e7efe135SRashmica Gupta                 /* Look on D-Bus for a virtual sensor config. Set up matches
965e7efe135SRashmica Gupta                  * first because the configs may not be on D-Bus yet and we
966e7efe135SRashmica Gupta                  * don't want to miss them */
967e7efe135SRashmica Gupta                 setupMatches();
968e7efe135SRashmica Gupta 
969e7efe135SRashmica Gupta                 if (desc.contains("Type"))
970e7efe135SRashmica Gupta                 {
97182b39c66SPatrick Williams                     auto type = desc.value("Type", "");
97282b39c66SPatrick Williams                     auto path = "xyz.openbmc_project.Configuration." + type;
97382b39c66SPatrick Williams 
974e7efe135SRashmica Gupta                     if (!isCalculationType(path))
975e7efe135SRashmica Gupta                     {
97682b39c66SPatrick Williams                         error("Invalid calculation type {TYPE} supplied.",
97782b39c66SPatrick Williams                               "TYPE", type);
978e7efe135SRashmica Gupta                         continue;
979e7efe135SRashmica Gupta                     }
980e7efe135SRashmica Gupta                     createVirtualSensorsFromDBus(path);
981e7efe135SRashmica Gupta                 }
982e7efe135SRashmica Gupta                 continue;
983e7efe135SRashmica Gupta             }
984e7efe135SRashmica Gupta 
985abcc94faSVijay Khemka             std::string sensorType = desc.value("SensorType", "");
986abcc94faSVijay Khemka             std::string name = desc.value("Name", "");
987665a0a29SRashmica Gupta             std::replace(name.begin(), name.end(), ' ', '_');
988abcc94faSVijay Khemka 
989abcc94faSVijay Khemka             if (!name.empty() && !sensorType.empty())
990abcc94faSVijay Khemka             {
991e0d371e4SVijay Khemka                 if (unitMap.find(sensorType) == unitMap.end())
992e0d371e4SVijay Khemka                 {
99382b39c66SPatrick Williams                     error("Sensor type {TYPE} is not supported", "TYPE",
99482b39c66SPatrick Williams                           sensorType);
995e0d371e4SVijay Khemka                 }
996e0d371e4SVijay Khemka                 else
997e0d371e4SVijay Khemka                 {
99867d8b9d2SRashmica Gupta                     if (virtualSensorsMap.find(name) != virtualSensorsMap.end())
99967d8b9d2SRashmica Gupta                     {
100082b39c66SPatrick Williams                         error("A virtual sensor named {NAME} already exists",
100182b39c66SPatrick Williams                               "NAME", name);
100267d8b9d2SRashmica Gupta                         continue;
100367d8b9d2SRashmica Gupta                     }
1004862c3d1eSRashmica Gupta                     auto objPath = sensorDbusPath + sensorType + "/" + name;
1005abcc94faSVijay Khemka 
100632a7156bSVijay Khemka                     auto virtualSensorPtr = std::make_unique<VirtualSensor>(
100732a7156bSVijay Khemka                         bus, objPath.c_str(), j, name);
1008abcc94faSVijay Khemka 
100982b39c66SPatrick Williams                     info("Added a new virtual sensor: {NAME}", "NAME", name);
10103ed9a516SVijay Khemka                     virtualSensorPtr->updateVirtualSensor();
1011e0d371e4SVijay Khemka 
1012e0d371e4SVijay Khemka                     /* Initialize unit value for virtual sensor */
1013e0d371e4SVijay Khemka                     virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
1014a2fa63a6SRashmica Gupta                     virtualSensorPtr->emit_object_added();
1015e0d371e4SVijay Khemka 
10163ed9a516SVijay Khemka                     virtualSensorsMap.emplace(std::move(name),
10173ed9a516SVijay Khemka                                               std::move(virtualSensorPtr));
1018abcc94faSVijay Khemka                 }
1019e0d371e4SVijay Khemka             }
1020abcc94faSVijay Khemka             else
1021abcc94faSVijay Khemka             {
102282b39c66SPatrick Williams                 error(
102382b39c66SPatrick Williams                     "Sensor type ({TYPE}) or name ({NAME}) not found in config file",
102482b39c66SPatrick Williams                     "NAME", name, "TYPE", sensorType);
1025abcc94faSVijay Khemka             }
1026abcc94faSVijay Khemka         }
1027abcc94faSVijay Khemka         else
1028abcc94faSVijay Khemka         {
102982b39c66SPatrick Williams             error("Descriptor for new virtual sensor not found in config file");
1030abcc94faSVijay Khemka         }
1031abcc94faSVijay Khemka     }
1032abcc94faSVijay Khemka }
1033abcc94faSVijay Khemka 
1034f2e94221STao Lin } // namespace phosphor::virtual_sensor
1035abcc94faSVijay Khemka 
1036abcc94faSVijay Khemka /**
1037abcc94faSVijay Khemka  * @brief Main
1038abcc94faSVijay Khemka  */
1039abcc94faSVijay Khemka int main()
1040abcc94faSVijay Khemka {
1041abcc94faSVijay Khemka     // Get a handle to system dbus
1042abcc94faSVijay Khemka     auto bus = sdbusplus::bus::new_default();
1043abcc94faSVijay Khemka 
10446c19e7d2SMatt Spinler     // Add the ObjectManager interface
1045f7ec40aaSEd Tanous     sdbusplus::server::manager_t objManager(bus,
1046f7ec40aaSEd Tanous                                             "/xyz/openbmc_project/sensors");
10476c19e7d2SMatt Spinler 
1048abcc94faSVijay Khemka     // Create an virtual sensors object
1049f2e94221STao Lin     phosphor::virtual_sensor::VirtualSensors virtualSensors(bus);
1050abcc94faSVijay Khemka 
1051abcc94faSVijay Khemka     // Request service bus name
105294921490SGeorge Liu     bus.request_name("xyz.openbmc_project.VirtualSensor");
1053abcc94faSVijay Khemka 
1054e667239dSPatrick Williams     // Run the dbus loop.
1055e667239dSPatrick Williams     bus.process_loop();
1056abcc94faSVijay Khemka 
1057abcc94faSVijay Khemka     return 0;
1058abcc94faSVijay Khemka }
1059