1abcc94faSVijay Khemka #include "virtualSensor.hpp"
2abcc94faSVijay Khemka 
3abcc94faSVijay Khemka #include "config.hpp"
4abcc94faSVijay Khemka 
582b39c66SPatrick Williams #include <phosphor-logging/lg2.hpp>
6abcc94faSVijay Khemka #include <sdeventplus/event.hpp>
7abcc94faSVijay Khemka 
8abcc94faSVijay Khemka #include <fstream>
9abcc94faSVijay Khemka 
10abcc94faSVijay Khemka static constexpr bool DEBUG = false;
11abcc94faSVijay Khemka static constexpr auto busName = "xyz.openbmc_project.VirtualSensor";
12abcc94faSVijay Khemka static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/";
13e7efe135SRashmica Gupta static constexpr auto entityManagerBusName =
14e7efe135SRashmica Gupta     "xyz.openbmc_project.EntityManager";
15e7efe135SRashmica Gupta static constexpr auto vsThresholdsIfaceSuffix = ".Thresholds";
16304fd0e4SRashmica Gupta static constexpr std::array<const char*, 1> calculationIfaces = {
17304fd0e4SRashmica Gupta     "xyz.openbmc_project.Configuration.ModifiedMedian"};
181dff7dceSRashmica Gupta static constexpr auto defaultHysteresis = 0;
19abcc94faSVijay Khemka 
2082b39c66SPatrick Williams PHOSPHOR_LOG2_USING_WITH_FLAGS;
21abcc94faSVijay Khemka 
2251f898e2SVijay Khemka int handleDbusSignal(sd_bus_message* msg, void* usrData, sd_bus_error*)
2351f898e2SVijay Khemka {
2451f898e2SVijay Khemka     if (usrData == nullptr)
2551f898e2SVijay Khemka     {
2651f898e2SVijay Khemka         throw std::runtime_error("Invalid match");
2751f898e2SVijay Khemka     }
2851f898e2SVijay Khemka 
2951f898e2SVijay Khemka     auto sdbpMsg = sdbusplus::message::message(msg);
3051f898e2SVijay Khemka     std::string msgIfce;
3151f898e2SVijay Khemka     std::map<std::string, std::variant<int64_t, double, bool>> msgData;
3251f898e2SVijay Khemka 
3351f898e2SVijay Khemka     sdbpMsg.read(msgIfce, msgData);
3451f898e2SVijay Khemka 
3551f898e2SVijay Khemka     if (msgData.find("Value") != msgData.end())
3651f898e2SVijay Khemka     {
3751f898e2SVijay Khemka         using namespace phosphor::virtualSensor;
3851f898e2SVijay Khemka         VirtualSensor* obj = static_cast<VirtualSensor*>(usrData);
3951f898e2SVijay Khemka         // TODO(openbmc/phosphor-virtual-sensor#1): updateVirtualSensor should
4051f898e2SVijay Khemka         // be changed to take the information we got from the signal, to avoid
4151f898e2SVijay Khemka         // having to do numerous dbus queries.
4251f898e2SVijay Khemka         obj->updateVirtualSensor();
4351f898e2SVijay Khemka     }
4451f898e2SVijay Khemka     return 0;
4551f898e2SVijay Khemka }
4651f898e2SVijay Khemka 
47abcc94faSVijay Khemka namespace phosphor
48abcc94faSVijay Khemka {
49abcc94faSVijay Khemka namespace virtualSensor
50abcc94faSVijay Khemka {
51abcc94faSVijay Khemka 
52abcc94faSVijay Khemka void printParams(const VirtualSensor::ParamMap& paramMap)
53abcc94faSVijay Khemka {
54abcc94faSVijay Khemka     for (const auto& p : paramMap)
55abcc94faSVijay Khemka     {
56abcc94faSVijay Khemka         const auto& p1 = p.first;
57abcc94faSVijay Khemka         const auto& p2 = p.second;
58abcc94faSVijay Khemka         auto val = p2->getParamValue();
59fbd7145eSPatrick Williams         debug("Parameter: {PARAM} = {VALUE}", "PARAM", p1, "VALUE", val);
60abcc94faSVijay Khemka     }
61abcc94faSVijay Khemka }
62abcc94faSVijay Khemka 
63abcc94faSVijay Khemka double SensorParam::getParamValue()
64abcc94faSVijay Khemka {
65abcc94faSVijay Khemka     switch (paramType)
66abcc94faSVijay Khemka     {
67abcc94faSVijay Khemka         case constParam:
68abcc94faSVijay Khemka             return value;
69abcc94faSVijay Khemka             break;
707452a867SVijay Khemka         case dbusParam:
717452a867SVijay Khemka             return dbusSensor->getSensorValue();
727452a867SVijay Khemka             break;
73abcc94faSVijay Khemka         default:
74abcc94faSVijay Khemka             throw std::invalid_argument("param type not supported");
75abcc94faSVijay Khemka     }
76abcc94faSVijay Khemka }
77abcc94faSVijay Khemka 
780fcf0e1cSLei YU using AssociationList =
790fcf0e1cSLei YU     std::vector<std::tuple<std::string, std::string, std::string>>;
800fcf0e1cSLei YU 
810fcf0e1cSLei YU AssociationList getAssociationsFromJson(const Json& j)
820fcf0e1cSLei YU {
830fcf0e1cSLei YU     AssociationList assocs{};
840fcf0e1cSLei YU     try
850fcf0e1cSLei YU     {
860fcf0e1cSLei YU         j.get_to(assocs);
870fcf0e1cSLei YU     }
880fcf0e1cSLei YU     catch (const std::exception& ex)
890fcf0e1cSLei YU     {
9082b39c66SPatrick Williams         error("Failed to parse association: {ERROR}", "ERROR", ex);
910fcf0e1cSLei YU     }
920fcf0e1cSLei YU     return assocs;
930fcf0e1cSLei YU }
940fcf0e1cSLei YU 
95e7efe135SRashmica Gupta template <typename U>
96e7efe135SRashmica Gupta struct VariantToNumber
97e7efe135SRashmica Gupta {
98e7efe135SRashmica Gupta     template <typename T>
99e7efe135SRashmica Gupta     U operator()(const T& t) const
100e7efe135SRashmica Gupta     {
101e7efe135SRashmica Gupta         if constexpr (std::is_convertible<T, U>::value)
102e7efe135SRashmica Gupta         {
103e7efe135SRashmica Gupta             return static_cast<U>(t);
104e7efe135SRashmica Gupta         }
105e7efe135SRashmica Gupta         throw std::invalid_argument("Invalid number type in config\n");
106e7efe135SRashmica Gupta     }
107e7efe135SRashmica Gupta };
108e7efe135SRashmica Gupta 
109e7efe135SRashmica Gupta template <typename U>
110e7efe135SRashmica Gupta U getNumberFromConfig(const PropertyMap& map, const std::string& name,
111*190f6d06SJiaqing Zhao                       bool required,
112*190f6d06SJiaqing Zhao                       U defaultValue = std::numeric_limits<U>::quiet_NaN())
113e7efe135SRashmica Gupta {
114e7efe135SRashmica Gupta     if (auto itr = map.find(name); itr != map.end())
115e7efe135SRashmica Gupta     {
116e7efe135SRashmica Gupta         return std::visit(VariantToNumber<U>(), itr->second);
117e7efe135SRashmica Gupta     }
118e7efe135SRashmica Gupta     else if (required)
119e7efe135SRashmica Gupta     {
12082b39c66SPatrick Williams         error("Required field {NAME} missing in config", "NAME", name);
121e7efe135SRashmica Gupta         throw std::invalid_argument("Required field missing in config");
122e7efe135SRashmica Gupta     }
123*190f6d06SJiaqing Zhao     return defaultValue;
124e7efe135SRashmica Gupta }
125e7efe135SRashmica Gupta 
126e7efe135SRashmica Gupta bool isCalculationType(const std::string& interface)
127e7efe135SRashmica Gupta {
128e7efe135SRashmica Gupta     auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
129e7efe135SRashmica Gupta                          interface);
130e7efe135SRashmica Gupta     if (itr != calculationIfaces.end())
131e7efe135SRashmica Gupta     {
132e7efe135SRashmica Gupta         return true;
133e7efe135SRashmica Gupta     }
134e7efe135SRashmica Gupta     return false;
135e7efe135SRashmica Gupta }
136e7efe135SRashmica Gupta 
137e7efe135SRashmica Gupta const std::string getThresholdType(const std::string& direction,
13805b1d417SRashmica Gupta                                    const std::string& severity)
139e7efe135SRashmica Gupta {
140e7efe135SRashmica Gupta     std::string suffix;
141e7efe135SRashmica Gupta 
142e7efe135SRashmica Gupta     if (direction == "less than")
143e7efe135SRashmica Gupta     {
144e7efe135SRashmica Gupta         suffix = "Low";
145e7efe135SRashmica Gupta     }
146e7efe135SRashmica Gupta     else if (direction == "greater than")
147e7efe135SRashmica Gupta     {
148e7efe135SRashmica Gupta         suffix = "High";
149e7efe135SRashmica Gupta     }
150e7efe135SRashmica Gupta     else
151e7efe135SRashmica Gupta     {
152e7efe135SRashmica Gupta         throw std::invalid_argument(
153e7efe135SRashmica Gupta             "Invalid threshold direction specified in entity manager");
154e7efe135SRashmica Gupta     }
15505b1d417SRashmica Gupta     return severity + suffix;
15605b1d417SRashmica Gupta }
15705b1d417SRashmica Gupta 
15805b1d417SRashmica Gupta std::string getSeverityField(const PropertyMap& propertyMap)
15905b1d417SRashmica Gupta {
16005b1d417SRashmica Gupta     static const std::array thresholdTypes{"Warning", "Critical",
16105b1d417SRashmica Gupta                                            "PerformanceLoss", "SoftShutdown",
16205b1d417SRashmica Gupta                                            "HardShutdown"};
16305b1d417SRashmica Gupta 
16405b1d417SRashmica Gupta     std::string severity;
16505b1d417SRashmica Gupta     if (auto itr = propertyMap.find("Severity"); itr != propertyMap.end())
16605b1d417SRashmica Gupta     {
16705b1d417SRashmica Gupta         /* Severity should be a string, but can be an unsigned int */
16805b1d417SRashmica Gupta         if (std::holds_alternative<std::string>(itr->second))
16905b1d417SRashmica Gupta         {
17005b1d417SRashmica Gupta             severity = std::get<std::string>(itr->second);
17105b1d417SRashmica Gupta             if (0 == std::ranges::count(thresholdTypes, severity))
17205b1d417SRashmica Gupta             {
17305b1d417SRashmica Gupta                 throw std::invalid_argument(
17405b1d417SRashmica Gupta                     "Invalid threshold severity specified in entity manager");
17505b1d417SRashmica Gupta             }
17605b1d417SRashmica Gupta         }
17705b1d417SRashmica Gupta         else
17805b1d417SRashmica Gupta         {
17905b1d417SRashmica Gupta             auto sev =
18005b1d417SRashmica Gupta                 getNumberFromConfig<uint64_t>(propertyMap, "Severity", true);
18105b1d417SRashmica Gupta             /* Checking bounds ourselves so we throw invalid argument on
18205b1d417SRashmica Gupta              * invalid user input */
18305b1d417SRashmica Gupta             if (sev >= thresholdTypes.size())
18405b1d417SRashmica Gupta             {
18505b1d417SRashmica Gupta                 throw std::invalid_argument(
18605b1d417SRashmica Gupta                     "Invalid threshold severity specified in entity manager");
18705b1d417SRashmica Gupta             }
18805b1d417SRashmica Gupta             severity = thresholdTypes.at(sev);
18905b1d417SRashmica Gupta         }
19005b1d417SRashmica Gupta     }
19105b1d417SRashmica Gupta     return severity;
192e7efe135SRashmica Gupta }
193e7efe135SRashmica Gupta 
194e7efe135SRashmica Gupta void parseThresholds(Json& thresholds, const PropertyMap& propertyMap)
195e7efe135SRashmica Gupta {
196e7efe135SRashmica Gupta     std::string direction;
197e7efe135SRashmica Gupta 
198e7efe135SRashmica Gupta     auto value = getNumberFromConfig<double>(propertyMap, "Value", true);
199e7efe135SRashmica Gupta 
20005b1d417SRashmica Gupta     auto severity = getSeverityField(propertyMap);
20105b1d417SRashmica Gupta 
20205b1d417SRashmica Gupta     if (auto itr = propertyMap.find("Direction"); itr != propertyMap.end())
203e7efe135SRashmica Gupta     {
204e7efe135SRashmica Gupta         direction = std::get<std::string>(itr->second);
205e7efe135SRashmica Gupta     }
206e7efe135SRashmica Gupta 
207e7efe135SRashmica Gupta     auto threshold = getThresholdType(direction, severity);
208e7efe135SRashmica Gupta     thresholds[threshold] = value;
2091dff7dceSRashmica Gupta 
2101dff7dceSRashmica Gupta     auto hysteresis =
2111dff7dceSRashmica Gupta         getNumberFromConfig<double>(propertyMap, "Hysteresis", false);
2121dff7dceSRashmica Gupta     if (hysteresis != std::numeric_limits<double>::quiet_NaN())
2131dff7dceSRashmica Gupta     {
2141dff7dceSRashmica Gupta         thresholds[threshold + "Hysteresis"] = hysteresis;
2151dff7dceSRashmica Gupta     }
216e7efe135SRashmica Gupta }
217e7efe135SRashmica Gupta 
218e7efe135SRashmica Gupta void VirtualSensor::parseConfigInterface(const PropertyMap& propertyMap,
219e7efe135SRashmica Gupta                                          const std::string& sensorType,
220e7efe135SRashmica Gupta                                          const std::string& interface)
221e7efe135SRashmica Gupta {
222e7efe135SRashmica Gupta     /* Parse sensors / DBus params */
223e7efe135SRashmica Gupta     if (auto itr = propertyMap.find("Sensors"); itr != propertyMap.end())
224e7efe135SRashmica Gupta     {
225e7efe135SRashmica Gupta         auto sensors = std::get<std::vector<std::string>>(itr->second);
226e7efe135SRashmica Gupta         for (auto sensor : sensors)
227e7efe135SRashmica Gupta         {
228e7efe135SRashmica Gupta             std::replace(sensor.begin(), sensor.end(), ' ', '_');
229e7efe135SRashmica Gupta             auto sensorObjPath = sensorDbusPath + sensorType + "/" + sensor;
230e7efe135SRashmica Gupta 
231e7efe135SRashmica Gupta             auto paramPtr =
232e7efe135SRashmica Gupta                 std::make_unique<SensorParam>(bus, sensorObjPath, this);
233e7efe135SRashmica Gupta             symbols.create_variable(sensor);
234e7efe135SRashmica Gupta             paramMap.emplace(std::move(sensor), std::move(paramPtr));
235e7efe135SRashmica Gupta         }
236e7efe135SRashmica Gupta     }
237e7efe135SRashmica Gupta     /* Get expression string */
238e7efe135SRashmica Gupta     if (!isCalculationType(interface))
239e7efe135SRashmica Gupta     {
240e7efe135SRashmica Gupta         throw std::invalid_argument("Invalid expression in interface");
241e7efe135SRashmica Gupta     }
242e7efe135SRashmica Gupta     exprStr = interface;
243e7efe135SRashmica Gupta 
244e7efe135SRashmica Gupta     /* Get optional min and max input and output values */
245e7efe135SRashmica Gupta     ValueIface::maxValue(
246e7efe135SRashmica Gupta         getNumberFromConfig<double>(propertyMap, "MaxValue", false));
247e7efe135SRashmica Gupta     ValueIface::minValue(
248e7efe135SRashmica Gupta         getNumberFromConfig<double>(propertyMap, "MinValue", false));
249e7efe135SRashmica Gupta     maxValidInput =
250*190f6d06SJiaqing Zhao         getNumberFromConfig<double>(propertyMap, "MaxValidInput", false,
251*190f6d06SJiaqing Zhao                                     std::numeric_limits<double>::infinity());
252e7efe135SRashmica Gupta     minValidInput =
253*190f6d06SJiaqing Zhao         getNumberFromConfig<double>(propertyMap, "MinValidInput", false,
254*190f6d06SJiaqing Zhao                                     -std::numeric_limits<double>::infinity());
255e7efe135SRashmica Gupta }
256e7efe135SRashmica Gupta 
257ce675228SMatt Spinler void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
258ce675228SMatt Spinler                                       const std::string& objPath)
259abcc94faSVijay Khemka {
260abcc94faSVijay Khemka 
261abcc94faSVijay Khemka     static const Json empty{};
262abcc94faSVijay Khemka 
263abcc94faSVijay Khemka     /* Get threshold values if defined in config */
264abcc94faSVijay Khemka     auto threshold = sensorConfig.value("Threshold", empty);
265f15189e3SMatt Spinler 
2663e99919bSRashmica Gupta     createThresholds(threshold, objPath);
267abcc94faSVijay Khemka 
268f6443742SHarvey Wu     /* Get MaxValue, MinValue setting if defined in config */
269f6443742SHarvey Wu     auto confDesc = sensorConfig.value("Desc", empty);
270f6443742SHarvey Wu     if (auto maxConf = confDesc.find("MaxValue");
271f6443742SHarvey Wu         maxConf != confDesc.end() && maxConf->is_number())
272f6443742SHarvey Wu     {
273f6443742SHarvey Wu         ValueIface::maxValue(maxConf->get<double>());
274f6443742SHarvey Wu     }
275f6443742SHarvey Wu     if (auto minConf = confDesc.find("MinValue");
276f6443742SHarvey Wu         minConf != confDesc.end() && minConf->is_number())
277f6443742SHarvey Wu     {
278f6443742SHarvey Wu         ValueIface::minValue(minConf->get<double>());
279f6443742SHarvey Wu     }
280f6443742SHarvey Wu 
2810fcf0e1cSLei YU     /* Get optional association */
2820fcf0e1cSLei YU     auto assocJson = sensorConfig.value("Associations", empty);
2830fcf0e1cSLei YU     if (!assocJson.empty())
2840fcf0e1cSLei YU     {
2850fcf0e1cSLei YU         auto assocs = getAssociationsFromJson(assocJson);
2860fcf0e1cSLei YU         if (!assocs.empty())
2870fcf0e1cSLei YU         {
2880fcf0e1cSLei YU             associationIface =
2890fcf0e1cSLei YU                 std::make_unique<AssociationObject>(bus, objPath.c_str());
2900fcf0e1cSLei YU             associationIface->associations(assocs);
2910fcf0e1cSLei YU         }
2920fcf0e1cSLei YU     }
2930fcf0e1cSLei YU 
294abcc94faSVijay Khemka     /* Get expression string */
29503c4c8e2SPatrick Williams     static constexpr auto exprKey = "Expression";
29603c4c8e2SPatrick Williams     if (sensorConfig.contains(exprKey))
29703c4c8e2SPatrick Williams     {
298a959678cSPatrick Williams         auto& ref = sensorConfig.at(exprKey);
29903c4c8e2SPatrick Williams         if (ref.is_array())
30003c4c8e2SPatrick Williams         {
30103c4c8e2SPatrick Williams             exprStr = std::string{};
30203c4c8e2SPatrick Williams             for (auto& s : ref)
30303c4c8e2SPatrick Williams             {
30403c4c8e2SPatrick Williams                 exprStr += s;
30503c4c8e2SPatrick Williams             }
30603c4c8e2SPatrick Williams         }
30703c4c8e2SPatrick Williams         else if (ref.is_string())
30803c4c8e2SPatrick Williams         {
30903c4c8e2SPatrick Williams             exprStr = std::string{ref};
31003c4c8e2SPatrick Williams         }
31103c4c8e2SPatrick Williams     }
312abcc94faSVijay Khemka 
313abcc94faSVijay Khemka     /* Get all the parameter listed in configuration */
314abcc94faSVijay Khemka     auto params = sensorConfig.value("Params", empty);
315abcc94faSVijay Khemka 
316abcc94faSVijay Khemka     /* Check for constant parameter */
317abcc94faSVijay Khemka     const auto& consParams = params.value("ConstParam", empty);
318abcc94faSVijay Khemka     if (!consParams.empty())
319abcc94faSVijay Khemka     {
320abcc94faSVijay Khemka         for (auto& j : consParams)
321abcc94faSVijay Khemka         {
322abcc94faSVijay Khemka             if (j.find("ParamName") != j.end())
323abcc94faSVijay Khemka             {
324abcc94faSVijay Khemka                 auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
3253ed9a516SVijay Khemka                 std::string name = j["ParamName"];
3263ed9a516SVijay Khemka                 symbols.create_variable(name);
3273ed9a516SVijay Khemka                 paramMap.emplace(std::move(name), std::move(paramPtr));
328abcc94faSVijay Khemka             }
329abcc94faSVijay Khemka             else
330abcc94faSVijay Khemka             {
331abcc94faSVijay Khemka                 /* Invalid configuration */
332abcc94faSVijay Khemka                 throw std::invalid_argument(
333abcc94faSVijay Khemka                     "ParamName not found in configuration");
334abcc94faSVijay Khemka             }
335abcc94faSVijay Khemka         }
336abcc94faSVijay Khemka     }
337abcc94faSVijay Khemka 
3387452a867SVijay Khemka     /* Check for dbus parameter */
3397452a867SVijay Khemka     auto dbusParams = params.value("DbusParam", empty);
3407452a867SVijay Khemka     if (!dbusParams.empty())
3417452a867SVijay Khemka     {
3427452a867SVijay Khemka         for (auto& j : dbusParams)
3437452a867SVijay Khemka         {
3447452a867SVijay Khemka             /* Get parameter dbus sensor descriptor */
3457452a867SVijay Khemka             auto desc = j.value("Desc", empty);
3467452a867SVijay Khemka             if ((!desc.empty()) && (j.find("ParamName") != j.end()))
3477452a867SVijay Khemka             {
3487452a867SVijay Khemka                 std::string sensorType = desc.value("SensorType", "");
3497452a867SVijay Khemka                 std::string name = desc.value("Name", "");
3507452a867SVijay Khemka 
3517452a867SVijay Khemka                 if (!sensorType.empty() && !name.empty())
3527452a867SVijay Khemka                 {
3531204b433SGeorge Liu                     auto path = sensorDbusPath + sensorType + "/" + name;
3547452a867SVijay Khemka 
35551f898e2SVijay Khemka                     auto paramPtr =
3561204b433SGeorge Liu                         std::make_unique<SensorParam>(bus, path, this);
3571204b433SGeorge Liu                     std::string paramName = j["ParamName"];
3581204b433SGeorge Liu                     symbols.create_variable(paramName);
3591204b433SGeorge Liu                     paramMap.emplace(std::move(paramName), std::move(paramPtr));
3607452a867SVijay Khemka                 }
3617452a867SVijay Khemka             }
3627452a867SVijay Khemka         }
3637452a867SVijay Khemka     }
364abcc94faSVijay Khemka 
3653ed9a516SVijay Khemka     symbols.add_constants();
3669f1ef4f5SMatt Spinler     symbols.add_package(vecopsPackage);
3673ed9a516SVijay Khemka     expression.register_symbol_table(symbols);
3683ed9a516SVijay Khemka 
3693ed9a516SVijay Khemka     /* parser from exprtk */
3703ed9a516SVijay Khemka     exprtk::parser<double> parser{};
371ddc6dcd6SMatt Spinler     if (!parser.compile(exprStr, expression))
372ddc6dcd6SMatt Spinler     {
37382b39c66SPatrick Williams         error("Expression compilation failed");
374ddc6dcd6SMatt Spinler 
375ddc6dcd6SMatt Spinler         for (std::size_t i = 0; i < parser.error_count(); ++i)
376ddc6dcd6SMatt Spinler         {
37782b39c66SPatrick Williams             auto err = parser.get_error(i);
37882b39c66SPatrick Williams             error("Error parsing token at {POSITION}: {ERROR}", "POSITION",
37982b39c66SPatrick Williams                   err.token.position, "TYPE",
38082b39c66SPatrick Williams                   exprtk::parser_error::to_str(err.mode), "ERROR",
38182b39c66SPatrick Williams                   err.diagnostic);
382ddc6dcd6SMatt Spinler         }
383ddc6dcd6SMatt Spinler         throw std::runtime_error("Expression compilation failed");
384ddc6dcd6SMatt Spinler     }
3853ed9a516SVijay Khemka 
386abcc94faSVijay Khemka     /* Print all parameters for debug purpose only */
387abcc94faSVijay Khemka     if (DEBUG)
388abcc94faSVijay Khemka         printParams(paramMap);
389abcc94faSVijay Khemka }
390abcc94faSVijay Khemka 
391e7efe135SRashmica Gupta void VirtualSensor::initVirtualSensor(const InterfaceMap& interfaceMap,
392e7efe135SRashmica Gupta                                       const std::string& objPath,
393e7efe135SRashmica Gupta                                       const std::string& sensorType,
394e7efe135SRashmica Gupta                                       const std::string& calculationIface)
395e7efe135SRashmica Gupta {
396e7efe135SRashmica Gupta     Json thresholds;
397e7efe135SRashmica Gupta     const std::string vsThresholdsIntf =
398e7efe135SRashmica Gupta         calculationIface + vsThresholdsIfaceSuffix;
399e7efe135SRashmica Gupta 
400e7efe135SRashmica Gupta     for (const auto& [interface, propertyMap] : interfaceMap)
401e7efe135SRashmica Gupta     {
402e7efe135SRashmica Gupta         /* Each threshold is on it's own interface with a number as a suffix
403e7efe135SRashmica Gupta          * eg xyz.openbmc_project.Configuration.ModifiedMedian.Thresholds1 */
404e7efe135SRashmica Gupta         if (interface.find(vsThresholdsIntf) != std::string::npos)
405e7efe135SRashmica Gupta         {
406e7efe135SRashmica Gupta             parseThresholds(thresholds, propertyMap);
407e7efe135SRashmica Gupta         }
408e7efe135SRashmica Gupta         else if (interface == calculationIface)
409e7efe135SRashmica Gupta         {
410e7efe135SRashmica Gupta             parseConfigInterface(propertyMap, sensorType, interface);
411e7efe135SRashmica Gupta         }
412e7efe135SRashmica Gupta     }
413e7efe135SRashmica Gupta 
414e7efe135SRashmica Gupta     createThresholds(thresholds, objPath);
415e7efe135SRashmica Gupta     symbols.add_constants();
416e7efe135SRashmica Gupta     symbols.add_package(vecopsPackage);
417e7efe135SRashmica Gupta     expression.register_symbol_table(symbols);
418e7efe135SRashmica Gupta 
419e7efe135SRashmica Gupta     /* Print all parameters for debug purpose only */
420e7efe135SRashmica Gupta     if (DEBUG)
421e7efe135SRashmica Gupta     {
422e7efe135SRashmica Gupta         printParams(paramMap);
423e7efe135SRashmica Gupta     }
424e7efe135SRashmica Gupta }
425e7efe135SRashmica Gupta 
426abcc94faSVijay Khemka void VirtualSensor::setSensorValue(double value)
427abcc94faSVijay Khemka {
428543bf668SPatrick Williams     value = std::clamp(value, ValueIface::minValue(), ValueIface::maxValue());
429abcc94faSVijay Khemka     ValueIface::value(value);
430abcc94faSVijay Khemka }
431abcc94faSVijay Khemka 
432304fd0e4SRashmica Gupta double VirtualSensor::calculateValue(const std::string& calculation,
433304fd0e4SRashmica Gupta                                      const VirtualSensor::ParamMap& paramMap)
434e7efe135SRashmica Gupta {
435304fd0e4SRashmica Gupta     auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
436304fd0e4SRashmica Gupta                          calculation);
437304fd0e4SRashmica Gupta     if (itr == calculationIfaces.end())
438304fd0e4SRashmica Gupta     {
439e7efe135SRashmica Gupta         return std::numeric_limits<double>::quiet_NaN();
440e7efe135SRashmica Gupta     }
441304fd0e4SRashmica Gupta     else if (calculation == "xyz.openbmc_project.Configuration.ModifiedMedian")
442304fd0e4SRashmica Gupta     {
443304fd0e4SRashmica Gupta         return calculateModifiedMedianValue(paramMap);
444304fd0e4SRashmica Gupta     }
445304fd0e4SRashmica Gupta     return std::numeric_limits<double>::quiet_NaN();
446304fd0e4SRashmica Gupta }
447304fd0e4SRashmica Gupta 
448304fd0e4SRashmica Gupta bool VirtualSensor::sensorInRange(double value)
449304fd0e4SRashmica Gupta {
450304fd0e4SRashmica Gupta     if (value <= this->maxValidInput && value >= this->minValidInput)
451304fd0e4SRashmica Gupta     {
452304fd0e4SRashmica Gupta         return true;
453304fd0e4SRashmica Gupta     }
454304fd0e4SRashmica Gupta     return false;
455304fd0e4SRashmica Gupta }
456e7efe135SRashmica Gupta 
457abcc94faSVijay Khemka void VirtualSensor::updateVirtualSensor()
4583ed9a516SVijay Khemka {
4593ed9a516SVijay Khemka     for (auto& param : paramMap)
4603ed9a516SVijay Khemka     {
4613ed9a516SVijay Khemka         auto& name = param.first;
4623ed9a516SVijay Khemka         auto& data = param.second;
4633ed9a516SVijay Khemka         if (auto var = symbols.get_variable(name))
4643ed9a516SVijay Khemka         {
4653ed9a516SVijay Khemka             var->ref() = data->getParamValue();
4663ed9a516SVijay Khemka         }
4673ed9a516SVijay Khemka         else
4683ed9a516SVijay Khemka         {
4693ed9a516SVijay Khemka             /* Invalid parameter */
4703ed9a516SVijay Khemka             throw std::invalid_argument("ParamName not found in symbols");
4713ed9a516SVijay Khemka         }
4723ed9a516SVijay Khemka     }
473e7efe135SRashmica Gupta     auto itr =
474e7efe135SRashmica Gupta         std::find(calculationIfaces.begin(), calculationIfaces.end(), exprStr);
475304fd0e4SRashmica Gupta     auto val = (itr == calculationIfaces.end())
476304fd0e4SRashmica Gupta                    ? expression.value()
477304fd0e4SRashmica Gupta                    : calculateValue(exprStr, paramMap);
47832a7156bSVijay Khemka 
47932a7156bSVijay Khemka     /* Set sensor value to dbus interface */
4803ed9a516SVijay Khemka     setSensorValue(val);
48132a7156bSVijay Khemka 
4823ed9a516SVijay Khemka     if (DEBUG)
483e7efe135SRashmica Gupta     {
484fbd7145eSPatrick Williams         debug("Sensor {NAME} = {VALUE}", "NAME", this->name, "VALUE", val);
485e7efe135SRashmica Gupta     }
48632a7156bSVijay Khemka 
4878f5e6119SMatt Spinler     /* Check sensor thresholds and log required message */
488b306b03dSMatt Spinler     checkThresholds(val, perfLossIface);
489fdb826d5SPatrick Williams     checkThresholds(val, warningIface);
490fdb826d5SPatrick Williams     checkThresholds(val, criticalIface);
491fdb826d5SPatrick Williams     checkThresholds(val, softShutdownIface);
492fdb826d5SPatrick Williams     checkThresholds(val, hardShutdownIface);
4933ed9a516SVijay Khemka }
494abcc94faSVijay Khemka 
495304fd0e4SRashmica Gupta double VirtualSensor::calculateModifiedMedianValue(
496304fd0e4SRashmica Gupta     const VirtualSensor::ParamMap& paramMap)
497304fd0e4SRashmica Gupta {
498304fd0e4SRashmica Gupta     std::vector<double> values;
499304fd0e4SRashmica Gupta 
500304fd0e4SRashmica Gupta     for (auto& param : paramMap)
501304fd0e4SRashmica Gupta     {
502304fd0e4SRashmica Gupta         auto& name = param.first;
503304fd0e4SRashmica Gupta         if (auto var = symbols.get_variable(name))
504304fd0e4SRashmica Gupta         {
505304fd0e4SRashmica Gupta             if (!sensorInRange(var->ref()))
506304fd0e4SRashmica Gupta             {
507304fd0e4SRashmica Gupta                 continue;
508304fd0e4SRashmica Gupta             }
509304fd0e4SRashmica Gupta             values.push_back(var->ref());
510304fd0e4SRashmica Gupta         }
511304fd0e4SRashmica Gupta     }
512304fd0e4SRashmica Gupta 
513304fd0e4SRashmica Gupta     size_t size = values.size();
514304fd0e4SRashmica Gupta     std::sort(values.begin(), values.end());
515304fd0e4SRashmica Gupta     switch (size)
516304fd0e4SRashmica Gupta     {
517304fd0e4SRashmica Gupta         case 2:
518304fd0e4SRashmica Gupta             /* Choose biggest value */
519304fd0e4SRashmica Gupta             return values.at(1);
520304fd0e4SRashmica Gupta         case 0:
521304fd0e4SRashmica Gupta             return std::numeric_limits<double>::quiet_NaN();
522304fd0e4SRashmica Gupta         default:
523304fd0e4SRashmica Gupta             /* Choose median value */
524304fd0e4SRashmica Gupta             if (size % 2 == 0)
525304fd0e4SRashmica Gupta             {
526304fd0e4SRashmica Gupta                 // Average of the two middle values
527304fd0e4SRashmica Gupta                 return (values.at(size / 2) + values.at(size / 2 - 1)) / 2;
528304fd0e4SRashmica Gupta             }
529304fd0e4SRashmica Gupta             else
530304fd0e4SRashmica Gupta             {
531304fd0e4SRashmica Gupta                 return values.at((size - 1) / 2);
532304fd0e4SRashmica Gupta             }
533304fd0e4SRashmica Gupta     }
534304fd0e4SRashmica Gupta }
535304fd0e4SRashmica Gupta 
5363e99919bSRashmica Gupta void VirtualSensor::createThresholds(const Json& threshold,
5373e99919bSRashmica Gupta                                      const std::string& objPath)
5383e99919bSRashmica Gupta {
5393e99919bSRashmica Gupta     if (threshold.empty())
5403e99919bSRashmica Gupta     {
5413e99919bSRashmica Gupta         return;
5423e99919bSRashmica Gupta     }
5433e99919bSRashmica Gupta     // Only create the threshold interfaces if
5443e99919bSRashmica Gupta     // at least one of their values is present.
5453e99919bSRashmica Gupta     if (threshold.contains("CriticalHigh") || threshold.contains("CriticalLow"))
5463e99919bSRashmica Gupta     {
5473e99919bSRashmica Gupta         criticalIface =
5483e99919bSRashmica Gupta             std::make_unique<Threshold<CriticalObject>>(bus, objPath.c_str());
5493e99919bSRashmica Gupta 
5503e99919bSRashmica Gupta         criticalIface->criticalHigh(threshold.value(
5513e99919bSRashmica Gupta             "CriticalHigh", std::numeric_limits<double>::quiet_NaN()));
5523e99919bSRashmica Gupta         criticalIface->criticalLow(threshold.value(
5533e99919bSRashmica Gupta             "CriticalLow", std::numeric_limits<double>::quiet_NaN()));
5541dff7dceSRashmica Gupta         criticalIface->setHighHysteresis(
5551dff7dceSRashmica Gupta             threshold.value("CriticalHighHysteresis", defaultHysteresis));
5561dff7dceSRashmica Gupta         criticalIface->setLowHysteresis(
5571dff7dceSRashmica Gupta             threshold.value("CriticalLowHysteresis", defaultHysteresis));
5583e99919bSRashmica Gupta     }
5593e99919bSRashmica Gupta 
5603e99919bSRashmica Gupta     if (threshold.contains("WarningHigh") || threshold.contains("WarningLow"))
5613e99919bSRashmica Gupta     {
5623e99919bSRashmica Gupta         warningIface =
5633e99919bSRashmica Gupta             std::make_unique<Threshold<WarningObject>>(bus, objPath.c_str());
5643e99919bSRashmica Gupta 
5653e99919bSRashmica Gupta         warningIface->warningHigh(threshold.value(
5663e99919bSRashmica Gupta             "WarningHigh", std::numeric_limits<double>::quiet_NaN()));
5673e99919bSRashmica Gupta         warningIface->warningLow(threshold.value(
5683e99919bSRashmica Gupta             "WarningLow", std::numeric_limits<double>::quiet_NaN()));
5691dff7dceSRashmica Gupta         warningIface->setHighHysteresis(
5701dff7dceSRashmica Gupta             threshold.value("WarningHighHysteresis", defaultHysteresis));
5711dff7dceSRashmica Gupta         warningIface->setLowHysteresis(
5721dff7dceSRashmica Gupta             threshold.value("WarningLowHysteresis", defaultHysteresis));
5733e99919bSRashmica Gupta     }
5743e99919bSRashmica Gupta 
5753e99919bSRashmica Gupta     if (threshold.contains("HardShutdownHigh") ||
5763e99919bSRashmica Gupta         threshold.contains("HardShutdownLow"))
5773e99919bSRashmica Gupta     {
5783e99919bSRashmica Gupta         hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>(
5793e99919bSRashmica Gupta             bus, objPath.c_str());
5803e99919bSRashmica Gupta 
5813e99919bSRashmica Gupta         hardShutdownIface->hardShutdownHigh(threshold.value(
5823e99919bSRashmica Gupta             "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
5833e99919bSRashmica Gupta         hardShutdownIface->hardShutdownLow(threshold.value(
5843e99919bSRashmica Gupta             "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
5851dff7dceSRashmica Gupta         hardShutdownIface->setHighHysteresis(
5861dff7dceSRashmica Gupta             threshold.value("HardShutdownHighHysteresis", defaultHysteresis));
5871dff7dceSRashmica Gupta         hardShutdownIface->setLowHysteresis(
5881dff7dceSRashmica Gupta             threshold.value("HardShutdownLowHysteresis", defaultHysteresis));
5893e99919bSRashmica Gupta     }
5903e99919bSRashmica Gupta 
5913e99919bSRashmica Gupta     if (threshold.contains("SoftShutdownHigh") ||
5923e99919bSRashmica Gupta         threshold.contains("SoftShutdownLow"))
5933e99919bSRashmica Gupta     {
5943e99919bSRashmica Gupta         softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>(
5953e99919bSRashmica Gupta             bus, objPath.c_str());
5963e99919bSRashmica Gupta 
5973e99919bSRashmica Gupta         softShutdownIface->softShutdownHigh(threshold.value(
5983e99919bSRashmica Gupta             "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
5993e99919bSRashmica Gupta         softShutdownIface->softShutdownLow(threshold.value(
6003e99919bSRashmica Gupta             "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
6011dff7dceSRashmica Gupta         softShutdownIface->setHighHysteresis(
6021dff7dceSRashmica Gupta             threshold.value("SoftShutdownHighHysteresis", defaultHysteresis));
6031dff7dceSRashmica Gupta         softShutdownIface->setLowHysteresis(
6041dff7dceSRashmica Gupta             threshold.value("SoftShutdownLowHysteresis", defaultHysteresis));
6053e99919bSRashmica Gupta     }
6063e99919bSRashmica Gupta 
6073e99919bSRashmica Gupta     if (threshold.contains("PerformanceLossHigh") ||
6083e99919bSRashmica Gupta         threshold.contains("PerformanceLossLow"))
6093e99919bSRashmica Gupta     {
6103e99919bSRashmica Gupta         perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>(
6113e99919bSRashmica Gupta             bus, objPath.c_str());
6123e99919bSRashmica Gupta 
6133e99919bSRashmica Gupta         perfLossIface->performanceLossHigh(threshold.value(
6143e99919bSRashmica Gupta             "PerformanceLossHigh", std::numeric_limits<double>::quiet_NaN()));
6153e99919bSRashmica Gupta         perfLossIface->performanceLossLow(threshold.value(
6163e99919bSRashmica Gupta             "PerformanceLossLow", std::numeric_limits<double>::quiet_NaN()));
6171dff7dceSRashmica Gupta         perfLossIface->setHighHysteresis(threshold.value(
6181dff7dceSRashmica Gupta             "PerformanceLossHighHysteresis", defaultHysteresis));
6191dff7dceSRashmica Gupta         perfLossIface->setLowHysteresis(
6201dff7dceSRashmica Gupta             threshold.value("PerformanceLossLowHysteresis", defaultHysteresis));
6213e99919bSRashmica Gupta     }
6223e99919bSRashmica Gupta }
6233e99919bSRashmica Gupta 
624e7efe135SRashmica Gupta ManagedObjectType VirtualSensors::getObjectsFromDBus()
625e7efe135SRashmica Gupta {
626e7efe135SRashmica Gupta     ManagedObjectType objects;
627e7efe135SRashmica Gupta 
628e7efe135SRashmica Gupta     try
629e7efe135SRashmica Gupta     {
630e7efe135SRashmica Gupta         auto method = bus.new_method_call(entityManagerBusName, "/",
631e7efe135SRashmica Gupta                                           "org.freedesktop.DBus.ObjectManager",
632e7efe135SRashmica Gupta                                           "GetManagedObjects");
633e7efe135SRashmica Gupta         auto reply = bus.call(method);
634e7efe135SRashmica Gupta         reply.read(objects);
635e7efe135SRashmica Gupta     }
63674c1e7d8SPatrick Williams     catch (const sdbusplus::exception::exception& ex)
637e7efe135SRashmica Gupta     {
638e7efe135SRashmica Gupta         // If entity manager isn't running yet, keep going.
639e7efe135SRashmica Gupta         if (std::string("org.freedesktop.DBus.Error.ServiceUnknown") !=
640e7efe135SRashmica Gupta             ex.name())
641e7efe135SRashmica Gupta         {
642e7efe135SRashmica Gupta             throw ex.name();
643e7efe135SRashmica Gupta         }
644e7efe135SRashmica Gupta     }
645e7efe135SRashmica Gupta 
646e7efe135SRashmica Gupta     return objects;
647e7efe135SRashmica Gupta }
648e7efe135SRashmica Gupta 
649e7efe135SRashmica Gupta void VirtualSensors::propertiesChanged(sdbusplus::message::message& msg)
650e7efe135SRashmica Gupta {
651e7efe135SRashmica Gupta     std::string path;
652e7efe135SRashmica Gupta     PropertyMap properties;
653e7efe135SRashmica Gupta 
654e7efe135SRashmica Gupta     msg.read(path, properties);
655e7efe135SRashmica Gupta 
656e7efe135SRashmica Gupta     /* We get multiple callbacks for one sensor. 'Type' is a required field and
657e7efe135SRashmica Gupta      * is a unique label so use to to only proceed once per sensor */
658e7efe135SRashmica Gupta     if (properties.contains("Type"))
659e7efe135SRashmica Gupta     {
660e7efe135SRashmica Gupta         if (isCalculationType(path))
661e7efe135SRashmica Gupta         {
662e7efe135SRashmica Gupta             createVirtualSensorsFromDBus(path);
663e7efe135SRashmica Gupta         }
664e7efe135SRashmica Gupta     }
665e7efe135SRashmica Gupta }
666e7efe135SRashmica Gupta 
667abcc94faSVijay Khemka /** @brief Parsing Virtual Sensor config JSON file  */
6681204b433SGeorge Liu Json VirtualSensors::parseConfigFile(const std::string& configFile)
669abcc94faSVijay Khemka {
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},
691e0d371e4SVijay Khemka     {"voltage", ValueIface::Unit::Volts},
692e0d371e4SVijay Khemka     {"altitude", ValueIface::Unit::Meters},
693e0d371e4SVijay Khemka     {"current", ValueIface::Unit::Amperes},
694e0d371e4SVijay Khemka     {"power", ValueIface::Unit::Watts},
695e0d371e4SVijay Khemka     {"energy", ValueIface::Unit::Joules},
6962b56ddb3SKumar Thangavel     {"utilization", ValueIface::Unit::Percent},
6974ac7a7f2SRashmica Gupta     {"airflow", ValueIface::Unit::CFM},
6984ac7a7f2SRashmica Gupta     {"pressure", ValueIface::Unit::Pascals}};
699e0d371e4SVijay Khemka 
700e7efe135SRashmica Gupta const std::string getSensorTypeFromUnit(const std::string& unit)
701e7efe135SRashmica Gupta {
702e7efe135SRashmica Gupta     std::string unitPrefix = "xyz.openbmc_project.Sensor.Value.Unit.";
703e7efe135SRashmica Gupta     for (auto [type, unitObj] : unitMap)
704e7efe135SRashmica Gupta     {
705e7efe135SRashmica Gupta         auto unitPath = ValueIface::convertUnitToString(unitObj);
706e7efe135SRashmica Gupta         if (unitPath == (unitPrefix + unit))
707e7efe135SRashmica Gupta         {
708e7efe135SRashmica Gupta             return type;
709e7efe135SRashmica Gupta         }
710e7efe135SRashmica Gupta     }
711e7efe135SRashmica Gupta     return "";
712e7efe135SRashmica Gupta }
713e7efe135SRashmica Gupta 
714e7efe135SRashmica Gupta void VirtualSensors::setupMatches()
715e7efe135SRashmica Gupta {
716e7efe135SRashmica Gupta     /* Already setup */
717e7efe135SRashmica Gupta     if (!this->matches.empty())
718e7efe135SRashmica Gupta     {
719e7efe135SRashmica Gupta         return;
720e7efe135SRashmica Gupta     }
721e7efe135SRashmica Gupta 
722e7efe135SRashmica Gupta     /* Setup matches */
723e7efe135SRashmica Gupta     auto eventHandler = [this](sdbusplus::message::message& message) {
724e7efe135SRashmica Gupta         if (message.is_method_error())
725e7efe135SRashmica Gupta         {
72682b39c66SPatrick Williams             error("Callback method error");
727e7efe135SRashmica Gupta             return;
728e7efe135SRashmica Gupta         }
729e7efe135SRashmica Gupta         this->propertiesChanged(message);
730e7efe135SRashmica Gupta     };
731e7efe135SRashmica Gupta 
732e7efe135SRashmica Gupta     for (const char* iface : calculationIfaces)
733e7efe135SRashmica Gupta     {
734e7efe135SRashmica Gupta         auto match = std::make_unique<sdbusplus::bus::match::match>(
735e7efe135SRashmica Gupta             bus,
736e7efe135SRashmica Gupta             sdbusplus::bus::match::rules::propertiesChangedNamespace(
737e7efe135SRashmica Gupta                 "/xyz/openbmc_project/inventory", iface),
738e7efe135SRashmica Gupta             eventHandler);
739e7efe135SRashmica Gupta         this->matches.emplace_back(std::move(match));
740e7efe135SRashmica Gupta     }
741e7efe135SRashmica Gupta }
742e7efe135SRashmica Gupta 
743e7efe135SRashmica Gupta void VirtualSensors::createVirtualSensorsFromDBus(
744e7efe135SRashmica Gupta     const std::string& calculationIface)
745e7efe135SRashmica Gupta {
746e7efe135SRashmica Gupta     if (calculationIface.empty())
747e7efe135SRashmica Gupta     {
74882b39c66SPatrick Williams         error("No calculation type supplied");
749e7efe135SRashmica Gupta         return;
750e7efe135SRashmica Gupta     }
751e7efe135SRashmica Gupta     auto objects = getObjectsFromDBus();
752e7efe135SRashmica Gupta 
753e7efe135SRashmica Gupta     /* Get virtual sensors config data */
754e7efe135SRashmica Gupta     for (const auto& [path, interfaceMap] : objects)
755e7efe135SRashmica Gupta     {
756e7efe135SRashmica Gupta         auto objpath = static_cast<std::string>(path);
757e7efe135SRashmica Gupta         std::string name = path.filename();
758e7efe135SRashmica Gupta         std::string sensorType, sensorUnit;
759e7efe135SRashmica Gupta 
760e7efe135SRashmica Gupta         /* Find Virtual Sensor interfaces */
761e7efe135SRashmica Gupta         if (!interfaceMap.contains(calculationIface))
762e7efe135SRashmica Gupta         {
763e7efe135SRashmica Gupta             continue;
764e7efe135SRashmica Gupta         }
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 */
778e7efe135SRashmica Gupta         for (const auto& [interface, propertyMap] : interfaceMap)
779e7efe135SRashmica Gupta         {
780e7efe135SRashmica Gupta             if (interface != calculationIface)
781e7efe135SRashmica Gupta             {
782e7efe135SRashmica Gupta                 continue;
783e7efe135SRashmica Gupta             }
784e7efe135SRashmica Gupta             auto itr = propertyMap.find("Units");
785e7efe135SRashmica Gupta             if (itr != propertyMap.end())
786e7efe135SRashmica Gupta             {
787e7efe135SRashmica Gupta                 sensorUnit = std::get<std::string>(itr->second);
788e7efe135SRashmica Gupta                 break;
789e7efe135SRashmica Gupta             }
790e7efe135SRashmica Gupta         }
791e7efe135SRashmica Gupta         sensorType = getSensorTypeFromUnit(sensorUnit);
792e7efe135SRashmica Gupta         if (sensorType.empty())
793e7efe135SRashmica Gupta         {
79482b39c66SPatrick Williams             error("Sensor unit type {TYPE} is not supported", "TYPE",
79582b39c66SPatrick Williams                   sensorUnit);
796e7efe135SRashmica Gupta             continue;
797e7efe135SRashmica Gupta         }
798e7efe135SRashmica Gupta 
799e7efe135SRashmica Gupta         try
800e7efe135SRashmica Gupta         {
801e7efe135SRashmica Gupta             auto virtObjPath = sensorDbusPath + sensorType + "/" + name;
802e7efe135SRashmica Gupta 
803e7efe135SRashmica Gupta             auto virtualSensorPtr = std::make_unique<VirtualSensor>(
804e7efe135SRashmica Gupta                 bus, virtObjPath.c_str(), interfaceMap, name, sensorType,
805e7efe135SRashmica Gupta                 calculationIface);
80682b39c66SPatrick Williams             info("Added a new virtual sensor: {NAME} {TYPE}", "NAME", name,
80782b39c66SPatrick Williams                  "TYPE", sensorType);
808e7efe135SRashmica Gupta             virtualSensorPtr->updateVirtualSensor();
809e7efe135SRashmica Gupta 
810e7efe135SRashmica Gupta             /* Initialize unit value for virtual sensor */
811e7efe135SRashmica Gupta             virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
812e7efe135SRashmica Gupta             virtualSensorPtr->emit_object_added();
813e7efe135SRashmica Gupta 
814e7efe135SRashmica Gupta             virtualSensorsMap.emplace(name, std::move(virtualSensorPtr));
815e7efe135SRashmica Gupta 
816e7efe135SRashmica Gupta             /* Setup match for interfaces removed */
817e7efe135SRashmica Gupta             auto intfRemoved = [this, objpath,
818e7efe135SRashmica Gupta                                 name](sdbusplus::message::message& message) {
819e7efe135SRashmica Gupta                 if (!virtualSensorsMap.contains(name))
820e7efe135SRashmica Gupta                 {
821e7efe135SRashmica Gupta                     return;
822e7efe135SRashmica Gupta                 }
823e7efe135SRashmica Gupta                 sdbusplus::message::object_path path;
824e7efe135SRashmica Gupta                 message.read(path);
825e7efe135SRashmica Gupta                 if (static_cast<const std::string&>(path) == objpath)
826e7efe135SRashmica Gupta                 {
82782b39c66SPatrick Williams                     info("Removed a virtual sensor: {NAME}", "NAME", name);
828e7efe135SRashmica Gupta                     virtualSensorsMap.erase(name);
829e7efe135SRashmica Gupta                 }
830e7efe135SRashmica Gupta             };
831e7efe135SRashmica Gupta             auto matchOnRemove = std::make_unique<sdbusplus::bus::match::match>(
832e7efe135SRashmica Gupta                 bus,
833e7efe135SRashmica Gupta                 sdbusplus::bus::match::rules::interfacesRemoved() +
834e7efe135SRashmica Gupta                     sdbusplus::bus::match::rules::argNpath(0, objpath),
835e7efe135SRashmica Gupta                 intfRemoved);
836e7efe135SRashmica Gupta             /* TODO: slight race condition here. Check that the config still
837e7efe135SRashmica Gupta              * exists */
838e7efe135SRashmica Gupta             this->matches.emplace_back(std::move(matchOnRemove));
839e7efe135SRashmica Gupta         }
840dac2663cSPatrick Williams         catch (const std::invalid_argument& ia)
841e7efe135SRashmica Gupta         {
84282b39c66SPatrick Williams             error("Failed to set up virtual sensor: {ERROR}", "ERROR", ia);
843e7efe135SRashmica Gupta         }
844e7efe135SRashmica Gupta     }
845e7efe135SRashmica Gupta }
846e7efe135SRashmica Gupta 
847abcc94faSVijay Khemka void VirtualSensors::createVirtualSensors()
848abcc94faSVijay Khemka {
849abcc94faSVijay Khemka     static const Json empty{};
850abcc94faSVijay Khemka 
851abcc94faSVijay Khemka     auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE);
852e7efe135SRashmica Gupta 
853abcc94faSVijay Khemka     // print values
854abcc94faSVijay Khemka     if (DEBUG)
855e7efe135SRashmica Gupta     {
856fbd7145eSPatrick Williams         debug("JSON: {JSON}", "JSON", data.dump());
857e7efe135SRashmica Gupta     }
858abcc94faSVijay Khemka 
859abcc94faSVijay Khemka     /* Get virtual sensors  config data */
860abcc94faSVijay Khemka     for (const auto& j : data)
861abcc94faSVijay Khemka     {
862abcc94faSVijay Khemka         auto desc = j.value("Desc", empty);
863abcc94faSVijay Khemka         if (!desc.empty())
864abcc94faSVijay Khemka         {
865e7efe135SRashmica Gupta             if (desc.value("Config", "") == "D-Bus")
866e7efe135SRashmica Gupta             {
867e7efe135SRashmica Gupta                 /* Look on D-Bus for a virtual sensor config. Set up matches
868e7efe135SRashmica Gupta                  * first because the configs may not be on D-Bus yet and we
869e7efe135SRashmica Gupta                  * don't want to miss them */
870e7efe135SRashmica Gupta                 setupMatches();
871e7efe135SRashmica Gupta 
872e7efe135SRashmica Gupta                 if (desc.contains("Type"))
873e7efe135SRashmica Gupta                 {
87482b39c66SPatrick Williams                     auto type = desc.value("Type", "");
87582b39c66SPatrick Williams                     auto path = "xyz.openbmc_project.Configuration." + type;
87682b39c66SPatrick Williams 
877e7efe135SRashmica Gupta                     if (!isCalculationType(path))
878e7efe135SRashmica Gupta                     {
87982b39c66SPatrick Williams                         error("Invalid calculation type {TYPE} supplied.",
88082b39c66SPatrick Williams                               "TYPE", type);
881e7efe135SRashmica Gupta                         continue;
882e7efe135SRashmica Gupta                     }
883e7efe135SRashmica Gupta                     createVirtualSensorsFromDBus(path);
884e7efe135SRashmica Gupta                 }
885e7efe135SRashmica Gupta                 continue;
886e7efe135SRashmica Gupta             }
887e7efe135SRashmica Gupta 
888abcc94faSVijay Khemka             std::string sensorType = desc.value("SensorType", "");
889abcc94faSVijay Khemka             std::string name = desc.value("Name", "");
890665a0a29SRashmica Gupta             std::replace(name.begin(), name.end(), ' ', '_');
891abcc94faSVijay Khemka 
892abcc94faSVijay Khemka             if (!name.empty() && !sensorType.empty())
893abcc94faSVijay Khemka             {
894e0d371e4SVijay Khemka                 if (unitMap.find(sensorType) == unitMap.end())
895e0d371e4SVijay Khemka                 {
89682b39c66SPatrick Williams                     error("Sensor type {TYPE} is not supported", "TYPE",
89782b39c66SPatrick Williams                           sensorType);
898e0d371e4SVijay Khemka                 }
899e0d371e4SVijay Khemka                 else
900e0d371e4SVijay Khemka                 {
90167d8b9d2SRashmica Gupta                     if (virtualSensorsMap.find(name) != virtualSensorsMap.end())
90267d8b9d2SRashmica Gupta                     {
90382b39c66SPatrick Williams                         error("A virtual sensor named {NAME} already exists",
90482b39c66SPatrick Williams                               "NAME", name);
90567d8b9d2SRashmica Gupta                         continue;
90667d8b9d2SRashmica Gupta                     }
907862c3d1eSRashmica Gupta                     auto objPath = sensorDbusPath + sensorType + "/" + name;
908abcc94faSVijay Khemka 
90932a7156bSVijay Khemka                     auto virtualSensorPtr = std::make_unique<VirtualSensor>(
91032a7156bSVijay Khemka                         bus, objPath.c_str(), j, name);
911abcc94faSVijay Khemka 
91282b39c66SPatrick Williams                     info("Added a new virtual sensor: {NAME}", "NAME", name);
9133ed9a516SVijay Khemka                     virtualSensorPtr->updateVirtualSensor();
914e0d371e4SVijay Khemka 
915e0d371e4SVijay Khemka                     /* Initialize unit value for virtual sensor */
916e0d371e4SVijay Khemka                     virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
917a2fa63a6SRashmica Gupta                     virtualSensorPtr->emit_object_added();
918e0d371e4SVijay Khemka 
9193ed9a516SVijay Khemka                     virtualSensorsMap.emplace(std::move(name),
9203ed9a516SVijay Khemka                                               std::move(virtualSensorPtr));
921abcc94faSVijay Khemka                 }
922e0d371e4SVijay Khemka             }
923abcc94faSVijay Khemka             else
924abcc94faSVijay Khemka             {
92582b39c66SPatrick Williams                 error(
92682b39c66SPatrick Williams                     "Sensor type ({TYPE}) or name ({NAME}) not found in config file",
92782b39c66SPatrick Williams                     "NAME", name, "TYPE", sensorType);
928abcc94faSVijay Khemka             }
929abcc94faSVijay Khemka         }
930abcc94faSVijay Khemka         else
931abcc94faSVijay Khemka         {
93282b39c66SPatrick Williams             error("Descriptor for new virtual sensor not found in config file");
933abcc94faSVijay Khemka         }
934abcc94faSVijay Khemka     }
935abcc94faSVijay Khemka }
936abcc94faSVijay Khemka 
937abcc94faSVijay Khemka } // namespace virtualSensor
938abcc94faSVijay Khemka } // namespace phosphor
939abcc94faSVijay Khemka 
940abcc94faSVijay Khemka /**
941abcc94faSVijay Khemka  * @brief Main
942abcc94faSVijay Khemka  */
943abcc94faSVijay Khemka int main()
944abcc94faSVijay Khemka {
945abcc94faSVijay Khemka 
946abcc94faSVijay Khemka     // Get a default event loop
947abcc94faSVijay Khemka     auto event = sdeventplus::Event::get_default();
948abcc94faSVijay Khemka 
949abcc94faSVijay Khemka     // Get a handle to system dbus
950abcc94faSVijay Khemka     auto bus = sdbusplus::bus::new_default();
951abcc94faSVijay Khemka 
9526c19e7d2SMatt Spinler     // Add the ObjectManager interface
9536c19e7d2SMatt Spinler     sdbusplus::server::manager::manager objManager(bus, "/");
9546c19e7d2SMatt Spinler 
955abcc94faSVijay Khemka     // Create an virtual sensors object
956abcc94faSVijay Khemka     phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
957abcc94faSVijay Khemka 
958abcc94faSVijay Khemka     // Request service bus name
959abcc94faSVijay Khemka     bus.request_name(busName);
960abcc94faSVijay Khemka 
961abcc94faSVijay Khemka     // Attach the bus to sd_event to service user requests
962abcc94faSVijay Khemka     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
963abcc94faSVijay Khemka     event.loop();
964abcc94faSVijay Khemka 
965abcc94faSVijay Khemka     return 0;
966abcc94faSVijay Khemka }
967