1*6272a393SAlexander Hansen #include "virtualSensor.hpp"
2*6272a393SAlexander Hansen
3*6272a393SAlexander Hansen #include "calculate.hpp"
4*6272a393SAlexander Hansen
5*6272a393SAlexander Hansen #include <phosphor-logging/lg2.hpp>
6*6272a393SAlexander Hansen
7*6272a393SAlexander Hansen #include <fstream>
8*6272a393SAlexander Hansen
9*6272a393SAlexander Hansen static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/";
10*6272a393SAlexander Hansen static constexpr auto vsThresholdsIfaceSuffix = ".Thresholds";
11*6272a393SAlexander Hansen static constexpr auto defaultHysteresis = 0;
12*6272a393SAlexander Hansen
13*6272a393SAlexander Hansen PHOSPHOR_LOG2_USING_WITH_FLAGS;
14*6272a393SAlexander Hansen
15*6272a393SAlexander Hansen namespace phosphor::virtual_sensor
16*6272a393SAlexander Hansen {
17*6272a393SAlexander Hansen
18*6272a393SAlexander Hansen FuncMaxIgnoreNaN<double> VirtualSensor::funcMaxIgnoreNaN;
19*6272a393SAlexander Hansen FuncSumIgnoreNaN<double> VirtualSensor::funcSumIgnoreNaN;
20*6272a393SAlexander Hansen FuncIfNan<double> VirtualSensor::funcIfNan;
21*6272a393SAlexander Hansen
22*6272a393SAlexander Hansen std::map<std::string, ValueIface::Unit> unitMap = {
23*6272a393SAlexander Hansen {"temperature", ValueIface::Unit::DegreesC},
24*6272a393SAlexander Hansen {"fan_tach", ValueIface::Unit::RPMS},
25*6272a393SAlexander Hansen {"fan_pwm", ValueIface::Unit::Percent},
26*6272a393SAlexander Hansen {"voltage", ValueIface::Unit::Volts},
27*6272a393SAlexander Hansen {"altitude", ValueIface::Unit::Meters},
28*6272a393SAlexander Hansen {"current", ValueIface::Unit::Amperes},
29*6272a393SAlexander Hansen {"power", ValueIface::Unit::Watts},
30*6272a393SAlexander Hansen {"energy", ValueIface::Unit::Joules},
31*6272a393SAlexander Hansen {"utilization", ValueIface::Unit::Percent},
32*6272a393SAlexander Hansen {"airflow", ValueIface::Unit::CFM},
33*6272a393SAlexander Hansen {"pressure", ValueIface::Unit::Pascals}};
34*6272a393SAlexander Hansen
printParams(const VirtualSensor::ParamMap & paramMap)35*6272a393SAlexander Hansen void printParams(const VirtualSensor::ParamMap& paramMap)
36*6272a393SAlexander Hansen {
37*6272a393SAlexander Hansen for (const auto& p : paramMap)
38*6272a393SAlexander Hansen {
39*6272a393SAlexander Hansen const auto& p1 = p.first;
40*6272a393SAlexander Hansen const auto& p2 = p.second;
41*6272a393SAlexander Hansen auto val = p2->getParamValue();
42*6272a393SAlexander Hansen debug("Parameter: {PARAM} = {VALUE}", "PARAM", p1, "VALUE", val);
43*6272a393SAlexander Hansen }
44*6272a393SAlexander Hansen }
45*6272a393SAlexander Hansen
getParamValue()46*6272a393SAlexander Hansen double SensorParam::getParamValue()
47*6272a393SAlexander Hansen {
48*6272a393SAlexander Hansen switch (paramType)
49*6272a393SAlexander Hansen {
50*6272a393SAlexander Hansen case constParam:
51*6272a393SAlexander Hansen return value;
52*6272a393SAlexander Hansen break;
53*6272a393SAlexander Hansen case dbusParam:
54*6272a393SAlexander Hansen return dbusSensor->getSensorValue();
55*6272a393SAlexander Hansen break;
56*6272a393SAlexander Hansen default:
57*6272a393SAlexander Hansen throw std::invalid_argument("param type not supported");
58*6272a393SAlexander Hansen }
59*6272a393SAlexander Hansen }
60*6272a393SAlexander Hansen
61*6272a393SAlexander Hansen using AssociationList =
62*6272a393SAlexander Hansen std::vector<std::tuple<std::string, std::string, std::string>>;
63*6272a393SAlexander Hansen
getAssociationsFromJson(const Json & j)64*6272a393SAlexander Hansen AssociationList getAssociationsFromJson(const Json& j)
65*6272a393SAlexander Hansen {
66*6272a393SAlexander Hansen AssociationList assocs{};
67*6272a393SAlexander Hansen try
68*6272a393SAlexander Hansen {
69*6272a393SAlexander Hansen j.get_to(assocs);
70*6272a393SAlexander Hansen }
71*6272a393SAlexander Hansen catch (const std::exception& ex)
72*6272a393SAlexander Hansen {
73*6272a393SAlexander Hansen error("Failed to parse association: {ERROR}", "ERROR", ex);
74*6272a393SAlexander Hansen }
75*6272a393SAlexander Hansen return assocs;
76*6272a393SAlexander Hansen }
77*6272a393SAlexander Hansen
78*6272a393SAlexander Hansen template <typename U>
79*6272a393SAlexander Hansen struct VariantToNumber
80*6272a393SAlexander Hansen {
81*6272a393SAlexander Hansen template <typename T>
operator ()phosphor::virtual_sensor::VariantToNumber82*6272a393SAlexander Hansen U operator()(const T& t) const
83*6272a393SAlexander Hansen {
84*6272a393SAlexander Hansen if constexpr (std::is_convertible<T, U>::value)
85*6272a393SAlexander Hansen {
86*6272a393SAlexander Hansen return static_cast<U>(t);
87*6272a393SAlexander Hansen }
88*6272a393SAlexander Hansen throw std::invalid_argument("Invalid number type in config\n");
89*6272a393SAlexander Hansen }
90*6272a393SAlexander Hansen };
91*6272a393SAlexander Hansen
92*6272a393SAlexander Hansen template <typename U>
getNumberFromConfig(const PropertyMap & map,const std::string & name,bool required,U defaultValue=std::numeric_limits<U>::quiet_NaN ())93*6272a393SAlexander Hansen U getNumberFromConfig(const PropertyMap& map, const std::string& name,
94*6272a393SAlexander Hansen bool required,
95*6272a393SAlexander Hansen U defaultValue = std::numeric_limits<U>::quiet_NaN())
96*6272a393SAlexander Hansen {
97*6272a393SAlexander Hansen if (auto itr = map.find(name); itr != map.end())
98*6272a393SAlexander Hansen {
99*6272a393SAlexander Hansen return std::visit(VariantToNumber<U>(), itr->second);
100*6272a393SAlexander Hansen }
101*6272a393SAlexander Hansen else if (required)
102*6272a393SAlexander Hansen {
103*6272a393SAlexander Hansen error("Required field {NAME} missing in config", "NAME", name);
104*6272a393SAlexander Hansen throw std::invalid_argument("Required field missing in config");
105*6272a393SAlexander Hansen }
106*6272a393SAlexander Hansen return defaultValue;
107*6272a393SAlexander Hansen }
108*6272a393SAlexander Hansen
getThresholdType(const std::string & direction,const std::string & severity)109*6272a393SAlexander Hansen const std::string getThresholdType(const std::string& direction,
110*6272a393SAlexander Hansen const std::string& severity)
111*6272a393SAlexander Hansen {
112*6272a393SAlexander Hansen std::string suffix;
113*6272a393SAlexander Hansen
114*6272a393SAlexander Hansen if (direction == "less than")
115*6272a393SAlexander Hansen {
116*6272a393SAlexander Hansen suffix = "Low";
117*6272a393SAlexander Hansen }
118*6272a393SAlexander Hansen else if (direction == "greater than")
119*6272a393SAlexander Hansen {
120*6272a393SAlexander Hansen suffix = "High";
121*6272a393SAlexander Hansen }
122*6272a393SAlexander Hansen else
123*6272a393SAlexander Hansen {
124*6272a393SAlexander Hansen throw std::invalid_argument(
125*6272a393SAlexander Hansen "Invalid threshold direction specified in entity manager");
126*6272a393SAlexander Hansen }
127*6272a393SAlexander Hansen return severity + suffix;
128*6272a393SAlexander Hansen }
129*6272a393SAlexander Hansen
getSeverityField(const PropertyMap & propertyMap)130*6272a393SAlexander Hansen std::string getSeverityField(const PropertyMap& propertyMap)
131*6272a393SAlexander Hansen {
132*6272a393SAlexander Hansen static const std::array thresholdTypes{
133*6272a393SAlexander Hansen "Warning", "Critical", "PerformanceLoss", "SoftShutdown",
134*6272a393SAlexander Hansen "HardShutdown"};
135*6272a393SAlexander Hansen
136*6272a393SAlexander Hansen std::string severity;
137*6272a393SAlexander Hansen if (auto itr = propertyMap.find("Severity"); itr != propertyMap.end())
138*6272a393SAlexander Hansen {
139*6272a393SAlexander Hansen /* Severity should be a string, but can be an unsigned int */
140*6272a393SAlexander Hansen if (std::holds_alternative<std::string>(itr->second))
141*6272a393SAlexander Hansen {
142*6272a393SAlexander Hansen severity = std::get<std::string>(itr->second);
143*6272a393SAlexander Hansen if (0 == std::ranges::count(thresholdTypes, severity))
144*6272a393SAlexander Hansen {
145*6272a393SAlexander Hansen throw std::invalid_argument(
146*6272a393SAlexander Hansen "Invalid threshold severity specified in entity manager");
147*6272a393SAlexander Hansen }
148*6272a393SAlexander Hansen }
149*6272a393SAlexander Hansen else
150*6272a393SAlexander Hansen {
151*6272a393SAlexander Hansen auto sev =
152*6272a393SAlexander Hansen getNumberFromConfig<uint64_t>(propertyMap, "Severity", true);
153*6272a393SAlexander Hansen /* Checking bounds ourselves so we throw invalid argument on
154*6272a393SAlexander Hansen * invalid user input */
155*6272a393SAlexander Hansen if (sev >= thresholdTypes.size())
156*6272a393SAlexander Hansen {
157*6272a393SAlexander Hansen throw std::invalid_argument(
158*6272a393SAlexander Hansen "Invalid threshold severity specified in entity manager");
159*6272a393SAlexander Hansen }
160*6272a393SAlexander Hansen severity = thresholdTypes.at(sev);
161*6272a393SAlexander Hansen }
162*6272a393SAlexander Hansen }
163*6272a393SAlexander Hansen return severity;
164*6272a393SAlexander Hansen }
165*6272a393SAlexander Hansen
parseThresholds(Json & thresholds,const PropertyMap & propertyMap,const std::string & entityInterface="")166*6272a393SAlexander Hansen void parseThresholds(Json& thresholds, const PropertyMap& propertyMap,
167*6272a393SAlexander Hansen const std::string& entityInterface = "")
168*6272a393SAlexander Hansen {
169*6272a393SAlexander Hansen std::string direction;
170*6272a393SAlexander Hansen
171*6272a393SAlexander Hansen auto value = getNumberFromConfig<double>(propertyMap, "Value", true);
172*6272a393SAlexander Hansen
173*6272a393SAlexander Hansen auto severity = getSeverityField(propertyMap);
174*6272a393SAlexander Hansen
175*6272a393SAlexander Hansen if (auto itr = propertyMap.find("Direction"); itr != propertyMap.end())
176*6272a393SAlexander Hansen {
177*6272a393SAlexander Hansen direction = std::get<std::string>(itr->second);
178*6272a393SAlexander Hansen }
179*6272a393SAlexander Hansen
180*6272a393SAlexander Hansen auto threshold = getThresholdType(direction, severity);
181*6272a393SAlexander Hansen thresholds[threshold] = value;
182*6272a393SAlexander Hansen
183*6272a393SAlexander Hansen auto hysteresis =
184*6272a393SAlexander Hansen getNumberFromConfig<double>(propertyMap, "Hysteresis", false);
185*6272a393SAlexander Hansen if (hysteresis != std::numeric_limits<double>::quiet_NaN())
186*6272a393SAlexander Hansen {
187*6272a393SAlexander Hansen thresholds[threshold + "Hysteresis"] = hysteresis;
188*6272a393SAlexander Hansen }
189*6272a393SAlexander Hansen
190*6272a393SAlexander Hansen if (!entityInterface.empty())
191*6272a393SAlexander Hansen {
192*6272a393SAlexander Hansen thresholds[threshold + "Direction"] = entityInterface;
193*6272a393SAlexander Hansen }
194*6272a393SAlexander Hansen }
195*6272a393SAlexander Hansen
parseConfigInterface(const PropertyMap & propertyMap,const std::string & sensorType,const std::string & interface)196*6272a393SAlexander Hansen void VirtualSensor::parseConfigInterface(const PropertyMap& propertyMap,
197*6272a393SAlexander Hansen const std::string& sensorType,
198*6272a393SAlexander Hansen const std::string& interface)
199*6272a393SAlexander Hansen {
200*6272a393SAlexander Hansen /* Parse sensors / DBus params */
201*6272a393SAlexander Hansen if (auto itr = propertyMap.find("Sensors"); itr != propertyMap.end())
202*6272a393SAlexander Hansen {
203*6272a393SAlexander Hansen auto sensors = std::get<std::vector<std::string>>(itr->second);
204*6272a393SAlexander Hansen for (auto sensor : sensors)
205*6272a393SAlexander Hansen {
206*6272a393SAlexander Hansen std::replace(sensor.begin(), sensor.end(), ' ', '_');
207*6272a393SAlexander Hansen auto sensorObjPath = sensorDbusPath + sensorType + "/" + sensor;
208*6272a393SAlexander Hansen
209*6272a393SAlexander Hansen auto paramPtr =
210*6272a393SAlexander Hansen std::make_unique<SensorParam>(bus, sensorObjPath, *this);
211*6272a393SAlexander Hansen symbols.create_variable(sensor);
212*6272a393SAlexander Hansen paramMap.emplace(std::move(sensor), std::move(paramPtr));
213*6272a393SAlexander Hansen }
214*6272a393SAlexander Hansen }
215*6272a393SAlexander Hansen /* Get expression string */
216*6272a393SAlexander Hansen if (!calculationIfaces.contains(interface))
217*6272a393SAlexander Hansen {
218*6272a393SAlexander Hansen throw std::invalid_argument("Invalid expression in interface");
219*6272a393SAlexander Hansen }
220*6272a393SAlexander Hansen exprStr = interface;
221*6272a393SAlexander Hansen
222*6272a393SAlexander Hansen /* Get optional min and max input and output values */
223*6272a393SAlexander Hansen ValueIface::maxValue(
224*6272a393SAlexander Hansen getNumberFromConfig<double>(propertyMap, "MaxValue", false));
225*6272a393SAlexander Hansen ValueIface::minValue(
226*6272a393SAlexander Hansen getNumberFromConfig<double>(propertyMap, "MinValue", false));
227*6272a393SAlexander Hansen maxValidInput =
228*6272a393SAlexander Hansen getNumberFromConfig<double>(propertyMap, "MaxValidInput", false,
229*6272a393SAlexander Hansen std::numeric_limits<double>::infinity());
230*6272a393SAlexander Hansen minValidInput =
231*6272a393SAlexander Hansen getNumberFromConfig<double>(propertyMap, "MinValidInput", false,
232*6272a393SAlexander Hansen -std::numeric_limits<double>::infinity());
233*6272a393SAlexander Hansen }
234*6272a393SAlexander Hansen
initVirtualSensor(const Json & sensorConfig,const std::string & objPath,const std::string & type)235*6272a393SAlexander Hansen void VirtualSensor::initVirtualSensor(const Json& sensorConfig,
236*6272a393SAlexander Hansen const std::string& objPath,
237*6272a393SAlexander Hansen const std::string& type)
238*6272a393SAlexander Hansen {
239*6272a393SAlexander Hansen static const Json empty{};
240*6272a393SAlexander Hansen
241*6272a393SAlexander Hansen units = unitMap.at(type);
242*6272a393SAlexander Hansen
243*6272a393SAlexander Hansen /* Get threshold values if defined in config */
244*6272a393SAlexander Hansen auto threshold = sensorConfig.value("Threshold", empty);
245*6272a393SAlexander Hansen
246*6272a393SAlexander Hansen createThresholds(threshold, objPath, units);
247*6272a393SAlexander Hansen
248*6272a393SAlexander Hansen /* Get MaxValue, MinValue setting if defined in config */
249*6272a393SAlexander Hansen auto confDesc = sensorConfig.value("Desc", empty);
250*6272a393SAlexander Hansen if (auto maxConf = confDesc.find("MaxValue");
251*6272a393SAlexander Hansen maxConf != confDesc.end() && maxConf->is_number())
252*6272a393SAlexander Hansen {
253*6272a393SAlexander Hansen ValueIface::maxValue(maxConf->get<double>());
254*6272a393SAlexander Hansen }
255*6272a393SAlexander Hansen if (auto minConf = confDesc.find("MinValue");
256*6272a393SAlexander Hansen minConf != confDesc.end() && minConf->is_number())
257*6272a393SAlexander Hansen {
258*6272a393SAlexander Hansen ValueIface::minValue(minConf->get<double>());
259*6272a393SAlexander Hansen }
260*6272a393SAlexander Hansen
261*6272a393SAlexander Hansen /* Get optional association */
262*6272a393SAlexander Hansen auto assocJson = sensorConfig.value("Associations", empty);
263*6272a393SAlexander Hansen if (!assocJson.empty())
264*6272a393SAlexander Hansen {
265*6272a393SAlexander Hansen auto assocs = getAssociationsFromJson(assocJson);
266*6272a393SAlexander Hansen if (!assocs.empty())
267*6272a393SAlexander Hansen {
268*6272a393SAlexander Hansen associationIface =
269*6272a393SAlexander Hansen std::make_unique<AssociationObject>(bus, objPath.c_str());
270*6272a393SAlexander Hansen associationIface->associations(assocs);
271*6272a393SAlexander Hansen }
272*6272a393SAlexander Hansen }
273*6272a393SAlexander Hansen
274*6272a393SAlexander Hansen /* Get expression string */
275*6272a393SAlexander Hansen static constexpr auto exprKey = "Expression";
276*6272a393SAlexander Hansen if (sensorConfig.contains(exprKey))
277*6272a393SAlexander Hansen {
278*6272a393SAlexander Hansen auto& ref = sensorConfig.at(exprKey);
279*6272a393SAlexander Hansen if (ref.is_array())
280*6272a393SAlexander Hansen {
281*6272a393SAlexander Hansen exprStr = std::string{};
282*6272a393SAlexander Hansen for (auto& s : ref)
283*6272a393SAlexander Hansen {
284*6272a393SAlexander Hansen exprStr += s;
285*6272a393SAlexander Hansen }
286*6272a393SAlexander Hansen }
287*6272a393SAlexander Hansen else if (ref.is_string())
288*6272a393SAlexander Hansen {
289*6272a393SAlexander Hansen exprStr = std::string{ref};
290*6272a393SAlexander Hansen }
291*6272a393SAlexander Hansen }
292*6272a393SAlexander Hansen
293*6272a393SAlexander Hansen /* Get all the parameter listed in configuration */
294*6272a393SAlexander Hansen auto params = sensorConfig.value("Params", empty);
295*6272a393SAlexander Hansen
296*6272a393SAlexander Hansen /* Check for constant parameter */
297*6272a393SAlexander Hansen const auto& consParams = params.value("ConstParam", empty);
298*6272a393SAlexander Hansen if (!consParams.empty())
299*6272a393SAlexander Hansen {
300*6272a393SAlexander Hansen for (auto& j : consParams)
301*6272a393SAlexander Hansen {
302*6272a393SAlexander Hansen if (j.find("ParamName") != j.end())
303*6272a393SAlexander Hansen {
304*6272a393SAlexander Hansen auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
305*6272a393SAlexander Hansen std::string name = j["ParamName"];
306*6272a393SAlexander Hansen symbols.create_variable(name);
307*6272a393SAlexander Hansen paramMap.emplace(std::move(name), std::move(paramPtr));
308*6272a393SAlexander Hansen }
309*6272a393SAlexander Hansen else
310*6272a393SAlexander Hansen {
311*6272a393SAlexander Hansen /* Invalid configuration */
312*6272a393SAlexander Hansen throw std::invalid_argument(
313*6272a393SAlexander Hansen "ParamName not found in configuration");
314*6272a393SAlexander Hansen }
315*6272a393SAlexander Hansen }
316*6272a393SAlexander Hansen }
317*6272a393SAlexander Hansen
318*6272a393SAlexander Hansen /* Check for dbus parameter */
319*6272a393SAlexander Hansen auto dbusParams = params.value("DbusParam", empty);
320*6272a393SAlexander Hansen if (!dbusParams.empty())
321*6272a393SAlexander Hansen {
322*6272a393SAlexander Hansen for (auto& j : dbusParams)
323*6272a393SAlexander Hansen {
324*6272a393SAlexander Hansen /* Get parameter dbus sensor descriptor */
325*6272a393SAlexander Hansen auto desc = j.value("Desc", empty);
326*6272a393SAlexander Hansen if ((!desc.empty()) && (j.find("ParamName") != j.end()))
327*6272a393SAlexander Hansen {
328*6272a393SAlexander Hansen std::string sensorType = desc.value("SensorType", "");
329*6272a393SAlexander Hansen std::string name = desc.value("Name", "");
330*6272a393SAlexander Hansen
331*6272a393SAlexander Hansen if (!sensorType.empty() && !name.empty())
332*6272a393SAlexander Hansen {
333*6272a393SAlexander Hansen auto path = sensorDbusPath + sensorType + "/" + name;
334*6272a393SAlexander Hansen auto paramPtr =
335*6272a393SAlexander Hansen std::make_unique<SensorParam>(bus, path, *this);
336*6272a393SAlexander Hansen std::string paramName = j["ParamName"];
337*6272a393SAlexander Hansen symbols.create_variable(paramName);
338*6272a393SAlexander Hansen paramMap.emplace(std::move(paramName), std::move(paramPtr));
339*6272a393SAlexander Hansen }
340*6272a393SAlexander Hansen }
341*6272a393SAlexander Hansen }
342*6272a393SAlexander Hansen }
343*6272a393SAlexander Hansen
344*6272a393SAlexander Hansen symbols.add_constants();
345*6272a393SAlexander Hansen symbols.add_package(vecopsPackage);
346*6272a393SAlexander Hansen symbols.add_function("maxIgnoreNaN", funcMaxIgnoreNaN);
347*6272a393SAlexander Hansen symbols.add_function("sumIgnoreNaN", funcSumIgnoreNaN);
348*6272a393SAlexander Hansen symbols.add_function("ifNan", funcIfNan);
349*6272a393SAlexander Hansen
350*6272a393SAlexander Hansen expression.register_symbol_table(symbols);
351*6272a393SAlexander Hansen
352*6272a393SAlexander Hansen /* parser from exprtk */
353*6272a393SAlexander Hansen exprtk::parser<double> parser{};
354*6272a393SAlexander Hansen if (!parser.compile(exprStr, expression))
355*6272a393SAlexander Hansen {
356*6272a393SAlexander Hansen error("Expression compilation failed");
357*6272a393SAlexander Hansen
358*6272a393SAlexander Hansen for (std::size_t i = 0; i < parser.error_count(); ++i)
359*6272a393SAlexander Hansen {
360*6272a393SAlexander Hansen auto err = parser.get_error(i);
361*6272a393SAlexander Hansen error("Error parsing token at {POSITION}: {ERROR}", "POSITION",
362*6272a393SAlexander Hansen err.token.position, "TYPE",
363*6272a393SAlexander Hansen exprtk::parser_error::to_str(err.mode), "ERROR",
364*6272a393SAlexander Hansen err.diagnostic);
365*6272a393SAlexander Hansen }
366*6272a393SAlexander Hansen throw std::runtime_error("Expression compilation failed");
367*6272a393SAlexander Hansen }
368*6272a393SAlexander Hansen
369*6272a393SAlexander Hansen /* Print all parameters for debug purpose only */
370*6272a393SAlexander Hansen printParams(paramMap);
371*6272a393SAlexander Hansen }
372*6272a393SAlexander Hansen
createAssociation(const std::string & objPath,const std::string & entityPath)373*6272a393SAlexander Hansen void VirtualSensor::createAssociation(const std::string& objPath,
374*6272a393SAlexander Hansen const std::string& entityPath)
375*6272a393SAlexander Hansen {
376*6272a393SAlexander Hansen if (objPath.empty() || entityPath.empty())
377*6272a393SAlexander Hansen {
378*6272a393SAlexander Hansen return;
379*6272a393SAlexander Hansen }
380*6272a393SAlexander Hansen
381*6272a393SAlexander Hansen std::filesystem::path p(entityPath);
382*6272a393SAlexander Hansen auto assocsDbus =
383*6272a393SAlexander Hansen AssociationList{{"chassis", "all_sensors", p.parent_path().string()}};
384*6272a393SAlexander Hansen associationIface =
385*6272a393SAlexander Hansen std::make_unique<AssociationObject>(bus, objPath.c_str());
386*6272a393SAlexander Hansen associationIface->associations(assocsDbus);
387*6272a393SAlexander Hansen }
388*6272a393SAlexander Hansen
initVirtualSensor(const InterfaceMap & interfaceMap,const std::string & objPath,const std::string & sensorType,const std::string & calculationIface)389*6272a393SAlexander Hansen void VirtualSensor::initVirtualSensor(
390*6272a393SAlexander Hansen const InterfaceMap& interfaceMap, const std::string& objPath,
391*6272a393SAlexander Hansen const std::string& sensorType, const std::string& calculationIface)
392*6272a393SAlexander Hansen {
393*6272a393SAlexander Hansen Json thresholds;
394*6272a393SAlexander Hansen const std::string vsThresholdsIntf =
395*6272a393SAlexander Hansen calculationIface + vsThresholdsIfaceSuffix;
396*6272a393SAlexander Hansen
397*6272a393SAlexander Hansen units = unitMap.at(sensorType);
398*6272a393SAlexander Hansen
399*6272a393SAlexander Hansen for (const auto& [interface, propertyMap] : interfaceMap)
400*6272a393SAlexander Hansen {
401*6272a393SAlexander Hansen /* Each threshold is on it's own interface with a number as a suffix
402*6272a393SAlexander Hansen * eg xyz.openbmc_project.Configuration.ModifiedMedian.Thresholds1 */
403*6272a393SAlexander Hansen if (interface.find(vsThresholdsIntf) != std::string::npos)
404*6272a393SAlexander Hansen {
405*6272a393SAlexander Hansen parseThresholds(thresholds, propertyMap, interface);
406*6272a393SAlexander Hansen }
407*6272a393SAlexander Hansen else if (interface == calculationIface)
408*6272a393SAlexander Hansen {
409*6272a393SAlexander Hansen parseConfigInterface(propertyMap, sensorType, interface);
410*6272a393SAlexander Hansen }
411*6272a393SAlexander Hansen }
412*6272a393SAlexander Hansen
413*6272a393SAlexander Hansen createThresholds(thresholds, objPath, units);
414*6272a393SAlexander Hansen symbols.add_constants();
415*6272a393SAlexander Hansen symbols.add_package(vecopsPackage);
416*6272a393SAlexander Hansen expression.register_symbol_table(symbols);
417*6272a393SAlexander Hansen
418*6272a393SAlexander Hansen createAssociation(objPath, entityPath);
419*6272a393SAlexander Hansen /* Print all parameters for debug purpose only */
420*6272a393SAlexander Hansen printParams(paramMap);
421*6272a393SAlexander Hansen }
422*6272a393SAlexander Hansen
setSensorValue(double value)423*6272a393SAlexander Hansen void VirtualSensor::setSensorValue(double value)
424*6272a393SAlexander Hansen {
425*6272a393SAlexander Hansen value = std::clamp(value, ValueIface::minValue(), ValueIface::maxValue());
426*6272a393SAlexander Hansen ValueIface::value(value);
427*6272a393SAlexander Hansen }
428*6272a393SAlexander Hansen
calculateValue(const std::string & calculation,const VirtualSensor::ParamMap & paramMap)429*6272a393SAlexander Hansen double VirtualSensor::calculateValue(const std::string& calculation,
430*6272a393SAlexander Hansen const VirtualSensor::ParamMap& paramMap)
431*6272a393SAlexander Hansen {
432*6272a393SAlexander Hansen auto iter = calculationIfaces.find(calculation);
433*6272a393SAlexander Hansen if (iter == calculationIfaces.end())
434*6272a393SAlexander Hansen {
435*6272a393SAlexander Hansen return std::numeric_limits<double>::quiet_NaN();
436*6272a393SAlexander Hansen }
437*6272a393SAlexander Hansen
438*6272a393SAlexander Hansen std::vector<double> values;
439*6272a393SAlexander Hansen for (auto& param : paramMap)
440*6272a393SAlexander Hansen {
441*6272a393SAlexander Hansen auto& name = param.first;
442*6272a393SAlexander Hansen if (auto var = symbols.get_variable(name))
443*6272a393SAlexander Hansen {
444*6272a393SAlexander Hansen if (!sensorInRange(var->ref()))
445*6272a393SAlexander Hansen {
446*6272a393SAlexander Hansen continue;
447*6272a393SAlexander Hansen }
448*6272a393SAlexander Hansen values.push_back(var->ref());
449*6272a393SAlexander Hansen }
450*6272a393SAlexander Hansen }
451*6272a393SAlexander Hansen
452*6272a393SAlexander Hansen return iter->second(values);
453*6272a393SAlexander Hansen }
454*6272a393SAlexander Hansen
sensorInRange(double value)455*6272a393SAlexander Hansen bool VirtualSensor::sensorInRange(double value)
456*6272a393SAlexander Hansen {
457*6272a393SAlexander Hansen if (value <= this->maxValidInput && value >= this->minValidInput)
458*6272a393SAlexander Hansen {
459*6272a393SAlexander Hansen return true;
460*6272a393SAlexander Hansen }
461*6272a393SAlexander Hansen return false;
462*6272a393SAlexander Hansen }
463*6272a393SAlexander Hansen
updateVirtualSensor()464*6272a393SAlexander Hansen void VirtualSensor::updateVirtualSensor()
465*6272a393SAlexander Hansen {
466*6272a393SAlexander Hansen for (auto& param : paramMap)
467*6272a393SAlexander Hansen {
468*6272a393SAlexander Hansen auto& name = param.first;
469*6272a393SAlexander Hansen auto& data = param.second;
470*6272a393SAlexander Hansen if (auto var = symbols.get_variable(name))
471*6272a393SAlexander Hansen {
472*6272a393SAlexander Hansen var->ref() = data->getParamValue();
473*6272a393SAlexander Hansen }
474*6272a393SAlexander Hansen else
475*6272a393SAlexander Hansen {
476*6272a393SAlexander Hansen /* Invalid parameter */
477*6272a393SAlexander Hansen throw std::invalid_argument("ParamName not found in symbols");
478*6272a393SAlexander Hansen }
479*6272a393SAlexander Hansen }
480*6272a393SAlexander Hansen auto val = (!calculationIfaces.contains(exprStr))
481*6272a393SAlexander Hansen ? expression.value()
482*6272a393SAlexander Hansen : calculateValue(exprStr, paramMap);
483*6272a393SAlexander Hansen
484*6272a393SAlexander Hansen /* Set sensor value to dbus interface */
485*6272a393SAlexander Hansen setSensorValue(val);
486*6272a393SAlexander Hansen debug("Sensor {NAME} = {VALUE}", "NAME", this->name, "VALUE", val);
487*6272a393SAlexander Hansen
488*6272a393SAlexander Hansen /* Check sensor thresholds and log required message */
489*6272a393SAlexander Hansen auto changed = false;
490*6272a393SAlexander Hansen auto normal = checkThresholds(val, perfLossIface, changed);
491*6272a393SAlexander Hansen normal &= checkThresholds(val, warningIface, changed);
492*6272a393SAlexander Hansen normal &= checkThresholds(val, criticalIface, changed);
493*6272a393SAlexander Hansen normal &= checkThresholds(val, softShutdownIface, changed);
494*6272a393SAlexander Hansen normal &= checkThresholds(val, hardShutdownIface, changed);
495*6272a393SAlexander Hansen if (changed && normal)
496*6272a393SAlexander Hansen {
497*6272a393SAlexander Hansen namespace Events =
498*6272a393SAlexander Hansen sdbusplus::event::xyz::openbmc_project::sensor::Threshold;
499*6272a393SAlexander Hansen
500*6272a393SAlexander Hansen try
501*6272a393SAlexander Hansen {
502*6272a393SAlexander Hansen lg2::commit(Events::SensorReadingNormalRange(
503*6272a393SAlexander Hansen "SENSOR_NAME", objPath, "READING_VALUE", val, "UNITS", units));
504*6272a393SAlexander Hansen }
505*6272a393SAlexander Hansen catch (std::exception&)
506*6272a393SAlexander Hansen {
507*6272a393SAlexander Hansen lg2::debug("Failed to create normal range event {NAME}", "NAME",
508*6272a393SAlexander Hansen objPath);
509*6272a393SAlexander Hansen }
510*6272a393SAlexander Hansen }
511*6272a393SAlexander Hansen }
512*6272a393SAlexander Hansen
createThresholds(const Json & threshold,const std::string & objPath,ValueIface::Unit units)513*6272a393SAlexander Hansen void VirtualSensor::createThresholds(
514*6272a393SAlexander Hansen const Json& threshold, const std::string& objPath, ValueIface::Unit units)
515*6272a393SAlexander Hansen {
516*6272a393SAlexander Hansen if (threshold.empty())
517*6272a393SAlexander Hansen {
518*6272a393SAlexander Hansen return;
519*6272a393SAlexander Hansen }
520*6272a393SAlexander Hansen // Only create the threshold interfaces if
521*6272a393SAlexander Hansen // at least one of their values is present.
522*6272a393SAlexander Hansen if (threshold.contains("CriticalHigh") || threshold.contains("CriticalLow"))
523*6272a393SAlexander Hansen {
524*6272a393SAlexander Hansen criticalIface = std::make_unique<Threshold<CriticalObject>>(
525*6272a393SAlexander Hansen bus, objPath.c_str(), units);
526*6272a393SAlexander Hansen
527*6272a393SAlexander Hansen if (threshold.contains("CriticalHigh"))
528*6272a393SAlexander Hansen {
529*6272a393SAlexander Hansen criticalIface->setEntityInterfaceHigh(
530*6272a393SAlexander Hansen threshold.value("CriticalHighDirection", ""));
531*6272a393SAlexander Hansen debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
532*6272a393SAlexander Hansen "INTF", threshold.value("CriticalHighDirection", ""));
533*6272a393SAlexander Hansen }
534*6272a393SAlexander Hansen if (threshold.contains("CriticalLow"))
535*6272a393SAlexander Hansen {
536*6272a393SAlexander Hansen criticalIface->setEntityInterfaceLow(
537*6272a393SAlexander Hansen threshold.value("CriticalLowDirection", ""));
538*6272a393SAlexander Hansen debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
539*6272a393SAlexander Hansen "INTF", threshold.value("CriticalLowDirection", ""));
540*6272a393SAlexander Hansen }
541*6272a393SAlexander Hansen
542*6272a393SAlexander Hansen criticalIface->setEntityPath(entityPath);
543*6272a393SAlexander Hansen debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath, "PATH",
544*6272a393SAlexander Hansen entityPath);
545*6272a393SAlexander Hansen
546*6272a393SAlexander Hansen criticalIface->criticalHigh(threshold.value(
547*6272a393SAlexander Hansen "CriticalHigh", std::numeric_limits<double>::quiet_NaN()));
548*6272a393SAlexander Hansen criticalIface->criticalLow(threshold.value(
549*6272a393SAlexander Hansen "CriticalLow", std::numeric_limits<double>::quiet_NaN()));
550*6272a393SAlexander Hansen criticalIface->setHighHysteresis(
551*6272a393SAlexander Hansen threshold.value("CriticalHighHysteresis", defaultHysteresis));
552*6272a393SAlexander Hansen criticalIface->setLowHysteresis(
553*6272a393SAlexander Hansen threshold.value("CriticalLowHysteresis", defaultHysteresis));
554*6272a393SAlexander Hansen }
555*6272a393SAlexander Hansen
556*6272a393SAlexander Hansen if (threshold.contains("WarningHigh") || threshold.contains("WarningLow"))
557*6272a393SAlexander Hansen {
558*6272a393SAlexander Hansen warningIface = std::make_unique<Threshold<WarningObject>>(
559*6272a393SAlexander Hansen bus, objPath.c_str(), units);
560*6272a393SAlexander Hansen
561*6272a393SAlexander Hansen if (threshold.contains("WarningHigh"))
562*6272a393SAlexander Hansen {
563*6272a393SAlexander Hansen warningIface->setEntityInterfaceHigh(
564*6272a393SAlexander Hansen threshold.value("WarningHighDirection", ""));
565*6272a393SAlexander Hansen debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
566*6272a393SAlexander Hansen "INTF", threshold.value("WarningHighDirection", ""));
567*6272a393SAlexander Hansen }
568*6272a393SAlexander Hansen if (threshold.contains("WarningLow"))
569*6272a393SAlexander Hansen {
570*6272a393SAlexander Hansen warningIface->setEntityInterfaceLow(
571*6272a393SAlexander Hansen threshold.value("WarningLowDirection", ""));
572*6272a393SAlexander Hansen debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath,
573*6272a393SAlexander Hansen "INTF", threshold.value("WarningLowDirection", ""));
574*6272a393SAlexander Hansen }
575*6272a393SAlexander Hansen
576*6272a393SAlexander Hansen warningIface->setEntityPath(entityPath);
577*6272a393SAlexander Hansen debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath, "PATH",
578*6272a393SAlexander Hansen entityPath);
579*6272a393SAlexander Hansen
580*6272a393SAlexander Hansen warningIface->warningHigh(threshold.value(
581*6272a393SAlexander Hansen "WarningHigh", std::numeric_limits<double>::quiet_NaN()));
582*6272a393SAlexander Hansen warningIface->warningLow(threshold.value(
583*6272a393SAlexander Hansen "WarningLow", std::numeric_limits<double>::quiet_NaN()));
584*6272a393SAlexander Hansen warningIface->setHighHysteresis(
585*6272a393SAlexander Hansen threshold.value("WarningHighHysteresis", defaultHysteresis));
586*6272a393SAlexander Hansen warningIface->setLowHysteresis(
587*6272a393SAlexander Hansen threshold.value("WarningLowHysteresis", defaultHysteresis));
588*6272a393SAlexander Hansen }
589*6272a393SAlexander Hansen
590*6272a393SAlexander Hansen if (threshold.contains("HardShutdownHigh") ||
591*6272a393SAlexander Hansen threshold.contains("HardShutdownLow"))
592*6272a393SAlexander Hansen {
593*6272a393SAlexander Hansen hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>(
594*6272a393SAlexander Hansen bus, objPath.c_str(), units);
595*6272a393SAlexander Hansen
596*6272a393SAlexander Hansen hardShutdownIface->hardShutdownHigh(threshold.value(
597*6272a393SAlexander Hansen "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
598*6272a393SAlexander Hansen hardShutdownIface->hardShutdownLow(threshold.value(
599*6272a393SAlexander Hansen "HardShutdownLow", std::numeric_limits<double>::quiet_NaN()));
600*6272a393SAlexander Hansen hardShutdownIface->setHighHysteresis(
601*6272a393SAlexander Hansen threshold.value("HardShutdownHighHysteresis", defaultHysteresis));
602*6272a393SAlexander Hansen hardShutdownIface->setLowHysteresis(
603*6272a393SAlexander Hansen threshold.value("HardShutdownLowHysteresis", defaultHysteresis));
604*6272a393SAlexander Hansen }
605*6272a393SAlexander Hansen
606*6272a393SAlexander Hansen if (threshold.contains("SoftShutdownHigh") ||
607*6272a393SAlexander Hansen threshold.contains("SoftShutdownLow"))
608*6272a393SAlexander Hansen {
609*6272a393SAlexander Hansen softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>(
610*6272a393SAlexander Hansen bus, objPath.c_str(), units);
611*6272a393SAlexander Hansen
612*6272a393SAlexander Hansen softShutdownIface->softShutdownHigh(threshold.value(
613*6272a393SAlexander Hansen "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN()));
614*6272a393SAlexander Hansen softShutdownIface->softShutdownLow(threshold.value(
615*6272a393SAlexander Hansen "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN()));
616*6272a393SAlexander Hansen softShutdownIface->setHighHysteresis(
617*6272a393SAlexander Hansen threshold.value("SoftShutdownHighHysteresis", defaultHysteresis));
618*6272a393SAlexander Hansen softShutdownIface->setLowHysteresis(
619*6272a393SAlexander Hansen threshold.value("SoftShutdownLowHysteresis", defaultHysteresis));
620*6272a393SAlexander Hansen }
621*6272a393SAlexander Hansen
622*6272a393SAlexander Hansen if (threshold.contains("PerformanceLossHigh") ||
623*6272a393SAlexander Hansen threshold.contains("PerformanceLossLow"))
624*6272a393SAlexander Hansen {
625*6272a393SAlexander Hansen perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>(
626*6272a393SAlexander Hansen bus, objPath.c_str(), units);
627*6272a393SAlexander Hansen
628*6272a393SAlexander Hansen perfLossIface->performanceLossHigh(threshold.value(
629*6272a393SAlexander Hansen "PerformanceLossHigh", std::numeric_limits<double>::quiet_NaN()));
630*6272a393SAlexander Hansen perfLossIface->performanceLossLow(threshold.value(
631*6272a393SAlexander Hansen "PerformanceLossLow", std::numeric_limits<double>::quiet_NaN()));
632*6272a393SAlexander Hansen perfLossIface->setHighHysteresis(threshold.value(
633*6272a393SAlexander Hansen "PerformanceLossHighHysteresis", defaultHysteresis));
634*6272a393SAlexander Hansen perfLossIface->setLowHysteresis(
635*6272a393SAlexander Hansen threshold.value("PerformanceLossLowHysteresis", defaultHysteresis));
636*6272a393SAlexander Hansen }
637*6272a393SAlexander Hansen }
638*6272a393SAlexander Hansen
getObjectsFromDBus()639*6272a393SAlexander Hansen ManagedObjectType VirtualSensors::getObjectsFromDBus()
640*6272a393SAlexander Hansen {
641*6272a393SAlexander Hansen ManagedObjectType objects;
642*6272a393SAlexander Hansen
643*6272a393SAlexander Hansen try
644*6272a393SAlexander Hansen {
645*6272a393SAlexander Hansen auto method = bus.new_method_call(
646*6272a393SAlexander Hansen "xyz.openbmc_project.EntityManager",
647*6272a393SAlexander Hansen "/xyz/openbmc_project/inventory",
648*6272a393SAlexander Hansen "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
649*6272a393SAlexander Hansen auto reply = bus.call(method);
650*6272a393SAlexander Hansen reply.read(objects);
651*6272a393SAlexander Hansen }
652*6272a393SAlexander Hansen catch (const sdbusplus::exception_t& ex)
653*6272a393SAlexander Hansen {
654*6272a393SAlexander Hansen // If entity manager isn't running yet, keep going.
655*6272a393SAlexander Hansen if (std::string("org.freedesktop.DBus.Error.ServiceUnknown") !=
656*6272a393SAlexander Hansen ex.name())
657*6272a393SAlexander Hansen {
658*6272a393SAlexander Hansen error("Could not reach entity-manager: {ERROR}", "ERROR", ex);
659*6272a393SAlexander Hansen throw;
660*6272a393SAlexander Hansen }
661*6272a393SAlexander Hansen }
662*6272a393SAlexander Hansen
663*6272a393SAlexander Hansen return objects;
664*6272a393SAlexander Hansen }
665*6272a393SAlexander Hansen
propertiesChanged(sdbusplus::message_t & msg)666*6272a393SAlexander Hansen void VirtualSensors::propertiesChanged(sdbusplus::message_t& msg)
667*6272a393SAlexander Hansen {
668*6272a393SAlexander Hansen std::string interface;
669*6272a393SAlexander Hansen PropertyMap properties;
670*6272a393SAlexander Hansen
671*6272a393SAlexander Hansen msg.read(interface, properties);
672*6272a393SAlexander Hansen
673*6272a393SAlexander Hansen /* We get multiple callbacks for one sensor. 'Type' is a required field and
674*6272a393SAlexander Hansen * is a unique label so use to to only proceed once per sensor */
675*6272a393SAlexander Hansen if (properties.contains("Type"))
676*6272a393SAlexander Hansen {
677*6272a393SAlexander Hansen if (calculationIfaces.contains(interface))
678*6272a393SAlexander Hansen {
679*6272a393SAlexander Hansen createVirtualSensorsFromDBus(interface);
680*6272a393SAlexander Hansen }
681*6272a393SAlexander Hansen }
682*6272a393SAlexander Hansen }
683*6272a393SAlexander Hansen
684*6272a393SAlexander Hansen /** @brief Parsing Virtual Sensor config JSON file */
parseConfigFile()685*6272a393SAlexander Hansen Json VirtualSensors::parseConfigFile()
686*6272a393SAlexander Hansen {
687*6272a393SAlexander Hansen using path = std::filesystem::path;
688*6272a393SAlexander Hansen auto configFile = []() -> path {
689*6272a393SAlexander Hansen static constexpr auto name = "virtual_sensor_config.json";
690*6272a393SAlexander Hansen
691*6272a393SAlexander Hansen for (auto pathSeg : {std::filesystem::current_path(),
692*6272a393SAlexander Hansen path{"/var/lib/phosphor-virtual-sensor"},
693*6272a393SAlexander Hansen path{"/usr/share/phosphor-virtual-sensor"}})
694*6272a393SAlexander Hansen {
695*6272a393SAlexander Hansen auto file = pathSeg / name;
696*6272a393SAlexander Hansen if (std::filesystem::exists(file))
697*6272a393SAlexander Hansen {
698*6272a393SAlexander Hansen return file;
699*6272a393SAlexander Hansen }
700*6272a393SAlexander Hansen }
701*6272a393SAlexander Hansen return name;
702*6272a393SAlexander Hansen }();
703*6272a393SAlexander Hansen
704*6272a393SAlexander Hansen std::ifstream jsonFile(configFile);
705*6272a393SAlexander Hansen if (!jsonFile.is_open())
706*6272a393SAlexander Hansen {
707*6272a393SAlexander Hansen error("config JSON file {FILENAME} not found", "FILENAME", configFile);
708*6272a393SAlexander Hansen return {};
709*6272a393SAlexander Hansen }
710*6272a393SAlexander Hansen
711*6272a393SAlexander Hansen auto data = Json::parse(jsonFile, nullptr, false);
712*6272a393SAlexander Hansen if (data.is_discarded())
713*6272a393SAlexander Hansen {
714*6272a393SAlexander Hansen error("config readings JSON parser failure with {FILENAME}", "FILENAME",
715*6272a393SAlexander Hansen configFile);
716*6272a393SAlexander Hansen throw std::exception{};
717*6272a393SAlexander Hansen }
718*6272a393SAlexander Hansen
719*6272a393SAlexander Hansen return data;
720*6272a393SAlexander Hansen }
721*6272a393SAlexander Hansen
getSensorTypeFromUnit(const std::string & unit)722*6272a393SAlexander Hansen const std::string getSensorTypeFromUnit(const std::string& unit)
723*6272a393SAlexander Hansen {
724*6272a393SAlexander Hansen std::string unitPrefix = "xyz.openbmc_project.Sensor.Value.Unit.";
725*6272a393SAlexander Hansen for (auto [type, unitObj] : unitMap)
726*6272a393SAlexander Hansen {
727*6272a393SAlexander Hansen auto unitPath = ValueIface::convertUnitToString(unitObj);
728*6272a393SAlexander Hansen if (unitPath == (unitPrefix + unit))
729*6272a393SAlexander Hansen {
730*6272a393SAlexander Hansen return type;
731*6272a393SAlexander Hansen }
732*6272a393SAlexander Hansen }
733*6272a393SAlexander Hansen return "";
734*6272a393SAlexander Hansen }
735*6272a393SAlexander Hansen
setupMatches()736*6272a393SAlexander Hansen void VirtualSensors::setupMatches()
737*6272a393SAlexander Hansen {
738*6272a393SAlexander Hansen /* Already setup */
739*6272a393SAlexander Hansen if (!this->matches.empty())
740*6272a393SAlexander Hansen {
741*6272a393SAlexander Hansen return;
742*6272a393SAlexander Hansen }
743*6272a393SAlexander Hansen
744*6272a393SAlexander Hansen /* Setup matches */
745*6272a393SAlexander Hansen auto eventHandler = [this](sdbusplus::message_t& message) {
746*6272a393SAlexander Hansen if (message.is_method_error())
747*6272a393SAlexander Hansen {
748*6272a393SAlexander Hansen error("Callback method error");
749*6272a393SAlexander Hansen return;
750*6272a393SAlexander Hansen }
751*6272a393SAlexander Hansen this->propertiesChanged(message);
752*6272a393SAlexander Hansen };
753*6272a393SAlexander Hansen
754*6272a393SAlexander Hansen for (const auto& [iface, _] : calculationIfaces)
755*6272a393SAlexander Hansen {
756*6272a393SAlexander Hansen auto match = std::make_unique<sdbusplus::bus::match_t>(
757*6272a393SAlexander Hansen bus,
758*6272a393SAlexander Hansen sdbusplus::bus::match::rules::propertiesChangedNamespace(
759*6272a393SAlexander Hansen "/xyz/openbmc_project/inventory", iface),
760*6272a393SAlexander Hansen eventHandler);
761*6272a393SAlexander Hansen this->matches.emplace_back(std::move(match));
762*6272a393SAlexander Hansen }
763*6272a393SAlexander Hansen }
764*6272a393SAlexander Hansen
createVirtualSensorsFromDBus(const std::string & calculationIface)765*6272a393SAlexander Hansen void VirtualSensors::createVirtualSensorsFromDBus(
766*6272a393SAlexander Hansen const std::string& calculationIface)
767*6272a393SAlexander Hansen {
768*6272a393SAlexander Hansen if (calculationIface.empty())
769*6272a393SAlexander Hansen {
770*6272a393SAlexander Hansen error("No calculation type supplied");
771*6272a393SAlexander Hansen return;
772*6272a393SAlexander Hansen }
773*6272a393SAlexander Hansen auto objects = getObjectsFromDBus();
774*6272a393SAlexander Hansen
775*6272a393SAlexander Hansen /* Get virtual sensors config data */
776*6272a393SAlexander Hansen for (const auto& [path, interfaceMap] : objects)
777*6272a393SAlexander Hansen {
778*6272a393SAlexander Hansen /* Find Virtual Sensor interfaces */
779*6272a393SAlexander Hansen auto intfIter = interfaceMap.find(calculationIface);
780*6272a393SAlexander Hansen if (intfIter == interfaceMap.end())
781*6272a393SAlexander Hansen {
782*6272a393SAlexander Hansen continue;
783*6272a393SAlexander Hansen }
784*6272a393SAlexander Hansen
785*6272a393SAlexander Hansen std::string name = path.filename();
786*6272a393SAlexander Hansen if (name.empty())
787*6272a393SAlexander Hansen {
788*6272a393SAlexander Hansen error("Virtual Sensor name not found in entity manager config");
789*6272a393SAlexander Hansen continue;
790*6272a393SAlexander Hansen }
791*6272a393SAlexander Hansen if (virtualSensorsMap.contains(name))
792*6272a393SAlexander Hansen {
793*6272a393SAlexander Hansen error("A virtual sensor named {NAME} already exists", "NAME", name);
794*6272a393SAlexander Hansen continue;
795*6272a393SAlexander Hansen }
796*6272a393SAlexander Hansen
797*6272a393SAlexander Hansen /* Extract the virtual sensor type as we need this to initialize the
798*6272a393SAlexander Hansen * sensor */
799*6272a393SAlexander Hansen std::string sensorType, sensorUnit;
800*6272a393SAlexander Hansen auto propertyMap = intfIter->second;
801*6272a393SAlexander Hansen auto proIter = propertyMap.find("Units");
802*6272a393SAlexander Hansen if (proIter != propertyMap.end())
803*6272a393SAlexander Hansen {
804*6272a393SAlexander Hansen sensorUnit = std::get<std::string>(proIter->second);
805*6272a393SAlexander Hansen }
806*6272a393SAlexander Hansen sensorType = getSensorTypeFromUnit(sensorUnit);
807*6272a393SAlexander Hansen if (sensorType.empty())
808*6272a393SAlexander Hansen {
809*6272a393SAlexander Hansen error("Sensor unit type {TYPE} is not supported", "TYPE",
810*6272a393SAlexander Hansen sensorUnit);
811*6272a393SAlexander Hansen continue;
812*6272a393SAlexander Hansen }
813*6272a393SAlexander Hansen
814*6272a393SAlexander Hansen try
815*6272a393SAlexander Hansen {
816*6272a393SAlexander Hansen auto objpath = static_cast<std::string>(path);
817*6272a393SAlexander Hansen auto virtObjPath = sensorDbusPath + sensorType + "/" + name;
818*6272a393SAlexander Hansen
819*6272a393SAlexander Hansen auto virtualSensorPtr = std::make_unique<VirtualSensor>(
820*6272a393SAlexander Hansen bus, virtObjPath.c_str(), interfaceMap, name, sensorType,
821*6272a393SAlexander Hansen calculationIface, objpath);
822*6272a393SAlexander Hansen info("Added a new virtual sensor: {NAME} {TYPE}", "NAME", name,
823*6272a393SAlexander Hansen "TYPE", sensorType);
824*6272a393SAlexander Hansen virtualSensorPtr->updateVirtualSensor();
825*6272a393SAlexander Hansen
826*6272a393SAlexander Hansen /* Initialize unit value for virtual sensor */
827*6272a393SAlexander Hansen virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
828*6272a393SAlexander Hansen virtualSensorPtr->emit_object_added();
829*6272a393SAlexander Hansen
830*6272a393SAlexander Hansen virtualSensorsMap.emplace(name, std::move(virtualSensorPtr));
831*6272a393SAlexander Hansen
832*6272a393SAlexander Hansen /* Setup match for interfaces removed */
833*6272a393SAlexander Hansen auto intfRemoved = [this, objpath,
834*6272a393SAlexander Hansen name](sdbusplus::message_t& message) {
835*6272a393SAlexander Hansen if (!virtualSensorsMap.contains(name))
836*6272a393SAlexander Hansen {
837*6272a393SAlexander Hansen return;
838*6272a393SAlexander Hansen }
839*6272a393SAlexander Hansen sdbusplus::message::object_path path;
840*6272a393SAlexander Hansen message.read(path);
841*6272a393SAlexander Hansen if (static_cast<const std::string&>(path) == objpath)
842*6272a393SAlexander Hansen {
843*6272a393SAlexander Hansen info("Removed a virtual sensor: {NAME}", "NAME", name);
844*6272a393SAlexander Hansen virtualSensorsMap.erase(name);
845*6272a393SAlexander Hansen }
846*6272a393SAlexander Hansen };
847*6272a393SAlexander Hansen auto matchOnRemove = std::make_unique<sdbusplus::bus::match_t>(
848*6272a393SAlexander Hansen bus,
849*6272a393SAlexander Hansen sdbusplus::bus::match::rules::interfacesRemoved() +
850*6272a393SAlexander Hansen sdbusplus::bus::match::rules::argNpath(0, objpath),
851*6272a393SAlexander Hansen intfRemoved);
852*6272a393SAlexander Hansen /* TODO: slight race condition here. Check that the config still
853*6272a393SAlexander Hansen * exists */
854*6272a393SAlexander Hansen this->matches.emplace_back(std::move(matchOnRemove));
855*6272a393SAlexander Hansen }
856*6272a393SAlexander Hansen catch (const std::invalid_argument& ia)
857*6272a393SAlexander Hansen {
858*6272a393SAlexander Hansen error("Failed to set up virtual sensor: {ERROR}", "ERROR", ia);
859*6272a393SAlexander Hansen }
860*6272a393SAlexander Hansen }
861*6272a393SAlexander Hansen }
862*6272a393SAlexander Hansen
createVirtualSensors()863*6272a393SAlexander Hansen void VirtualSensors::createVirtualSensors()
864*6272a393SAlexander Hansen {
865*6272a393SAlexander Hansen static const Json empty{};
866*6272a393SAlexander Hansen
867*6272a393SAlexander Hansen auto data = parseConfigFile();
868*6272a393SAlexander Hansen
869*6272a393SAlexander Hansen // print values
870*6272a393SAlexander Hansen debug("JSON: {JSON}", "JSON", data.dump());
871*6272a393SAlexander Hansen
872*6272a393SAlexander Hansen /* Get virtual sensors config data */
873*6272a393SAlexander Hansen for (const auto& j : data)
874*6272a393SAlexander Hansen {
875*6272a393SAlexander Hansen auto desc = j.value("Desc", empty);
876*6272a393SAlexander Hansen if (!desc.empty())
877*6272a393SAlexander Hansen {
878*6272a393SAlexander Hansen if (desc.value("Config", "") == "D-Bus")
879*6272a393SAlexander Hansen {
880*6272a393SAlexander Hansen /* Look on D-Bus for a virtual sensor config. Set up matches
881*6272a393SAlexander Hansen * first because the configs may not be on D-Bus yet and we
882*6272a393SAlexander Hansen * don't want to miss them */
883*6272a393SAlexander Hansen setupMatches();
884*6272a393SAlexander Hansen
885*6272a393SAlexander Hansen for (const auto& intf : std::views::keys(calculationIfaces))
886*6272a393SAlexander Hansen {
887*6272a393SAlexander Hansen createVirtualSensorsFromDBus(intf);
888*6272a393SAlexander Hansen }
889*6272a393SAlexander Hansen continue;
890*6272a393SAlexander Hansen }
891*6272a393SAlexander Hansen
892*6272a393SAlexander Hansen std::string sensorType = desc.value("SensorType", "");
893*6272a393SAlexander Hansen std::string name = desc.value("Name", "");
894*6272a393SAlexander Hansen std::replace(name.begin(), name.end(), ' ', '_');
895*6272a393SAlexander Hansen
896*6272a393SAlexander Hansen if (!name.empty() && !sensorType.empty())
897*6272a393SAlexander Hansen {
898*6272a393SAlexander Hansen if (unitMap.find(sensorType) == unitMap.end())
899*6272a393SAlexander Hansen {
900*6272a393SAlexander Hansen error("Sensor type {TYPE} is not supported", "TYPE",
901*6272a393SAlexander Hansen sensorType);
902*6272a393SAlexander Hansen }
903*6272a393SAlexander Hansen else
904*6272a393SAlexander Hansen {
905*6272a393SAlexander Hansen if (virtualSensorsMap.find(name) != virtualSensorsMap.end())
906*6272a393SAlexander Hansen {
907*6272a393SAlexander Hansen error("A virtual sensor named {NAME} already exists",
908*6272a393SAlexander Hansen "NAME", name);
909*6272a393SAlexander Hansen continue;
910*6272a393SAlexander Hansen }
911*6272a393SAlexander Hansen auto objPath = sensorDbusPath + sensorType + "/" + name;
912*6272a393SAlexander Hansen
913*6272a393SAlexander Hansen auto virtualSensorPtr = std::make_unique<VirtualSensor>(
914*6272a393SAlexander Hansen bus, objPath.c_str(), j, name, sensorType);
915*6272a393SAlexander Hansen
916*6272a393SAlexander Hansen info("Added a new virtual sensor: {NAME}", "NAME", name);
917*6272a393SAlexander Hansen virtualSensorPtr->updateVirtualSensor();
918*6272a393SAlexander Hansen
919*6272a393SAlexander Hansen /* Initialize unit value for virtual sensor */
920*6272a393SAlexander Hansen virtualSensorPtr->ValueIface::unit(unitMap[sensorType]);
921*6272a393SAlexander Hansen virtualSensorPtr->emit_object_added();
922*6272a393SAlexander Hansen
923*6272a393SAlexander Hansen virtualSensorsMap.emplace(std::move(name),
924*6272a393SAlexander Hansen std::move(virtualSensorPtr));
925*6272a393SAlexander Hansen }
926*6272a393SAlexander Hansen }
927*6272a393SAlexander Hansen else
928*6272a393SAlexander Hansen {
929*6272a393SAlexander Hansen error(
930*6272a393SAlexander Hansen "Sensor type ({TYPE}) or name ({NAME}) not found in config file",
931*6272a393SAlexander Hansen "NAME", name, "TYPE", sensorType);
932*6272a393SAlexander Hansen }
933*6272a393SAlexander Hansen }
934*6272a393SAlexander Hansen else
935*6272a393SAlexander Hansen {
936*6272a393SAlexander Hansen error("Descriptor for new virtual sensor not found in config file");
937*6272a393SAlexander Hansen }
938*6272a393SAlexander Hansen }
939*6272a393SAlexander Hansen }
940*6272a393SAlexander Hansen
941*6272a393SAlexander Hansen } // namespace phosphor::virtual_sensor
942