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