1abcc94faSVijay Khemka #include "virtualSensor.hpp" 2abcc94faSVijay Khemka 3abcc94faSVijay Khemka #include "config.hpp" 4abcc94faSVijay Khemka 5*82b39c66SPatrick Williams #include <phosphor-logging/lg2.hpp> 6abcc94faSVijay Khemka #include <sdeventplus/event.hpp> 7abcc94faSVijay Khemka 8abcc94faSVijay Khemka #include <fstream> 9abcc94faSVijay Khemka #include <iostream> 10abcc94faSVijay Khemka 11abcc94faSVijay Khemka static constexpr bool DEBUG = false; 12abcc94faSVijay Khemka static constexpr auto busName = "xyz.openbmc_project.VirtualSensor"; 13abcc94faSVijay Khemka static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/"; 14e7efe135SRashmica Gupta static constexpr auto entityManagerBusName = 15e7efe135SRashmica Gupta "xyz.openbmc_project.EntityManager"; 16e7efe135SRashmica Gupta static constexpr auto vsThresholdsIfaceSuffix = ".Thresholds"; 17304fd0e4SRashmica Gupta static constexpr std::array<const char*, 1> calculationIfaces = { 18304fd0e4SRashmica Gupta "xyz.openbmc_project.Configuration.ModifiedMedian"}; 191dff7dceSRashmica Gupta static constexpr auto defaultHysteresis = 0; 20abcc94faSVijay Khemka 21*82b39c66SPatrick Williams PHOSPHOR_LOG2_USING_WITH_FLAGS; 22abcc94faSVijay Khemka 2351f898e2SVijay Khemka int handleDbusSignal(sd_bus_message* msg, void* usrData, sd_bus_error*) 2451f898e2SVijay Khemka { 2551f898e2SVijay Khemka if (usrData == nullptr) 2651f898e2SVijay Khemka { 2751f898e2SVijay Khemka throw std::runtime_error("Invalid match"); 2851f898e2SVijay Khemka } 2951f898e2SVijay Khemka 3051f898e2SVijay Khemka auto sdbpMsg = sdbusplus::message::message(msg); 3151f898e2SVijay Khemka std::string msgIfce; 3251f898e2SVijay Khemka std::map<std::string, std::variant<int64_t, double, bool>> msgData; 3351f898e2SVijay Khemka 3451f898e2SVijay Khemka sdbpMsg.read(msgIfce, msgData); 3551f898e2SVijay Khemka 3651f898e2SVijay Khemka if (msgData.find("Value") != msgData.end()) 3751f898e2SVijay Khemka { 3851f898e2SVijay Khemka using namespace phosphor::virtualSensor; 3951f898e2SVijay Khemka VirtualSensor* obj = static_cast<VirtualSensor*>(usrData); 4051f898e2SVijay Khemka // TODO(openbmc/phosphor-virtual-sensor#1): updateVirtualSensor should 4151f898e2SVijay Khemka // be changed to take the information we got from the signal, to avoid 4251f898e2SVijay Khemka // having to do numerous dbus queries. 4351f898e2SVijay Khemka obj->updateVirtualSensor(); 4451f898e2SVijay Khemka } 4551f898e2SVijay Khemka return 0; 4651f898e2SVijay Khemka } 4751f898e2SVijay Khemka 48abcc94faSVijay Khemka namespace phosphor 49abcc94faSVijay Khemka { 50abcc94faSVijay Khemka namespace virtualSensor 51abcc94faSVijay Khemka { 52abcc94faSVijay Khemka 53abcc94faSVijay Khemka void printParams(const VirtualSensor::ParamMap& paramMap) 54abcc94faSVijay Khemka { 55abcc94faSVijay Khemka for (const auto& p : paramMap) 56abcc94faSVijay Khemka { 57abcc94faSVijay Khemka const auto& p1 = p.first; 58abcc94faSVijay Khemka const auto& p2 = p.second; 59abcc94faSVijay Khemka auto val = p2->getParamValue(); 60abcc94faSVijay Khemka std::cout << p1 << " = " << val << "\n"; 61abcc94faSVijay Khemka } 62abcc94faSVijay Khemka } 63abcc94faSVijay Khemka 64abcc94faSVijay Khemka double SensorParam::getParamValue() 65abcc94faSVijay Khemka { 66abcc94faSVijay Khemka switch (paramType) 67abcc94faSVijay Khemka { 68abcc94faSVijay Khemka case constParam: 69abcc94faSVijay Khemka return value; 70abcc94faSVijay Khemka break; 717452a867SVijay Khemka case dbusParam: 727452a867SVijay Khemka return dbusSensor->getSensorValue(); 737452a867SVijay Khemka break; 74abcc94faSVijay Khemka default: 75abcc94faSVijay Khemka throw std::invalid_argument("param type not supported"); 76abcc94faSVijay Khemka } 77abcc94faSVijay Khemka } 78abcc94faSVijay Khemka 790fcf0e1cSLei YU using AssociationList = 800fcf0e1cSLei YU std::vector<std::tuple<std::string, std::string, std::string>>; 810fcf0e1cSLei YU 820fcf0e1cSLei YU AssociationList getAssociationsFromJson(const Json& j) 830fcf0e1cSLei YU { 840fcf0e1cSLei YU AssociationList assocs{}; 850fcf0e1cSLei YU try 860fcf0e1cSLei YU { 870fcf0e1cSLei YU j.get_to(assocs); 880fcf0e1cSLei YU } 890fcf0e1cSLei YU catch (const std::exception& ex) 900fcf0e1cSLei YU { 91*82b39c66SPatrick Williams error("Failed to parse association: {ERROR}", "ERROR", ex); 920fcf0e1cSLei YU } 930fcf0e1cSLei YU return assocs; 940fcf0e1cSLei YU } 950fcf0e1cSLei YU 96e7efe135SRashmica Gupta template <typename U> 97e7efe135SRashmica Gupta struct VariantToNumber 98e7efe135SRashmica Gupta { 99e7efe135SRashmica Gupta template <typename T> 100e7efe135SRashmica Gupta U operator()(const T& t) const 101e7efe135SRashmica Gupta { 102e7efe135SRashmica Gupta if constexpr (std::is_convertible<T, U>::value) 103e7efe135SRashmica Gupta { 104e7efe135SRashmica Gupta return static_cast<U>(t); 105e7efe135SRashmica Gupta } 106e7efe135SRashmica Gupta throw std::invalid_argument("Invalid number type in config\n"); 107e7efe135SRashmica Gupta } 108e7efe135SRashmica Gupta }; 109e7efe135SRashmica Gupta 110e7efe135SRashmica Gupta template <typename U> 111e7efe135SRashmica Gupta U getNumberFromConfig(const PropertyMap& map, const std::string& name, 112e7efe135SRashmica Gupta bool required) 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 { 120*82b39c66SPatrick Williams error("Required field {NAME} missing in config", "NAME", name); 121e7efe135SRashmica Gupta throw std::invalid_argument("Required field missing in config"); 122e7efe135SRashmica Gupta } 123e7efe135SRashmica Gupta return std::numeric_limits<U>::quiet_NaN(); 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, 138e7efe135SRashmica Gupta uint64_t severity) 139e7efe135SRashmica Gupta { 140e7efe135SRashmica Gupta std::string threshold; 141e7efe135SRashmica Gupta std::string suffix; 142e7efe135SRashmica Gupta static const std::array thresholdTypes{"Warning", "Critical", 143e7efe135SRashmica Gupta "PerformanceLoss", "SoftShutdown", 144e7efe135SRashmica Gupta "HardShutdown"}; 145e7efe135SRashmica Gupta 146e7efe135SRashmica Gupta if (severity >= thresholdTypes.size()) 147e7efe135SRashmica Gupta { 148e7efe135SRashmica Gupta throw std::invalid_argument( 149e7efe135SRashmica Gupta "Invalid threshold severity specified in entity manager"); 150e7efe135SRashmica Gupta } 151e7efe135SRashmica Gupta threshold = thresholdTypes[severity]; 152e7efe135SRashmica Gupta 153e7efe135SRashmica Gupta if (direction == "less than") 154e7efe135SRashmica Gupta { 155e7efe135SRashmica Gupta suffix = "Low"; 156e7efe135SRashmica Gupta } 157e7efe135SRashmica Gupta else if (direction == "greater than") 158e7efe135SRashmica Gupta { 159e7efe135SRashmica Gupta suffix = "High"; 160e7efe135SRashmica Gupta } 161e7efe135SRashmica Gupta else 162e7efe135SRashmica Gupta { 163e7efe135SRashmica Gupta throw std::invalid_argument( 164e7efe135SRashmica Gupta "Invalid threshold direction specified in entity manager"); 165e7efe135SRashmica Gupta } 166e7efe135SRashmica Gupta return threshold + suffix; 167e7efe135SRashmica Gupta } 168e7efe135SRashmica Gupta 169e7efe135SRashmica Gupta void parseThresholds(Json& thresholds, const PropertyMap& propertyMap) 170e7efe135SRashmica Gupta { 171e7efe135SRashmica Gupta std::string direction; 172e7efe135SRashmica Gupta 173e7efe135SRashmica Gupta auto severity = 174e7efe135SRashmica Gupta getNumberFromConfig<uint64_t>(propertyMap, "Severity", true); 175e7efe135SRashmica Gupta auto value = getNumberFromConfig<double>(propertyMap, "Value", true); 176e7efe135SRashmica Gupta 177e7efe135SRashmica Gupta auto itr = propertyMap.find("Direction"); 178e7efe135SRashmica Gupta if (itr != propertyMap.end()) 179e7efe135SRashmica Gupta { 180e7efe135SRashmica Gupta direction = std::get<std::string>(itr->second); 181e7efe135SRashmica Gupta } 182e7efe135SRashmica Gupta 183e7efe135SRashmica Gupta auto threshold = getThresholdType(direction, severity); 184e7efe135SRashmica Gupta thresholds[threshold] = value; 1851dff7dceSRashmica Gupta 1861dff7dceSRashmica Gupta auto hysteresis = 1871dff7dceSRashmica Gupta getNumberFromConfig<double>(propertyMap, "Hysteresis", false); 1881dff7dceSRashmica Gupta if (hysteresis != std::numeric_limits<double>::quiet_NaN()) 1891dff7dceSRashmica Gupta { 1901dff7dceSRashmica Gupta thresholds[threshold + "Hysteresis"] = hysteresis; 1911dff7dceSRashmica Gupta } 192e7efe135SRashmica Gupta } 193e7efe135SRashmica Gupta 194e7efe135SRashmica Gupta void VirtualSensor::parseConfigInterface(const PropertyMap& propertyMap, 195e7efe135SRashmica Gupta const std::string& sensorType, 196e7efe135SRashmica Gupta const std::string& interface) 197e7efe135SRashmica Gupta { 198e7efe135SRashmica Gupta /* Parse sensors / DBus params */ 199e7efe135SRashmica Gupta if (auto itr = propertyMap.find("Sensors"); itr != propertyMap.end()) 200e7efe135SRashmica Gupta { 201e7efe135SRashmica Gupta auto sensors = std::get<std::vector<std::string>>(itr->second); 202e7efe135SRashmica Gupta for (auto sensor : sensors) 203e7efe135SRashmica Gupta { 204e7efe135SRashmica Gupta std::replace(sensor.begin(), sensor.end(), ' ', '_'); 205e7efe135SRashmica Gupta auto sensorObjPath = sensorDbusPath + sensorType + "/" + sensor; 206e7efe135SRashmica Gupta 207e7efe135SRashmica Gupta auto paramPtr = 208e7efe135SRashmica Gupta std::make_unique<SensorParam>(bus, sensorObjPath, this); 209e7efe135SRashmica Gupta symbols.create_variable(sensor); 210e7efe135SRashmica Gupta paramMap.emplace(std::move(sensor), std::move(paramPtr)); 211e7efe135SRashmica Gupta } 212e7efe135SRashmica Gupta } 213e7efe135SRashmica Gupta /* Get expression string */ 214e7efe135SRashmica Gupta if (!isCalculationType(interface)) 215e7efe135SRashmica Gupta { 216e7efe135SRashmica Gupta throw std::invalid_argument("Invalid expression in interface"); 217e7efe135SRashmica Gupta } 218e7efe135SRashmica Gupta exprStr = interface; 219e7efe135SRashmica Gupta 220e7efe135SRashmica Gupta /* Get optional min and max input and output values */ 221e7efe135SRashmica Gupta ValueIface::maxValue( 222e7efe135SRashmica Gupta getNumberFromConfig<double>(propertyMap, "MaxValue", false)); 223e7efe135SRashmica Gupta ValueIface::minValue( 224e7efe135SRashmica Gupta getNumberFromConfig<double>(propertyMap, "MinValue", false)); 225e7efe135SRashmica Gupta maxValidInput = 226e7efe135SRashmica Gupta getNumberFromConfig<double>(propertyMap, "MaxValidInput", false); 227e7efe135SRashmica Gupta minValidInput = 228e7efe135SRashmica Gupta getNumberFromConfig<double>(propertyMap, "MinValidInput", false); 229e7efe135SRashmica Gupta } 230e7efe135SRashmica Gupta 231ce675228SMatt Spinler void VirtualSensor::initVirtualSensor(const Json& sensorConfig, 232ce675228SMatt Spinler const std::string& objPath) 233abcc94faSVijay Khemka { 234abcc94faSVijay Khemka 235abcc94faSVijay Khemka static const Json empty{}; 236abcc94faSVijay Khemka 237abcc94faSVijay Khemka /* Get threshold values if defined in config */ 238abcc94faSVijay Khemka auto threshold = sensorConfig.value("Threshold", empty); 239f15189e3SMatt Spinler 2403e99919bSRashmica Gupta createThresholds(threshold, objPath); 241abcc94faSVijay Khemka 242f6443742SHarvey Wu /* Get MaxValue, MinValue setting if defined in config */ 243f6443742SHarvey Wu auto confDesc = sensorConfig.value("Desc", empty); 244f6443742SHarvey Wu if (auto maxConf = confDesc.find("MaxValue"); 245f6443742SHarvey Wu maxConf != confDesc.end() && maxConf->is_number()) 246f6443742SHarvey Wu { 247f6443742SHarvey Wu ValueIface::maxValue(maxConf->get<double>()); 248f6443742SHarvey Wu } 249f6443742SHarvey Wu if (auto minConf = confDesc.find("MinValue"); 250f6443742SHarvey Wu minConf != confDesc.end() && minConf->is_number()) 251f6443742SHarvey Wu { 252f6443742SHarvey Wu ValueIface::minValue(minConf->get<double>()); 253f6443742SHarvey Wu } 254f6443742SHarvey Wu 2550fcf0e1cSLei YU /* Get optional association */ 2560fcf0e1cSLei YU auto assocJson = sensorConfig.value("Associations", empty); 2570fcf0e1cSLei YU if (!assocJson.empty()) 2580fcf0e1cSLei YU { 2590fcf0e1cSLei YU auto assocs = getAssociationsFromJson(assocJson); 2600fcf0e1cSLei YU if (!assocs.empty()) 2610fcf0e1cSLei YU { 2620fcf0e1cSLei YU associationIface = 2630fcf0e1cSLei YU std::make_unique<AssociationObject>(bus, objPath.c_str()); 2640fcf0e1cSLei YU associationIface->associations(assocs); 2650fcf0e1cSLei YU } 2660fcf0e1cSLei YU } 2670fcf0e1cSLei YU 268abcc94faSVijay Khemka /* Get expression string */ 269abcc94faSVijay Khemka exprStr = sensorConfig.value("Expression", ""); 270abcc94faSVijay Khemka 271abcc94faSVijay Khemka /* Get all the parameter listed in configuration */ 272abcc94faSVijay Khemka auto params = sensorConfig.value("Params", empty); 273abcc94faSVijay Khemka 274abcc94faSVijay Khemka /* Check for constant parameter */ 275abcc94faSVijay Khemka const auto& consParams = params.value("ConstParam", empty); 276abcc94faSVijay Khemka if (!consParams.empty()) 277abcc94faSVijay Khemka { 278abcc94faSVijay Khemka for (auto& j : consParams) 279abcc94faSVijay Khemka { 280abcc94faSVijay Khemka if (j.find("ParamName") != j.end()) 281abcc94faSVijay Khemka { 282abcc94faSVijay Khemka auto paramPtr = std::make_unique<SensorParam>(j["Value"]); 2833ed9a516SVijay Khemka std::string name = j["ParamName"]; 2843ed9a516SVijay Khemka symbols.create_variable(name); 2853ed9a516SVijay Khemka paramMap.emplace(std::move(name), std::move(paramPtr)); 286abcc94faSVijay Khemka } 287abcc94faSVijay Khemka else 288abcc94faSVijay Khemka { 289abcc94faSVijay Khemka /* Invalid configuration */ 290abcc94faSVijay Khemka throw std::invalid_argument( 291abcc94faSVijay Khemka "ParamName not found in configuration"); 292abcc94faSVijay Khemka } 293abcc94faSVijay Khemka } 294abcc94faSVijay Khemka } 295abcc94faSVijay Khemka 2967452a867SVijay Khemka /* Check for dbus parameter */ 2977452a867SVijay Khemka auto dbusParams = params.value("DbusParam", empty); 2987452a867SVijay Khemka if (!dbusParams.empty()) 2997452a867SVijay Khemka { 3007452a867SVijay Khemka for (auto& j : dbusParams) 3017452a867SVijay Khemka { 3027452a867SVijay Khemka /* Get parameter dbus sensor descriptor */ 3037452a867SVijay Khemka auto desc = j.value("Desc", empty); 3047452a867SVijay Khemka if ((!desc.empty()) && (j.find("ParamName") != j.end())) 3057452a867SVijay Khemka { 3067452a867SVijay Khemka std::string sensorType = desc.value("SensorType", ""); 3077452a867SVijay Khemka std::string name = desc.value("Name", ""); 3087452a867SVijay Khemka 3097452a867SVijay Khemka if (!sensorType.empty() && !name.empty()) 3107452a867SVijay Khemka { 311862c3d1eSRashmica Gupta auto objPath = sensorDbusPath + sensorType + "/" + name; 3127452a867SVijay Khemka 31351f898e2SVijay Khemka auto paramPtr = 31451f898e2SVijay Khemka std::make_unique<SensorParam>(bus, objPath, this); 3153ed9a516SVijay Khemka std::string name = j["ParamName"]; 3163ed9a516SVijay Khemka symbols.create_variable(name); 3173ed9a516SVijay Khemka paramMap.emplace(std::move(name), std::move(paramPtr)); 3187452a867SVijay Khemka } 3197452a867SVijay Khemka } 3207452a867SVijay Khemka } 3217452a867SVijay Khemka } 322abcc94faSVijay Khemka 3233ed9a516SVijay Khemka symbols.add_constants(); 3249f1ef4f5SMatt Spinler symbols.add_package(vecopsPackage); 3253ed9a516SVijay Khemka expression.register_symbol_table(symbols); 3263ed9a516SVijay Khemka 3273ed9a516SVijay Khemka /* parser from exprtk */ 3283ed9a516SVijay Khemka exprtk::parser<double> parser{}; 329ddc6dcd6SMatt Spinler if (!parser.compile(exprStr, expression)) 330ddc6dcd6SMatt Spinler { 331*82b39c66SPatrick Williams error("Expression compilation failed"); 332ddc6dcd6SMatt Spinler 333ddc6dcd6SMatt Spinler for (std::size_t i = 0; i < parser.error_count(); ++i) 334ddc6dcd6SMatt Spinler { 335*82b39c66SPatrick Williams auto err = parser.get_error(i); 336*82b39c66SPatrick Williams error("Error parsing token at {POSITION}: {ERROR}", "POSITION", 337*82b39c66SPatrick Williams err.token.position, "TYPE", 338*82b39c66SPatrick Williams exprtk::parser_error::to_str(err.mode), "ERROR", 339*82b39c66SPatrick Williams err.diagnostic); 340ddc6dcd6SMatt Spinler } 341ddc6dcd6SMatt Spinler throw std::runtime_error("Expression compilation failed"); 342ddc6dcd6SMatt Spinler } 3433ed9a516SVijay Khemka 344abcc94faSVijay Khemka /* Print all parameters for debug purpose only */ 345abcc94faSVijay Khemka if (DEBUG) 346abcc94faSVijay Khemka printParams(paramMap); 347abcc94faSVijay Khemka } 348abcc94faSVijay Khemka 349e7efe135SRashmica Gupta void VirtualSensor::initVirtualSensor(const InterfaceMap& interfaceMap, 350e7efe135SRashmica Gupta const std::string& objPath, 351e7efe135SRashmica Gupta const std::string& sensorType, 352e7efe135SRashmica Gupta const std::string& calculationIface) 353e7efe135SRashmica Gupta { 354e7efe135SRashmica Gupta Json thresholds; 355e7efe135SRashmica Gupta const std::string vsThresholdsIntf = 356e7efe135SRashmica Gupta calculationIface + vsThresholdsIfaceSuffix; 357e7efe135SRashmica Gupta 358e7efe135SRashmica Gupta for (const auto& [interface, propertyMap] : interfaceMap) 359e7efe135SRashmica Gupta { 360e7efe135SRashmica Gupta /* Each threshold is on it's own interface with a number as a suffix 361e7efe135SRashmica Gupta * eg xyz.openbmc_project.Configuration.ModifiedMedian.Thresholds1 */ 362e7efe135SRashmica Gupta if (interface.find(vsThresholdsIntf) != std::string::npos) 363e7efe135SRashmica Gupta { 364e7efe135SRashmica Gupta parseThresholds(thresholds, propertyMap); 365e7efe135SRashmica Gupta } 366e7efe135SRashmica Gupta else if (interface == calculationIface) 367e7efe135SRashmica Gupta { 368e7efe135SRashmica Gupta parseConfigInterface(propertyMap, sensorType, interface); 369e7efe135SRashmica Gupta } 370e7efe135SRashmica Gupta } 371e7efe135SRashmica Gupta 372e7efe135SRashmica Gupta createThresholds(thresholds, objPath); 373e7efe135SRashmica Gupta symbols.add_constants(); 374e7efe135SRashmica Gupta symbols.add_package(vecopsPackage); 375e7efe135SRashmica Gupta expression.register_symbol_table(symbols); 376e7efe135SRashmica Gupta 377e7efe135SRashmica Gupta /* Print all parameters for debug purpose only */ 378e7efe135SRashmica Gupta if (DEBUG) 379e7efe135SRashmica Gupta { 380e7efe135SRashmica Gupta printParams(paramMap); 381e7efe135SRashmica Gupta } 382e7efe135SRashmica Gupta } 383e7efe135SRashmica Gupta 384abcc94faSVijay Khemka void VirtualSensor::setSensorValue(double value) 385abcc94faSVijay Khemka { 386543bf668SPatrick Williams value = std::clamp(value, ValueIface::minValue(), ValueIface::maxValue()); 387abcc94faSVijay Khemka ValueIface::value(value); 388abcc94faSVijay Khemka } 389abcc94faSVijay Khemka 390304fd0e4SRashmica Gupta double VirtualSensor::calculateValue(const std::string& calculation, 391304fd0e4SRashmica Gupta const VirtualSensor::ParamMap& paramMap) 392e7efe135SRashmica Gupta { 393304fd0e4SRashmica Gupta auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(), 394304fd0e4SRashmica Gupta calculation); 395304fd0e4SRashmica Gupta if (itr == calculationIfaces.end()) 396304fd0e4SRashmica Gupta { 397e7efe135SRashmica Gupta return std::numeric_limits<double>::quiet_NaN(); 398e7efe135SRashmica Gupta } 399304fd0e4SRashmica Gupta else if (calculation == "xyz.openbmc_project.Configuration.ModifiedMedian") 400304fd0e4SRashmica Gupta { 401304fd0e4SRashmica Gupta return calculateModifiedMedianValue(paramMap); 402304fd0e4SRashmica Gupta } 403304fd0e4SRashmica Gupta return std::numeric_limits<double>::quiet_NaN(); 404304fd0e4SRashmica Gupta } 405304fd0e4SRashmica Gupta 406304fd0e4SRashmica Gupta bool VirtualSensor::sensorInRange(double value) 407304fd0e4SRashmica Gupta { 408304fd0e4SRashmica Gupta if (value <= this->maxValidInput && value >= this->minValidInput) 409304fd0e4SRashmica Gupta { 410304fd0e4SRashmica Gupta return true; 411304fd0e4SRashmica Gupta } 412304fd0e4SRashmica Gupta return false; 413304fd0e4SRashmica Gupta } 414e7efe135SRashmica Gupta 415abcc94faSVijay Khemka void VirtualSensor::updateVirtualSensor() 4163ed9a516SVijay Khemka { 4173ed9a516SVijay Khemka for (auto& param : paramMap) 4183ed9a516SVijay Khemka { 4193ed9a516SVijay Khemka auto& name = param.first; 4203ed9a516SVijay Khemka auto& data = param.second; 4213ed9a516SVijay Khemka if (auto var = symbols.get_variable(name)) 4223ed9a516SVijay Khemka { 4233ed9a516SVijay Khemka var->ref() = data->getParamValue(); 4243ed9a516SVijay Khemka } 4253ed9a516SVijay Khemka else 4263ed9a516SVijay Khemka { 4273ed9a516SVijay Khemka /* Invalid parameter */ 4283ed9a516SVijay Khemka throw std::invalid_argument("ParamName not found in symbols"); 4293ed9a516SVijay Khemka } 4303ed9a516SVijay Khemka } 431e7efe135SRashmica Gupta auto itr = 432e7efe135SRashmica Gupta std::find(calculationIfaces.begin(), calculationIfaces.end(), exprStr); 433304fd0e4SRashmica Gupta auto val = (itr == calculationIfaces.end()) 434304fd0e4SRashmica Gupta ? expression.value() 435304fd0e4SRashmica Gupta : calculateValue(exprStr, paramMap); 43632a7156bSVijay Khemka 43732a7156bSVijay Khemka /* Set sensor value to dbus interface */ 4383ed9a516SVijay Khemka setSensorValue(val); 43932a7156bSVijay Khemka 4403ed9a516SVijay Khemka if (DEBUG) 441e7efe135SRashmica Gupta { 4423ed9a516SVijay Khemka std::cout << "Sensor value is " << val << "\n"; 443e7efe135SRashmica Gupta } 44432a7156bSVijay Khemka 4458f5e6119SMatt Spinler /* Check sensor thresholds and log required message */ 446b306b03dSMatt Spinler checkThresholds(val, perfLossIface); 447fdb826d5SPatrick Williams checkThresholds(val, warningIface); 448fdb826d5SPatrick Williams checkThresholds(val, criticalIface); 449fdb826d5SPatrick Williams checkThresholds(val, softShutdownIface); 450fdb826d5SPatrick Williams checkThresholds(val, hardShutdownIface); 4513ed9a516SVijay Khemka } 452abcc94faSVijay Khemka 453304fd0e4SRashmica Gupta double VirtualSensor::calculateModifiedMedianValue( 454304fd0e4SRashmica Gupta const VirtualSensor::ParamMap& paramMap) 455304fd0e4SRashmica Gupta { 456304fd0e4SRashmica Gupta std::vector<double> values; 457304fd0e4SRashmica Gupta 458304fd0e4SRashmica Gupta for (auto& param : paramMap) 459304fd0e4SRashmica Gupta { 460304fd0e4SRashmica Gupta auto& name = param.first; 461304fd0e4SRashmica Gupta if (auto var = symbols.get_variable(name)) 462304fd0e4SRashmica Gupta { 463304fd0e4SRashmica Gupta if (!sensorInRange(var->ref())) 464304fd0e4SRashmica Gupta { 465304fd0e4SRashmica Gupta continue; 466304fd0e4SRashmica Gupta } 467304fd0e4SRashmica Gupta values.push_back(var->ref()); 468304fd0e4SRashmica Gupta } 469304fd0e4SRashmica Gupta } 470304fd0e4SRashmica Gupta 471304fd0e4SRashmica Gupta size_t size = values.size(); 472304fd0e4SRashmica Gupta std::sort(values.begin(), values.end()); 473304fd0e4SRashmica Gupta switch (size) 474304fd0e4SRashmica Gupta { 475304fd0e4SRashmica Gupta case 2: 476304fd0e4SRashmica Gupta /* Choose biggest value */ 477304fd0e4SRashmica Gupta return values.at(1); 478304fd0e4SRashmica Gupta case 0: 479304fd0e4SRashmica Gupta return std::numeric_limits<double>::quiet_NaN(); 480304fd0e4SRashmica Gupta default: 481304fd0e4SRashmica Gupta /* Choose median value */ 482304fd0e4SRashmica Gupta if (size % 2 == 0) 483304fd0e4SRashmica Gupta { 484304fd0e4SRashmica Gupta // Average of the two middle values 485304fd0e4SRashmica Gupta return (values.at(size / 2) + values.at(size / 2 - 1)) / 2; 486304fd0e4SRashmica Gupta } 487304fd0e4SRashmica Gupta else 488304fd0e4SRashmica Gupta { 489304fd0e4SRashmica Gupta return values.at((size - 1) / 2); 490304fd0e4SRashmica Gupta } 491304fd0e4SRashmica Gupta } 492304fd0e4SRashmica Gupta } 493304fd0e4SRashmica Gupta 4943e99919bSRashmica Gupta void VirtualSensor::createThresholds(const Json& threshold, 4953e99919bSRashmica Gupta const std::string& objPath) 4963e99919bSRashmica Gupta { 4973e99919bSRashmica Gupta if (threshold.empty()) 4983e99919bSRashmica Gupta { 4993e99919bSRashmica Gupta return; 5003e99919bSRashmica Gupta } 5013e99919bSRashmica Gupta // Only create the threshold interfaces if 5023e99919bSRashmica Gupta // at least one of their values is present. 5033e99919bSRashmica Gupta if (threshold.contains("CriticalHigh") || threshold.contains("CriticalLow")) 5043e99919bSRashmica Gupta { 5053e99919bSRashmica Gupta criticalIface = 5063e99919bSRashmica Gupta std::make_unique<Threshold<CriticalObject>>(bus, objPath.c_str()); 5073e99919bSRashmica Gupta 5083e99919bSRashmica Gupta criticalIface->criticalHigh(threshold.value( 5093e99919bSRashmica Gupta "CriticalHigh", std::numeric_limits<double>::quiet_NaN())); 5103e99919bSRashmica Gupta criticalIface->criticalLow(threshold.value( 5113e99919bSRashmica Gupta "CriticalLow", std::numeric_limits<double>::quiet_NaN())); 5121dff7dceSRashmica Gupta criticalIface->setHighHysteresis( 5131dff7dceSRashmica Gupta threshold.value("CriticalHighHysteresis", defaultHysteresis)); 5141dff7dceSRashmica Gupta criticalIface->setLowHysteresis( 5151dff7dceSRashmica Gupta threshold.value("CriticalLowHysteresis", defaultHysteresis)); 5163e99919bSRashmica Gupta } 5173e99919bSRashmica Gupta 5183e99919bSRashmica Gupta if (threshold.contains("WarningHigh") || threshold.contains("WarningLow")) 5193e99919bSRashmica Gupta { 5203e99919bSRashmica Gupta warningIface = 5213e99919bSRashmica Gupta std::make_unique<Threshold<WarningObject>>(bus, objPath.c_str()); 5223e99919bSRashmica Gupta 5233e99919bSRashmica Gupta warningIface->warningHigh(threshold.value( 5243e99919bSRashmica Gupta "WarningHigh", std::numeric_limits<double>::quiet_NaN())); 5253e99919bSRashmica Gupta warningIface->warningLow(threshold.value( 5263e99919bSRashmica Gupta "WarningLow", std::numeric_limits<double>::quiet_NaN())); 5271dff7dceSRashmica Gupta warningIface->setHighHysteresis( 5281dff7dceSRashmica Gupta threshold.value("WarningHighHysteresis", defaultHysteresis)); 5291dff7dceSRashmica Gupta warningIface->setLowHysteresis( 5301dff7dceSRashmica Gupta threshold.value("WarningLowHysteresis", defaultHysteresis)); 5313e99919bSRashmica Gupta } 5323e99919bSRashmica Gupta 5333e99919bSRashmica Gupta if (threshold.contains("HardShutdownHigh") || 5343e99919bSRashmica Gupta threshold.contains("HardShutdownLow")) 5353e99919bSRashmica Gupta { 5363e99919bSRashmica Gupta hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>( 5373e99919bSRashmica Gupta bus, objPath.c_str()); 5383e99919bSRashmica Gupta 5393e99919bSRashmica Gupta hardShutdownIface->hardShutdownHigh(threshold.value( 5403e99919bSRashmica Gupta "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN())); 5413e99919bSRashmica Gupta hardShutdownIface->hardShutdownLow(threshold.value( 5423e99919bSRashmica Gupta "HardShutdownLow", std::numeric_limits<double>::quiet_NaN())); 5431dff7dceSRashmica Gupta hardShutdownIface->setHighHysteresis( 5441dff7dceSRashmica Gupta threshold.value("HardShutdownHighHysteresis", defaultHysteresis)); 5451dff7dceSRashmica Gupta hardShutdownIface->setLowHysteresis( 5461dff7dceSRashmica Gupta threshold.value("HardShutdownLowHysteresis", defaultHysteresis)); 5473e99919bSRashmica Gupta } 5483e99919bSRashmica Gupta 5493e99919bSRashmica Gupta if (threshold.contains("SoftShutdownHigh") || 5503e99919bSRashmica Gupta threshold.contains("SoftShutdownLow")) 5513e99919bSRashmica Gupta { 5523e99919bSRashmica Gupta softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>( 5533e99919bSRashmica Gupta bus, objPath.c_str()); 5543e99919bSRashmica Gupta 5553e99919bSRashmica Gupta softShutdownIface->softShutdownHigh(threshold.value( 5563e99919bSRashmica Gupta "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN())); 5573e99919bSRashmica Gupta softShutdownIface->softShutdownLow(threshold.value( 5583e99919bSRashmica Gupta "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN())); 5591dff7dceSRashmica Gupta softShutdownIface->setHighHysteresis( 5601dff7dceSRashmica Gupta threshold.value("SoftShutdownHighHysteresis", defaultHysteresis)); 5611dff7dceSRashmica Gupta softShutdownIface->setLowHysteresis( 5621dff7dceSRashmica Gupta threshold.value("SoftShutdownLowHysteresis", defaultHysteresis)); 5633e99919bSRashmica Gupta } 5643e99919bSRashmica Gupta 5653e99919bSRashmica Gupta if (threshold.contains("PerformanceLossHigh") || 5663e99919bSRashmica Gupta threshold.contains("PerformanceLossLow")) 5673e99919bSRashmica Gupta { 5683e99919bSRashmica Gupta perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>( 5693e99919bSRashmica Gupta bus, objPath.c_str()); 5703e99919bSRashmica Gupta 5713e99919bSRashmica Gupta perfLossIface->performanceLossHigh(threshold.value( 5723e99919bSRashmica Gupta "PerformanceLossHigh", std::numeric_limits<double>::quiet_NaN())); 5733e99919bSRashmica Gupta perfLossIface->performanceLossLow(threshold.value( 5743e99919bSRashmica Gupta "PerformanceLossLow", std::numeric_limits<double>::quiet_NaN())); 5751dff7dceSRashmica Gupta perfLossIface->setHighHysteresis(threshold.value( 5761dff7dceSRashmica Gupta "PerformanceLossHighHysteresis", defaultHysteresis)); 5771dff7dceSRashmica Gupta perfLossIface->setLowHysteresis( 5781dff7dceSRashmica Gupta threshold.value("PerformanceLossLowHysteresis", defaultHysteresis)); 5793e99919bSRashmica Gupta } 5803e99919bSRashmica Gupta } 5813e99919bSRashmica Gupta 582e7efe135SRashmica Gupta ManagedObjectType VirtualSensors::getObjectsFromDBus() 583e7efe135SRashmica Gupta { 584e7efe135SRashmica Gupta ManagedObjectType objects; 585e7efe135SRashmica Gupta 586e7efe135SRashmica Gupta try 587e7efe135SRashmica Gupta { 588e7efe135SRashmica Gupta auto method = bus.new_method_call(entityManagerBusName, "/", 589e7efe135SRashmica Gupta "org.freedesktop.DBus.ObjectManager", 590e7efe135SRashmica Gupta "GetManagedObjects"); 591e7efe135SRashmica Gupta auto reply = bus.call(method); 592e7efe135SRashmica Gupta reply.read(objects); 593e7efe135SRashmica Gupta } 594e7efe135SRashmica Gupta catch (const sdbusplus::exception::SdBusError& ex) 595e7efe135SRashmica Gupta { 596e7efe135SRashmica Gupta // If entity manager isn't running yet, keep going. 597e7efe135SRashmica Gupta if (std::string("org.freedesktop.DBus.Error.ServiceUnknown") != 598e7efe135SRashmica Gupta ex.name()) 599e7efe135SRashmica Gupta { 600e7efe135SRashmica Gupta throw ex.name(); 601e7efe135SRashmica Gupta } 602e7efe135SRashmica Gupta } 603e7efe135SRashmica Gupta 604e7efe135SRashmica Gupta return objects; 605e7efe135SRashmica Gupta } 606e7efe135SRashmica Gupta 607e7efe135SRashmica Gupta void VirtualSensors::propertiesChanged(sdbusplus::message::message& msg) 608e7efe135SRashmica Gupta { 609e7efe135SRashmica Gupta std::string path; 610e7efe135SRashmica Gupta PropertyMap properties; 611e7efe135SRashmica Gupta 612e7efe135SRashmica Gupta msg.read(path, properties); 613e7efe135SRashmica Gupta 614e7efe135SRashmica Gupta /* We get multiple callbacks for one sensor. 'Type' is a required field and 615e7efe135SRashmica Gupta * is a unique label so use to to only proceed once per sensor */ 616e7efe135SRashmica Gupta if (properties.contains("Type")) 617e7efe135SRashmica Gupta { 618e7efe135SRashmica Gupta if (isCalculationType(path)) 619e7efe135SRashmica Gupta { 620e7efe135SRashmica Gupta createVirtualSensorsFromDBus(path); 621e7efe135SRashmica Gupta } 622e7efe135SRashmica Gupta } 623e7efe135SRashmica Gupta } 624e7efe135SRashmica Gupta 625abcc94faSVijay Khemka /** @brief Parsing Virtual Sensor config JSON file */ 626abcc94faSVijay Khemka Json VirtualSensors::parseConfigFile(const std::string configFile) 627abcc94faSVijay Khemka { 628abcc94faSVijay Khemka std::ifstream jsonFile(configFile); 629abcc94faSVijay Khemka if (!jsonFile.is_open()) 630abcc94faSVijay Khemka { 631*82b39c66SPatrick Williams error("config JSON file {FILENAME} not found", "FILENAME", configFile); 632e7efe135SRashmica Gupta return {}; 633abcc94faSVijay Khemka } 634abcc94faSVijay Khemka 635abcc94faSVijay Khemka auto data = Json::parse(jsonFile, nullptr, false); 636abcc94faSVijay Khemka if (data.is_discarded()) 637abcc94faSVijay Khemka { 638*82b39c66SPatrick Williams error("config readings JSON parser failure with {FILENAME}", "FILENAME", 639*82b39c66SPatrick Williams configFile); 640abcc94faSVijay Khemka throw std::exception{}; 641abcc94faSVijay Khemka } 642abcc94faSVijay Khemka 643abcc94faSVijay Khemka return data; 644abcc94faSVijay Khemka } 645abcc94faSVijay Khemka 646e0d371e4SVijay Khemka std::map<std::string, ValueIface::Unit> unitMap = { 647e0d371e4SVijay Khemka {"temperature", ValueIface::Unit::DegreesC}, 648e0d371e4SVijay Khemka {"fan_tach", ValueIface::Unit::RPMS}, 649e0d371e4SVijay Khemka {"voltage", ValueIface::Unit::Volts}, 650e0d371e4SVijay Khemka {"altitude", ValueIface::Unit::Meters}, 651e0d371e4SVijay Khemka {"current", ValueIface::Unit::Amperes}, 652e0d371e4SVijay Khemka {"power", ValueIface::Unit::Watts}, 653e0d371e4SVijay Khemka {"energy", ValueIface::Unit::Joules}, 6542b56ddb3SKumar Thangavel {"utilization", ValueIface::Unit::Percent}, 6554ac7a7f2SRashmica Gupta {"airflow", ValueIface::Unit::CFM}, 6564ac7a7f2SRashmica Gupta {"pressure", ValueIface::Unit::Pascals}}; 657e0d371e4SVijay Khemka 658e7efe135SRashmica Gupta const std::string getSensorTypeFromUnit(const std::string& unit) 659e7efe135SRashmica Gupta { 660e7efe135SRashmica Gupta std::string unitPrefix = "xyz.openbmc_project.Sensor.Value.Unit."; 661e7efe135SRashmica Gupta for (auto [type, unitObj] : unitMap) 662e7efe135SRashmica Gupta { 663e7efe135SRashmica Gupta auto unitPath = ValueIface::convertUnitToString(unitObj); 664e7efe135SRashmica Gupta if (unitPath == (unitPrefix + unit)) 665e7efe135SRashmica Gupta { 666e7efe135SRashmica Gupta return type; 667e7efe135SRashmica Gupta } 668e7efe135SRashmica Gupta } 669e7efe135SRashmica Gupta return ""; 670e7efe135SRashmica Gupta } 671e7efe135SRashmica Gupta 672e7efe135SRashmica Gupta void VirtualSensors::setupMatches() 673e7efe135SRashmica Gupta { 674e7efe135SRashmica Gupta /* Already setup */ 675e7efe135SRashmica Gupta if (!this->matches.empty()) 676e7efe135SRashmica Gupta { 677e7efe135SRashmica Gupta return; 678e7efe135SRashmica Gupta } 679e7efe135SRashmica Gupta 680e7efe135SRashmica Gupta /* Setup matches */ 681e7efe135SRashmica Gupta auto eventHandler = [this](sdbusplus::message::message& message) { 682e7efe135SRashmica Gupta if (message.is_method_error()) 683e7efe135SRashmica Gupta { 684*82b39c66SPatrick Williams error("Callback method error"); 685e7efe135SRashmica Gupta return; 686e7efe135SRashmica Gupta } 687e7efe135SRashmica Gupta this->propertiesChanged(message); 688e7efe135SRashmica Gupta }; 689e7efe135SRashmica Gupta 690e7efe135SRashmica Gupta for (const char* iface : calculationIfaces) 691e7efe135SRashmica Gupta { 692e7efe135SRashmica Gupta auto match = std::make_unique<sdbusplus::bus::match::match>( 693e7efe135SRashmica Gupta bus, 694e7efe135SRashmica Gupta sdbusplus::bus::match::rules::propertiesChangedNamespace( 695e7efe135SRashmica Gupta "/xyz/openbmc_project/inventory", iface), 696e7efe135SRashmica Gupta eventHandler); 697e7efe135SRashmica Gupta this->matches.emplace_back(std::move(match)); 698e7efe135SRashmica Gupta } 699e7efe135SRashmica Gupta } 700e7efe135SRashmica Gupta 701e7efe135SRashmica Gupta void VirtualSensors::createVirtualSensorsFromDBus( 702e7efe135SRashmica Gupta const std::string& calculationIface) 703e7efe135SRashmica Gupta { 704e7efe135SRashmica Gupta if (calculationIface.empty()) 705e7efe135SRashmica Gupta { 706*82b39c66SPatrick Williams error("No calculation type supplied"); 707e7efe135SRashmica Gupta return; 708e7efe135SRashmica Gupta } 709e7efe135SRashmica Gupta auto objects = getObjectsFromDBus(); 710e7efe135SRashmica Gupta 711e7efe135SRashmica Gupta /* Get virtual sensors config data */ 712e7efe135SRashmica Gupta for (const auto& [path, interfaceMap] : objects) 713e7efe135SRashmica Gupta { 714e7efe135SRashmica Gupta auto objpath = static_cast<std::string>(path); 715e7efe135SRashmica Gupta std::string name = path.filename(); 716e7efe135SRashmica Gupta std::string sensorType, sensorUnit; 717e7efe135SRashmica Gupta 718e7efe135SRashmica Gupta /* Find Virtual Sensor interfaces */ 719e7efe135SRashmica Gupta if (!interfaceMap.contains(calculationIface)) 720e7efe135SRashmica Gupta { 721e7efe135SRashmica Gupta continue; 722e7efe135SRashmica Gupta } 723e7efe135SRashmica Gupta if (name.empty()) 724e7efe135SRashmica Gupta { 725*82b39c66SPatrick Williams error("Virtual Sensor name not found in entity manager config"); 726e7efe135SRashmica Gupta continue; 727e7efe135SRashmica Gupta } 728e7efe135SRashmica Gupta if (virtualSensorsMap.contains(name)) 729e7efe135SRashmica Gupta { 730*82b39c66SPatrick Williams error("A virtual sensor named {NAME} already exists", "NAME", name); 731e7efe135SRashmica Gupta continue; 732e7efe135SRashmica Gupta } 733e7efe135SRashmica Gupta 734e7efe135SRashmica Gupta /* Extract the virtual sensor type as we need this to initialize the 735e7efe135SRashmica Gupta * sensor */ 736e7efe135SRashmica Gupta for (const auto& [interface, propertyMap] : interfaceMap) 737e7efe135SRashmica Gupta { 738e7efe135SRashmica Gupta if (interface != calculationIface) 739e7efe135SRashmica Gupta { 740e7efe135SRashmica Gupta continue; 741e7efe135SRashmica Gupta } 742e7efe135SRashmica Gupta auto itr = propertyMap.find("Units"); 743e7efe135SRashmica Gupta if (itr != propertyMap.end()) 744e7efe135SRashmica Gupta { 745e7efe135SRashmica Gupta sensorUnit = std::get<std::string>(itr->second); 746e7efe135SRashmica Gupta break; 747e7efe135SRashmica Gupta } 748e7efe135SRashmica Gupta } 749e7efe135SRashmica Gupta sensorType = getSensorTypeFromUnit(sensorUnit); 750e7efe135SRashmica Gupta if (sensorType.empty()) 751e7efe135SRashmica Gupta { 752*82b39c66SPatrick Williams error("Sensor unit type {TYPE} is not supported", "TYPE", 753*82b39c66SPatrick Williams sensorUnit); 754e7efe135SRashmica Gupta continue; 755e7efe135SRashmica Gupta } 756e7efe135SRashmica Gupta 757e7efe135SRashmica Gupta try 758e7efe135SRashmica Gupta { 759e7efe135SRashmica Gupta auto virtObjPath = sensorDbusPath + sensorType + "/" + name; 760e7efe135SRashmica Gupta 761e7efe135SRashmica Gupta auto virtualSensorPtr = std::make_unique<VirtualSensor>( 762e7efe135SRashmica Gupta bus, virtObjPath.c_str(), interfaceMap, name, sensorType, 763e7efe135SRashmica Gupta calculationIface); 764*82b39c66SPatrick Williams info("Added a new virtual sensor: {NAME} {TYPE}", "NAME", name, 765*82b39c66SPatrick Williams "TYPE", sensorType); 766e7efe135SRashmica Gupta virtualSensorPtr->updateVirtualSensor(); 767e7efe135SRashmica Gupta 768e7efe135SRashmica Gupta /* Initialize unit value for virtual sensor */ 769e7efe135SRashmica Gupta virtualSensorPtr->ValueIface::unit(unitMap[sensorType]); 770e7efe135SRashmica Gupta virtualSensorPtr->emit_object_added(); 771e7efe135SRashmica Gupta 772e7efe135SRashmica Gupta virtualSensorsMap.emplace(name, std::move(virtualSensorPtr)); 773e7efe135SRashmica Gupta 774e7efe135SRashmica Gupta /* Setup match for interfaces removed */ 775e7efe135SRashmica Gupta auto intfRemoved = [this, objpath, 776e7efe135SRashmica Gupta name](sdbusplus::message::message& message) { 777e7efe135SRashmica Gupta if (!virtualSensorsMap.contains(name)) 778e7efe135SRashmica Gupta { 779e7efe135SRashmica Gupta return; 780e7efe135SRashmica Gupta } 781e7efe135SRashmica Gupta sdbusplus::message::object_path path; 782e7efe135SRashmica Gupta message.read(path); 783e7efe135SRashmica Gupta if (static_cast<const std::string&>(path) == objpath) 784e7efe135SRashmica Gupta { 785*82b39c66SPatrick Williams info("Removed a virtual sensor: {NAME}", "NAME", name); 786e7efe135SRashmica Gupta virtualSensorsMap.erase(name); 787e7efe135SRashmica Gupta } 788e7efe135SRashmica Gupta }; 789e7efe135SRashmica Gupta auto matchOnRemove = std::make_unique<sdbusplus::bus::match::match>( 790e7efe135SRashmica Gupta bus, 791e7efe135SRashmica Gupta sdbusplus::bus::match::rules::interfacesRemoved() + 792e7efe135SRashmica Gupta sdbusplus::bus::match::rules::argNpath(0, objpath), 793e7efe135SRashmica Gupta intfRemoved); 794e7efe135SRashmica Gupta /* TODO: slight race condition here. Check that the config still 795e7efe135SRashmica Gupta * exists */ 796e7efe135SRashmica Gupta this->matches.emplace_back(std::move(matchOnRemove)); 797e7efe135SRashmica Gupta } 798e7efe135SRashmica Gupta catch (std::invalid_argument& ia) 799e7efe135SRashmica Gupta { 800*82b39c66SPatrick Williams error("Failed to set up virtual sensor: {ERROR}", "ERROR", ia); 801e7efe135SRashmica Gupta } 802e7efe135SRashmica Gupta } 803e7efe135SRashmica Gupta } 804e7efe135SRashmica Gupta 805abcc94faSVijay Khemka void VirtualSensors::createVirtualSensors() 806abcc94faSVijay Khemka { 807abcc94faSVijay Khemka static const Json empty{}; 808abcc94faSVijay Khemka 809abcc94faSVijay Khemka auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE); 810e7efe135SRashmica Gupta 811abcc94faSVijay Khemka // print values 812abcc94faSVijay Khemka if (DEBUG) 813e7efe135SRashmica Gupta { 814abcc94faSVijay Khemka std::cout << "Config json data:\n" << data << "\n\n"; 815e7efe135SRashmica Gupta } 816abcc94faSVijay Khemka 817abcc94faSVijay Khemka /* Get virtual sensors config data */ 818abcc94faSVijay Khemka for (const auto& j : data) 819abcc94faSVijay Khemka { 820abcc94faSVijay Khemka auto desc = j.value("Desc", empty); 821abcc94faSVijay Khemka if (!desc.empty()) 822abcc94faSVijay Khemka { 823e7efe135SRashmica Gupta if (desc.value("Config", "") == "D-Bus") 824e7efe135SRashmica Gupta { 825e7efe135SRashmica Gupta /* Look on D-Bus for a virtual sensor config. Set up matches 826e7efe135SRashmica Gupta * first because the configs may not be on D-Bus yet and we 827e7efe135SRashmica Gupta * don't want to miss them */ 828e7efe135SRashmica Gupta setupMatches(); 829e7efe135SRashmica Gupta 830e7efe135SRashmica Gupta if (desc.contains("Type")) 831e7efe135SRashmica Gupta { 832*82b39c66SPatrick Williams auto type = desc.value("Type", ""); 833*82b39c66SPatrick Williams auto path = "xyz.openbmc_project.Configuration." + type; 834*82b39c66SPatrick Williams 835e7efe135SRashmica Gupta if (!isCalculationType(path)) 836e7efe135SRashmica Gupta { 837*82b39c66SPatrick Williams error("Invalid calculation type {TYPE} supplied.", 838*82b39c66SPatrick Williams "TYPE", type); 839e7efe135SRashmica Gupta continue; 840e7efe135SRashmica Gupta } 841e7efe135SRashmica Gupta createVirtualSensorsFromDBus(path); 842e7efe135SRashmica Gupta } 843e7efe135SRashmica Gupta continue; 844e7efe135SRashmica Gupta } 845e7efe135SRashmica Gupta 846abcc94faSVijay Khemka std::string sensorType = desc.value("SensorType", ""); 847abcc94faSVijay Khemka std::string name = desc.value("Name", ""); 848665a0a29SRashmica Gupta std::replace(name.begin(), name.end(), ' ', '_'); 849abcc94faSVijay Khemka 850abcc94faSVijay Khemka if (!name.empty() && !sensorType.empty()) 851abcc94faSVijay Khemka { 852e0d371e4SVijay Khemka if (unitMap.find(sensorType) == unitMap.end()) 853e0d371e4SVijay Khemka { 854*82b39c66SPatrick Williams error("Sensor type {TYPE} is not supported", "TYPE", 855*82b39c66SPatrick Williams sensorType); 856e0d371e4SVijay Khemka } 857e0d371e4SVijay Khemka else 858e0d371e4SVijay Khemka { 85967d8b9d2SRashmica Gupta if (virtualSensorsMap.find(name) != virtualSensorsMap.end()) 86067d8b9d2SRashmica Gupta { 861*82b39c66SPatrick Williams error("A virtual sensor named {NAME} already exists", 862*82b39c66SPatrick Williams "NAME", name); 86367d8b9d2SRashmica Gupta continue; 86467d8b9d2SRashmica Gupta } 865862c3d1eSRashmica Gupta auto objPath = sensorDbusPath + sensorType + "/" + name; 866abcc94faSVijay Khemka 86732a7156bSVijay Khemka auto virtualSensorPtr = std::make_unique<VirtualSensor>( 86832a7156bSVijay Khemka bus, objPath.c_str(), j, name); 869abcc94faSVijay Khemka 870*82b39c66SPatrick Williams info("Added a new virtual sensor: {NAME}", "NAME", name); 8713ed9a516SVijay Khemka virtualSensorPtr->updateVirtualSensor(); 872e0d371e4SVijay Khemka 873e0d371e4SVijay Khemka /* Initialize unit value for virtual sensor */ 874e0d371e4SVijay Khemka virtualSensorPtr->ValueIface::unit(unitMap[sensorType]); 875a2fa63a6SRashmica Gupta virtualSensorPtr->emit_object_added(); 876e0d371e4SVijay Khemka 8773ed9a516SVijay Khemka virtualSensorsMap.emplace(std::move(name), 8783ed9a516SVijay Khemka std::move(virtualSensorPtr)); 879abcc94faSVijay Khemka } 880e0d371e4SVijay Khemka } 881abcc94faSVijay Khemka else 882abcc94faSVijay Khemka { 883*82b39c66SPatrick Williams error( 884*82b39c66SPatrick Williams "Sensor type ({TYPE}) or name ({NAME}) not found in config file", 885*82b39c66SPatrick Williams "NAME", name, "TYPE", sensorType); 886abcc94faSVijay Khemka } 887abcc94faSVijay Khemka } 888abcc94faSVijay Khemka else 889abcc94faSVijay Khemka { 890*82b39c66SPatrick Williams error("Descriptor for new virtual sensor not found in config file"); 891abcc94faSVijay Khemka } 892abcc94faSVijay Khemka } 893abcc94faSVijay Khemka } 894abcc94faSVijay Khemka 895abcc94faSVijay Khemka } // namespace virtualSensor 896abcc94faSVijay Khemka } // namespace phosphor 897abcc94faSVijay Khemka 898abcc94faSVijay Khemka /** 899abcc94faSVijay Khemka * @brief Main 900abcc94faSVijay Khemka */ 901abcc94faSVijay Khemka int main() 902abcc94faSVijay Khemka { 903abcc94faSVijay Khemka 904abcc94faSVijay Khemka // Get a default event loop 905abcc94faSVijay Khemka auto event = sdeventplus::Event::get_default(); 906abcc94faSVijay Khemka 907abcc94faSVijay Khemka // Get a handle to system dbus 908abcc94faSVijay Khemka auto bus = sdbusplus::bus::new_default(); 909abcc94faSVijay Khemka 9106c19e7d2SMatt Spinler // Add the ObjectManager interface 9116c19e7d2SMatt Spinler sdbusplus::server::manager::manager objManager(bus, "/"); 9126c19e7d2SMatt Spinler 913abcc94faSVijay Khemka // Create an virtual sensors object 914abcc94faSVijay Khemka phosphor::virtualSensor::VirtualSensors virtualSensors(bus); 915abcc94faSVijay Khemka 916abcc94faSVijay Khemka // Request service bus name 917abcc94faSVijay Khemka bus.request_name(busName); 918abcc94faSVijay Khemka 919abcc94faSVijay Khemka // Attach the bus to sd_event to service user requests 920abcc94faSVijay Khemka bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL); 921abcc94faSVijay Khemka event.loop(); 922abcc94faSVijay Khemka 923abcc94faSVijay Khemka return 0; 924abcc94faSVijay Khemka } 925