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 1751f898e2SVijay Khemka int handleDbusSignal(sd_bus_message* msg, void* usrData, sd_bus_error*) 1851f898e2SVijay Khemka { 1951f898e2SVijay Khemka if (usrData == nullptr) 2051f898e2SVijay Khemka { 2151f898e2SVijay Khemka throw std::runtime_error("Invalid match"); 2251f898e2SVijay Khemka } 2351f898e2SVijay Khemka 248e11cccbSPatrick Williams auto sdbpMsg = sdbusplus::message_t(msg); 2551f898e2SVijay Khemka std::string msgIfce; 2651f898e2SVijay Khemka std::map<std::string, std::variant<int64_t, double, bool>> msgData; 2751f898e2SVijay Khemka 2851f898e2SVijay Khemka sdbpMsg.read(msgIfce, msgData); 2951f898e2SVijay Khemka 3051f898e2SVijay Khemka if (msgData.find("Value") != msgData.end()) 3151f898e2SVijay Khemka { 3251f898e2SVijay Khemka using namespace phosphor::virtualSensor; 3351f898e2SVijay Khemka VirtualSensor* obj = static_cast<VirtualSensor*>(usrData); 3451f898e2SVijay Khemka // TODO(openbmc/phosphor-virtual-sensor#1): updateVirtualSensor should 3551f898e2SVijay Khemka // be changed to take the information we got from the signal, to avoid 3651f898e2SVijay Khemka // having to do numerous dbus queries. 3751f898e2SVijay Khemka obj->updateVirtualSensor(); 3851f898e2SVijay Khemka } 3951f898e2SVijay Khemka return 0; 4051f898e2SVijay Khemka } 4151f898e2SVijay Khemka 42abcc94faSVijay Khemka namespace phosphor 43abcc94faSVijay Khemka { 44abcc94faSVijay Khemka namespace virtualSensor 45abcc94faSVijay Khemka { 46abcc94faSVijay Khemka 470ab9d838SLei YU FuncMaxIgnoreNaN<double> VirtualSensor::funcMaxIgnoreNaN; 4887d35115SLei YU FuncSumIgnoreNaN<double> VirtualSensor::funcSumIgnoreNaN; 49c77b6b3fSLei YU FuncIfNan<double> VirtualSensor::funcIfNan; 500ab9d838SLei YU 51abcc94faSVijay Khemka void printParams(const VirtualSensor::ParamMap& paramMap) 52abcc94faSVijay Khemka { 53abcc94faSVijay Khemka for (const auto& p : paramMap) 54abcc94faSVijay Khemka { 55abcc94faSVijay Khemka const auto& p1 = p.first; 56abcc94faSVijay Khemka const auto& p2 = p.second; 57abcc94faSVijay Khemka auto val = p2->getParamValue(); 58fbd7145eSPatrick Williams debug("Parameter: {PARAM} = {VALUE}", "PARAM", p1, "VALUE", val); 59abcc94faSVijay Khemka } 60abcc94faSVijay Khemka } 61abcc94faSVijay Khemka 62abcc94faSVijay Khemka double SensorParam::getParamValue() 63abcc94faSVijay Khemka { 64abcc94faSVijay Khemka switch (paramType) 65abcc94faSVijay Khemka { 66abcc94faSVijay Khemka case constParam: 67abcc94faSVijay Khemka return value; 68abcc94faSVijay Khemka break; 697452a867SVijay Khemka case dbusParam: 707452a867SVijay Khemka return dbusSensor->getSensorValue(); 717452a867SVijay Khemka break; 72abcc94faSVijay Khemka default: 73abcc94faSVijay Khemka throw std::invalid_argument("param type not supported"); 74abcc94faSVijay Khemka } 75abcc94faSVijay Khemka } 76abcc94faSVijay Khemka 770fcf0e1cSLei YU using AssociationList = 780fcf0e1cSLei YU std::vector<std::tuple<std::string, std::string, std::string>>; 790fcf0e1cSLei YU 800fcf0e1cSLei YU AssociationList getAssociationsFromJson(const Json& j) 810fcf0e1cSLei YU { 820fcf0e1cSLei YU AssociationList assocs{}; 830fcf0e1cSLei YU try 840fcf0e1cSLei YU { 850fcf0e1cSLei YU j.get_to(assocs); 860fcf0e1cSLei YU } 870fcf0e1cSLei YU catch (const std::exception& ex) 880fcf0e1cSLei YU { 8982b39c66SPatrick Williams error("Failed to parse association: {ERROR}", "ERROR", ex); 900fcf0e1cSLei YU } 910fcf0e1cSLei YU return assocs; 920fcf0e1cSLei YU } 930fcf0e1cSLei YU 94e7efe135SRashmica Gupta template <typename U> 95e7efe135SRashmica Gupta struct VariantToNumber 96e7efe135SRashmica Gupta { 97e7efe135SRashmica Gupta template <typename T> 98e7efe135SRashmica Gupta U operator()(const T& t) const 99e7efe135SRashmica Gupta { 100e7efe135SRashmica Gupta if constexpr (std::is_convertible<T, U>::value) 101e7efe135SRashmica Gupta { 102e7efe135SRashmica Gupta return static_cast<U>(t); 103e7efe135SRashmica Gupta } 104e7efe135SRashmica Gupta throw std::invalid_argument("Invalid number type in config\n"); 105e7efe135SRashmica Gupta } 106e7efe135SRashmica Gupta }; 107e7efe135SRashmica Gupta 108e7efe135SRashmica Gupta template <typename U> 109e7efe135SRashmica Gupta U getNumberFromConfig(const PropertyMap& map, const std::string& name, 110190f6d06SJiaqing Zhao bool required, 111190f6d06SJiaqing Zhao U defaultValue = std::numeric_limits<U>::quiet_NaN()) 112e7efe135SRashmica Gupta { 113e7efe135SRashmica Gupta if (auto itr = map.find(name); itr != map.end()) 114e7efe135SRashmica Gupta { 115e7efe135SRashmica Gupta return std::visit(VariantToNumber<U>(), itr->second); 116e7efe135SRashmica Gupta } 117e7efe135SRashmica Gupta else if (required) 118e7efe135SRashmica Gupta { 11982b39c66SPatrick Williams error("Required field {NAME} missing in config", "NAME", name); 120e7efe135SRashmica Gupta throw std::invalid_argument("Required field missing in config"); 121e7efe135SRashmica Gupta } 122190f6d06SJiaqing Zhao return defaultValue; 123e7efe135SRashmica Gupta } 124e7efe135SRashmica Gupta 125e7efe135SRashmica Gupta bool isCalculationType(const std::string& interface) 126e7efe135SRashmica Gupta { 127e7efe135SRashmica Gupta auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(), 128e7efe135SRashmica Gupta interface); 129e7efe135SRashmica Gupta if (itr != calculationIfaces.end()) 130e7efe135SRashmica Gupta { 131e7efe135SRashmica Gupta return true; 132e7efe135SRashmica Gupta } 133e7efe135SRashmica Gupta return false; 134e7efe135SRashmica Gupta } 135e7efe135SRashmica Gupta 136e7efe135SRashmica Gupta const std::string getThresholdType(const std::string& direction, 13705b1d417SRashmica Gupta const std::string& severity) 138e7efe135SRashmica Gupta { 139e7efe135SRashmica Gupta std::string suffix; 140e7efe135SRashmica Gupta 141e7efe135SRashmica Gupta if (direction == "less than") 142e7efe135SRashmica Gupta { 143e7efe135SRashmica Gupta suffix = "Low"; 144e7efe135SRashmica Gupta } 145e7efe135SRashmica Gupta else if (direction == "greater than") 146e7efe135SRashmica Gupta { 147e7efe135SRashmica Gupta suffix = "High"; 148e7efe135SRashmica Gupta } 149e7efe135SRashmica Gupta else 150e7efe135SRashmica Gupta { 151e7efe135SRashmica Gupta throw std::invalid_argument( 152e7efe135SRashmica Gupta "Invalid threshold direction specified in entity manager"); 153e7efe135SRashmica Gupta } 15405b1d417SRashmica Gupta return severity + suffix; 15505b1d417SRashmica Gupta } 15605b1d417SRashmica Gupta 15705b1d417SRashmica Gupta std::string getSeverityField(const PropertyMap& propertyMap) 15805b1d417SRashmica Gupta { 15905b1d417SRashmica Gupta static const std::array thresholdTypes{"Warning", "Critical", 16005b1d417SRashmica Gupta "PerformanceLoss", "SoftShutdown", 16105b1d417SRashmica Gupta "HardShutdown"}; 16205b1d417SRashmica Gupta 16305b1d417SRashmica Gupta std::string severity; 16405b1d417SRashmica Gupta if (auto itr = propertyMap.find("Severity"); itr != propertyMap.end()) 16505b1d417SRashmica Gupta { 16605b1d417SRashmica Gupta /* Severity should be a string, but can be an unsigned int */ 16705b1d417SRashmica Gupta if (std::holds_alternative<std::string>(itr->second)) 16805b1d417SRashmica Gupta { 16905b1d417SRashmica Gupta severity = std::get<std::string>(itr->second); 17005b1d417SRashmica Gupta if (0 == std::ranges::count(thresholdTypes, severity)) 17105b1d417SRashmica Gupta { 17205b1d417SRashmica Gupta throw std::invalid_argument( 17305b1d417SRashmica Gupta "Invalid threshold severity specified in entity manager"); 17405b1d417SRashmica Gupta } 17505b1d417SRashmica Gupta } 17605b1d417SRashmica Gupta else 17705b1d417SRashmica Gupta { 1781226f208SPatrick Williams auto sev = getNumberFromConfig<uint64_t>(propertyMap, "Severity", 1791226f208SPatrick Williams true); 18005b1d417SRashmica Gupta /* Checking bounds ourselves so we throw invalid argument on 18105b1d417SRashmica Gupta * invalid user input */ 18205b1d417SRashmica Gupta if (sev >= thresholdTypes.size()) 18305b1d417SRashmica Gupta { 18405b1d417SRashmica Gupta throw std::invalid_argument( 18505b1d417SRashmica Gupta "Invalid threshold severity specified in entity manager"); 18605b1d417SRashmica Gupta } 18705b1d417SRashmica Gupta severity = thresholdTypes.at(sev); 18805b1d417SRashmica Gupta } 18905b1d417SRashmica Gupta } 19005b1d417SRashmica Gupta return severity; 191e7efe135SRashmica Gupta } 192e7efe135SRashmica Gupta 19391799dbdSTao Lin void parseThresholds(Json& thresholds, const PropertyMap& propertyMap, 19491799dbdSTao Lin const std::string& entityInterface = "") 195e7efe135SRashmica Gupta { 196e7efe135SRashmica Gupta std::string direction; 197e7efe135SRashmica Gupta 198e7efe135SRashmica Gupta auto value = getNumberFromConfig<double>(propertyMap, "Value", true); 199e7efe135SRashmica Gupta 20005b1d417SRashmica Gupta auto severity = getSeverityField(propertyMap); 20105b1d417SRashmica Gupta 20205b1d417SRashmica Gupta if (auto itr = propertyMap.find("Direction"); itr != propertyMap.end()) 203e7efe135SRashmica Gupta { 204e7efe135SRashmica Gupta direction = std::get<std::string>(itr->second); 205e7efe135SRashmica Gupta } 206e7efe135SRashmica Gupta 207e7efe135SRashmica Gupta auto threshold = getThresholdType(direction, severity); 208e7efe135SRashmica Gupta thresholds[threshold] = value; 2091dff7dceSRashmica Gupta 2101226f208SPatrick Williams auto hysteresis = getNumberFromConfig<double>(propertyMap, "Hysteresis", 2111226f208SPatrick Williams false); 2121dff7dceSRashmica Gupta if (hysteresis != std::numeric_limits<double>::quiet_NaN()) 2131dff7dceSRashmica Gupta { 2141dff7dceSRashmica Gupta thresholds[threshold + "Hysteresis"] = hysteresis; 2151dff7dceSRashmica Gupta } 21691799dbdSTao Lin 21791799dbdSTao Lin if (!entityInterface.empty()) 21891799dbdSTao Lin { 21991799dbdSTao Lin thresholds[threshold + "Direction"] = entityInterface; 22091799dbdSTao Lin } 221e7efe135SRashmica Gupta } 222e7efe135SRashmica Gupta 223e7efe135SRashmica Gupta void VirtualSensor::parseConfigInterface(const PropertyMap& propertyMap, 224e7efe135SRashmica Gupta const std::string& sensorType, 225e7efe135SRashmica Gupta const std::string& interface) 226e7efe135SRashmica Gupta { 227e7efe135SRashmica Gupta /* Parse sensors / DBus params */ 228e7efe135SRashmica Gupta if (auto itr = propertyMap.find("Sensors"); itr != propertyMap.end()) 229e7efe135SRashmica Gupta { 230e7efe135SRashmica Gupta auto sensors = std::get<std::vector<std::string>>(itr->second); 231e7efe135SRashmica Gupta for (auto sensor : sensors) 232e7efe135SRashmica Gupta { 233e7efe135SRashmica Gupta std::replace(sensor.begin(), sensor.end(), ' ', '_'); 234e7efe135SRashmica Gupta auto sensorObjPath = sensorDbusPath + sensorType + "/" + sensor; 235e7efe135SRashmica Gupta 2361226f208SPatrick Williams auto paramPtr = std::make_unique<SensorParam>(bus, sensorObjPath, 2371226f208SPatrick Williams this); 238e7efe135SRashmica Gupta symbols.create_variable(sensor); 239e7efe135SRashmica Gupta paramMap.emplace(std::move(sensor), std::move(paramPtr)); 240e7efe135SRashmica Gupta } 241e7efe135SRashmica Gupta } 242e7efe135SRashmica Gupta /* Get expression string */ 243e7efe135SRashmica Gupta if (!isCalculationType(interface)) 244e7efe135SRashmica Gupta { 245e7efe135SRashmica Gupta throw std::invalid_argument("Invalid expression in interface"); 246e7efe135SRashmica Gupta } 247e7efe135SRashmica Gupta exprStr = interface; 248e7efe135SRashmica Gupta 249e7efe135SRashmica Gupta /* Get optional min and max input and output values */ 250e7efe135SRashmica Gupta ValueIface::maxValue( 251e7efe135SRashmica Gupta getNumberFromConfig<double>(propertyMap, "MaxValue", false)); 252e7efe135SRashmica Gupta ValueIface::minValue( 253e7efe135SRashmica Gupta getNumberFromConfig<double>(propertyMap, "MinValue", false)); 254e7efe135SRashmica Gupta maxValidInput = 255190f6d06SJiaqing Zhao getNumberFromConfig<double>(propertyMap, "MaxValidInput", false, 256190f6d06SJiaqing Zhao std::numeric_limits<double>::infinity()); 257e7efe135SRashmica Gupta minValidInput = 258190f6d06SJiaqing Zhao getNumberFromConfig<double>(propertyMap, "MinValidInput", false, 259190f6d06SJiaqing Zhao -std::numeric_limits<double>::infinity()); 260e7efe135SRashmica Gupta } 261e7efe135SRashmica Gupta 262ce675228SMatt Spinler void VirtualSensor::initVirtualSensor(const Json& sensorConfig, 263ce675228SMatt Spinler const std::string& objPath) 264abcc94faSVijay Khemka { 265abcc94faSVijay Khemka static const Json empty{}; 266abcc94faSVijay Khemka 267abcc94faSVijay Khemka /* Get threshold values if defined in config */ 268abcc94faSVijay Khemka auto threshold = sensorConfig.value("Threshold", empty); 269f15189e3SMatt Spinler 2703e99919bSRashmica Gupta createThresholds(threshold, objPath); 271abcc94faSVijay Khemka 272f6443742SHarvey Wu /* Get MaxValue, MinValue setting if defined in config */ 273f6443742SHarvey Wu auto confDesc = sensorConfig.value("Desc", empty); 274f6443742SHarvey Wu if (auto maxConf = confDesc.find("MaxValue"); 275f6443742SHarvey Wu maxConf != confDesc.end() && maxConf->is_number()) 276f6443742SHarvey Wu { 277f6443742SHarvey Wu ValueIface::maxValue(maxConf->get<double>()); 278f6443742SHarvey Wu } 279f6443742SHarvey Wu if (auto minConf = confDesc.find("MinValue"); 280f6443742SHarvey Wu minConf != confDesc.end() && minConf->is_number()) 281f6443742SHarvey Wu { 282f6443742SHarvey Wu ValueIface::minValue(minConf->get<double>()); 283f6443742SHarvey Wu } 284f6443742SHarvey Wu 2850fcf0e1cSLei YU /* Get optional association */ 2860fcf0e1cSLei YU auto assocJson = sensorConfig.value("Associations", empty); 2870fcf0e1cSLei YU if (!assocJson.empty()) 2880fcf0e1cSLei YU { 2890fcf0e1cSLei YU auto assocs = getAssociationsFromJson(assocJson); 2900fcf0e1cSLei YU if (!assocs.empty()) 2910fcf0e1cSLei YU { 2920fcf0e1cSLei YU associationIface = 2930fcf0e1cSLei YU std::make_unique<AssociationObject>(bus, objPath.c_str()); 2940fcf0e1cSLei YU associationIface->associations(assocs); 2950fcf0e1cSLei YU } 2960fcf0e1cSLei YU } 2970fcf0e1cSLei YU 298abcc94faSVijay Khemka /* Get expression string */ 29903c4c8e2SPatrick Williams static constexpr auto exprKey = "Expression"; 30003c4c8e2SPatrick Williams if (sensorConfig.contains(exprKey)) 30103c4c8e2SPatrick Williams { 302a959678cSPatrick Williams auto& ref = sensorConfig.at(exprKey); 30303c4c8e2SPatrick Williams if (ref.is_array()) 30403c4c8e2SPatrick Williams { 30503c4c8e2SPatrick Williams exprStr = std::string{}; 30603c4c8e2SPatrick Williams for (auto& s : ref) 30703c4c8e2SPatrick Williams { 30803c4c8e2SPatrick Williams exprStr += s; 30903c4c8e2SPatrick Williams } 31003c4c8e2SPatrick Williams } 31103c4c8e2SPatrick Williams else if (ref.is_string()) 31203c4c8e2SPatrick Williams { 31303c4c8e2SPatrick Williams exprStr = std::string{ref}; 31403c4c8e2SPatrick Williams } 31503c4c8e2SPatrick Williams } 316abcc94faSVijay Khemka 317abcc94faSVijay Khemka /* Get all the parameter listed in configuration */ 318abcc94faSVijay Khemka auto params = sensorConfig.value("Params", empty); 319abcc94faSVijay Khemka 320abcc94faSVijay Khemka /* Check for constant parameter */ 321abcc94faSVijay Khemka const auto& consParams = params.value("ConstParam", empty); 322abcc94faSVijay Khemka if (!consParams.empty()) 323abcc94faSVijay Khemka { 324abcc94faSVijay Khemka for (auto& j : consParams) 325abcc94faSVijay Khemka { 326abcc94faSVijay Khemka if (j.find("ParamName") != j.end()) 327abcc94faSVijay Khemka { 328abcc94faSVijay Khemka auto paramPtr = std::make_unique<SensorParam>(j["Value"]); 3293ed9a516SVijay Khemka std::string name = j["ParamName"]; 3303ed9a516SVijay Khemka symbols.create_variable(name); 3313ed9a516SVijay Khemka paramMap.emplace(std::move(name), std::move(paramPtr)); 332abcc94faSVijay Khemka } 333abcc94faSVijay Khemka else 334abcc94faSVijay Khemka { 335abcc94faSVijay Khemka /* Invalid configuration */ 336abcc94faSVijay Khemka throw std::invalid_argument( 337abcc94faSVijay Khemka "ParamName not found in configuration"); 338abcc94faSVijay Khemka } 339abcc94faSVijay Khemka } 340abcc94faSVijay Khemka } 341abcc94faSVijay Khemka 3427452a867SVijay Khemka /* Check for dbus parameter */ 3437452a867SVijay Khemka auto dbusParams = params.value("DbusParam", empty); 3447452a867SVijay Khemka if (!dbusParams.empty()) 3457452a867SVijay Khemka { 3467452a867SVijay Khemka for (auto& j : dbusParams) 3477452a867SVijay Khemka { 3487452a867SVijay Khemka /* Get parameter dbus sensor descriptor */ 3497452a867SVijay Khemka auto desc = j.value("Desc", empty); 3507452a867SVijay Khemka if ((!desc.empty()) && (j.find("ParamName") != j.end())) 3517452a867SVijay Khemka { 3527452a867SVijay Khemka std::string sensorType = desc.value("SensorType", ""); 3537452a867SVijay Khemka std::string name = desc.value("Name", ""); 3547452a867SVijay Khemka 3557452a867SVijay Khemka if (!sensorType.empty() && !name.empty()) 3567452a867SVijay Khemka { 3571204b433SGeorge Liu auto path = sensorDbusPath + sensorType + "/" + name; 3587452a867SVijay Khemka 3591226f208SPatrick Williams auto paramPtr = std::make_unique<SensorParam>(bus, path, 3601226f208SPatrick Williams this); 3611204b433SGeorge Liu std::string paramName = j["ParamName"]; 3621204b433SGeorge Liu symbols.create_variable(paramName); 3631204b433SGeorge Liu paramMap.emplace(std::move(paramName), std::move(paramPtr)); 3647452a867SVijay Khemka } 3657452a867SVijay Khemka } 3667452a867SVijay Khemka } 3677452a867SVijay Khemka } 368abcc94faSVijay Khemka 3693ed9a516SVijay Khemka symbols.add_constants(); 3709f1ef4f5SMatt Spinler symbols.add_package(vecopsPackage); 3710ab9d838SLei YU symbols.add_function("maxIgnoreNaN", funcMaxIgnoreNaN); 37287d35115SLei YU symbols.add_function("sumIgnoreNaN", funcSumIgnoreNaN); 373c77b6b3fSLei YU symbols.add_function("ifNan", funcIfNan); 3740ab9d838SLei YU 3753ed9a516SVijay Khemka expression.register_symbol_table(symbols); 3763ed9a516SVijay Khemka 3773ed9a516SVijay Khemka /* parser from exprtk */ 3783ed9a516SVijay Khemka exprtk::parser<double> parser{}; 379ddc6dcd6SMatt Spinler if (!parser.compile(exprStr, expression)) 380ddc6dcd6SMatt Spinler { 38182b39c66SPatrick Williams error("Expression compilation failed"); 382ddc6dcd6SMatt Spinler 383ddc6dcd6SMatt Spinler for (std::size_t i = 0; i < parser.error_count(); ++i) 384ddc6dcd6SMatt Spinler { 38582b39c66SPatrick Williams auto err = parser.get_error(i); 38682b39c66SPatrick Williams error("Error parsing token at {POSITION}: {ERROR}", "POSITION", 38782b39c66SPatrick Williams err.token.position, "TYPE", 38882b39c66SPatrick Williams exprtk::parser_error::to_str(err.mode), "ERROR", 38982b39c66SPatrick Williams err.diagnostic); 390ddc6dcd6SMatt Spinler } 391ddc6dcd6SMatt Spinler throw std::runtime_error("Expression compilation failed"); 392ddc6dcd6SMatt Spinler } 3933ed9a516SVijay Khemka 394abcc94faSVijay Khemka /* Print all parameters for debug purpose only */ 395abcc94faSVijay Khemka if (DEBUG) 396abcc94faSVijay Khemka printParams(paramMap); 397abcc94faSVijay Khemka } 398abcc94faSVijay Khemka 399dc777015STao Lin void VirtualSensor::createAssociation(const std::string& objPath, 400dc777015STao Lin const std::string& entityPath) 401dc777015STao Lin { 402dc777015STao Lin if (objPath.empty() || entityPath.empty()) 403dc777015STao Lin { 404dc777015STao Lin return; 405dc777015STao Lin } 406dc777015STao Lin 407dc777015STao Lin std::filesystem::path p(entityPath); 408dc777015STao Lin auto assocsDbus = 409dc777015STao Lin AssociationList{{"chassis", "all_sensors", p.parent_path().string()}}; 4101226f208SPatrick Williams associationIface = std::make_unique<AssociationObject>(bus, 4111226f208SPatrick Williams objPath.c_str()); 412dc777015STao Lin associationIface->associations(assocsDbus); 413dc777015STao Lin } 414dc777015STao Lin 415e7efe135SRashmica Gupta void VirtualSensor::initVirtualSensor(const InterfaceMap& interfaceMap, 416e7efe135SRashmica Gupta const std::string& objPath, 417e7efe135SRashmica Gupta const std::string& sensorType, 418e7efe135SRashmica Gupta const std::string& calculationIface) 419e7efe135SRashmica Gupta { 420e7efe135SRashmica Gupta Json thresholds; 4211226f208SPatrick Williams const std::string vsThresholdsIntf = calculationIface + 4221226f208SPatrick Williams vsThresholdsIfaceSuffix; 423e7efe135SRashmica Gupta 424e7efe135SRashmica Gupta for (const auto& [interface, propertyMap] : interfaceMap) 425e7efe135SRashmica Gupta { 426e7efe135SRashmica Gupta /* Each threshold is on it's own interface with a number as a suffix 427e7efe135SRashmica Gupta * eg xyz.openbmc_project.Configuration.ModifiedMedian.Thresholds1 */ 428e7efe135SRashmica Gupta if (interface.find(vsThresholdsIntf) != std::string::npos) 429e7efe135SRashmica Gupta { 43091799dbdSTao Lin parseThresholds(thresholds, propertyMap, interface); 431e7efe135SRashmica Gupta } 432e7efe135SRashmica Gupta else if (interface == calculationIface) 433e7efe135SRashmica Gupta { 434e7efe135SRashmica Gupta parseConfigInterface(propertyMap, sensorType, interface); 435e7efe135SRashmica Gupta } 436e7efe135SRashmica Gupta } 437e7efe135SRashmica Gupta 438e7efe135SRashmica Gupta createThresholds(thresholds, objPath); 439e7efe135SRashmica Gupta symbols.add_constants(); 440e7efe135SRashmica Gupta symbols.add_package(vecopsPackage); 441e7efe135SRashmica Gupta expression.register_symbol_table(symbols); 442e7efe135SRashmica Gupta 443dc777015STao Lin createAssociation(objPath, entityPath); 444e7efe135SRashmica Gupta /* Print all parameters for debug purpose only */ 445e7efe135SRashmica Gupta if (DEBUG) 446e7efe135SRashmica Gupta { 447e7efe135SRashmica Gupta printParams(paramMap); 448e7efe135SRashmica Gupta } 449e7efe135SRashmica Gupta } 450e7efe135SRashmica Gupta 451abcc94faSVijay Khemka void VirtualSensor::setSensorValue(double value) 452abcc94faSVijay Khemka { 453543bf668SPatrick Williams value = std::clamp(value, ValueIface::minValue(), ValueIface::maxValue()); 454abcc94faSVijay Khemka ValueIface::value(value); 455abcc94faSVijay Khemka } 456abcc94faSVijay Khemka 457304fd0e4SRashmica Gupta double VirtualSensor::calculateValue(const std::string& calculation, 458304fd0e4SRashmica Gupta const VirtualSensor::ParamMap& paramMap) 459e7efe135SRashmica Gupta { 460304fd0e4SRashmica Gupta auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(), 461304fd0e4SRashmica Gupta calculation); 462304fd0e4SRashmica Gupta if (itr == calculationIfaces.end()) 463304fd0e4SRashmica Gupta { 464e7efe135SRashmica Gupta return std::numeric_limits<double>::quiet_NaN(); 465e7efe135SRashmica Gupta } 466304fd0e4SRashmica Gupta else if (calculation == "xyz.openbmc_project.Configuration.ModifiedMedian") 467304fd0e4SRashmica Gupta { 468304fd0e4SRashmica Gupta return calculateModifiedMedianValue(paramMap); 469304fd0e4SRashmica Gupta } 470f6b7e0a4STao Lin else if (calculation == "xyz.openbmc_project.Configuration.Maximum") 471f6b7e0a4STao Lin { 472f6b7e0a4STao Lin return calculateMaximumValue(paramMap); 473f6b7e0a4STao Lin } 474304fd0e4SRashmica Gupta return std::numeric_limits<double>::quiet_NaN(); 475304fd0e4SRashmica Gupta } 476304fd0e4SRashmica Gupta 477304fd0e4SRashmica Gupta bool VirtualSensor::sensorInRange(double value) 478304fd0e4SRashmica Gupta { 479304fd0e4SRashmica Gupta if (value <= this->maxValidInput && value >= this->minValidInput) 480304fd0e4SRashmica Gupta { 481304fd0e4SRashmica Gupta return true; 482304fd0e4SRashmica Gupta } 483304fd0e4SRashmica Gupta return false; 484304fd0e4SRashmica Gupta } 485e7efe135SRashmica Gupta 486abcc94faSVijay Khemka void VirtualSensor::updateVirtualSensor() 4873ed9a516SVijay Khemka { 4883ed9a516SVijay Khemka for (auto& param : paramMap) 4893ed9a516SVijay Khemka { 4903ed9a516SVijay Khemka auto& name = param.first; 4913ed9a516SVijay Khemka auto& data = param.second; 4923ed9a516SVijay Khemka if (auto var = symbols.get_variable(name)) 4933ed9a516SVijay Khemka { 4943ed9a516SVijay Khemka var->ref() = data->getParamValue(); 4953ed9a516SVijay Khemka } 4963ed9a516SVijay Khemka else 4973ed9a516SVijay Khemka { 4983ed9a516SVijay Khemka /* Invalid parameter */ 4993ed9a516SVijay Khemka throw std::invalid_argument("ParamName not found in symbols"); 5003ed9a516SVijay Khemka } 5013ed9a516SVijay Khemka } 5021226f208SPatrick Williams auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(), 5031226f208SPatrick Williams exprStr); 504304fd0e4SRashmica Gupta auto val = (itr == calculationIfaces.end()) 505304fd0e4SRashmica Gupta ? expression.value() 506304fd0e4SRashmica Gupta : calculateValue(exprStr, paramMap); 50732a7156bSVijay Khemka 50832a7156bSVijay Khemka /* Set sensor value to dbus interface */ 5093ed9a516SVijay Khemka setSensorValue(val); 51032a7156bSVijay Khemka 5113ed9a516SVijay Khemka if (DEBUG) 512e7efe135SRashmica Gupta { 513fbd7145eSPatrick Williams debug("Sensor {NAME} = {VALUE}", "NAME", this->name, "VALUE", val); 514e7efe135SRashmica Gupta } 51532a7156bSVijay Khemka 5168f5e6119SMatt Spinler /* Check sensor thresholds and log required message */ 517b306b03dSMatt Spinler checkThresholds(val, perfLossIface); 518fdb826d5SPatrick Williams checkThresholds(val, warningIface); 519fdb826d5SPatrick Williams checkThresholds(val, criticalIface); 520fdb826d5SPatrick Williams checkThresholds(val, softShutdownIface); 521fdb826d5SPatrick Williams checkThresholds(val, hardShutdownIface); 5223ed9a516SVijay Khemka } 523abcc94faSVijay Khemka 524304fd0e4SRashmica Gupta double VirtualSensor::calculateModifiedMedianValue( 525304fd0e4SRashmica Gupta const VirtualSensor::ParamMap& paramMap) 526304fd0e4SRashmica Gupta { 527304fd0e4SRashmica Gupta std::vector<double> values; 528304fd0e4SRashmica Gupta 529304fd0e4SRashmica Gupta for (auto& param : paramMap) 530304fd0e4SRashmica Gupta { 531304fd0e4SRashmica Gupta auto& name = param.first; 532304fd0e4SRashmica Gupta if (auto var = symbols.get_variable(name)) 533304fd0e4SRashmica Gupta { 534304fd0e4SRashmica Gupta if (!sensorInRange(var->ref())) 535304fd0e4SRashmica Gupta { 536304fd0e4SRashmica Gupta continue; 537304fd0e4SRashmica Gupta } 538304fd0e4SRashmica Gupta values.push_back(var->ref()); 539304fd0e4SRashmica Gupta } 540304fd0e4SRashmica Gupta } 541304fd0e4SRashmica Gupta 542304fd0e4SRashmica Gupta size_t size = values.size(); 543304fd0e4SRashmica Gupta std::sort(values.begin(), values.end()); 544304fd0e4SRashmica Gupta switch (size) 545304fd0e4SRashmica Gupta { 546304fd0e4SRashmica Gupta case 2: 547304fd0e4SRashmica Gupta /* Choose biggest value */ 548304fd0e4SRashmica Gupta return values.at(1); 549304fd0e4SRashmica Gupta case 0: 550304fd0e4SRashmica Gupta return std::numeric_limits<double>::quiet_NaN(); 551304fd0e4SRashmica Gupta default: 552304fd0e4SRashmica Gupta /* Choose median value */ 553304fd0e4SRashmica Gupta if (size % 2 == 0) 554304fd0e4SRashmica Gupta { 555304fd0e4SRashmica Gupta // Average of the two middle values 556304fd0e4SRashmica Gupta return (values.at(size / 2) + values.at(size / 2 - 1)) / 2; 557304fd0e4SRashmica Gupta } 558304fd0e4SRashmica Gupta else 559304fd0e4SRashmica Gupta { 560304fd0e4SRashmica Gupta return values.at((size - 1) / 2); 561304fd0e4SRashmica Gupta } 562304fd0e4SRashmica Gupta } 563304fd0e4SRashmica Gupta } 564304fd0e4SRashmica Gupta 565f6b7e0a4STao Lin double VirtualSensor::calculateMaximumValue( 566f6b7e0a4STao Lin const VirtualSensor::ParamMap& paramMap) 567f6b7e0a4STao Lin { 568f6b7e0a4STao Lin std::vector<double> values; 569f6b7e0a4STao Lin 570f6b7e0a4STao Lin for (auto& param : paramMap) 571f6b7e0a4STao Lin { 572f6b7e0a4STao Lin auto& name = param.first; 573f6b7e0a4STao Lin if (auto var = symbols.get_variable(name)) 574f6b7e0a4STao Lin { 575f6b7e0a4STao Lin if (!sensorInRange(var->ref())) 576f6b7e0a4STao Lin { 577f6b7e0a4STao Lin continue; 578f6b7e0a4STao Lin } 579f6b7e0a4STao Lin values.push_back(var->ref()); 580f6b7e0a4STao Lin } 581f6b7e0a4STao Lin } 582f6b7e0a4STao Lin auto maxIt = std::max_element(values.begin(), values.end()); 583f6b7e0a4STao Lin if (maxIt == values.end()) 584f6b7e0a4STao Lin { 585f6b7e0a4STao Lin return std::numeric_limits<double>::quiet_NaN(); 586f6b7e0a4STao Lin } 587f6b7e0a4STao Lin return *maxIt; 588f6b7e0a4STao Lin } 589f6b7e0a4STao Lin 5903e99919bSRashmica Gupta void VirtualSensor::createThresholds(const Json& threshold, 5913e99919bSRashmica Gupta const std::string& objPath) 5923e99919bSRashmica Gupta { 5933e99919bSRashmica Gupta if (threshold.empty()) 5943e99919bSRashmica Gupta { 5953e99919bSRashmica Gupta return; 5963e99919bSRashmica Gupta } 5973e99919bSRashmica Gupta // Only create the threshold interfaces if 5983e99919bSRashmica Gupta // at least one of their values is present. 5993e99919bSRashmica Gupta if (threshold.contains("CriticalHigh") || threshold.contains("CriticalLow")) 6003e99919bSRashmica Gupta { 6013e99919bSRashmica Gupta criticalIface = 6023e99919bSRashmica Gupta std::make_unique<Threshold<CriticalObject>>(bus, objPath.c_str()); 6033e99919bSRashmica Gupta 60491799dbdSTao Lin if (threshold.contains("CriticalHigh")) 60591799dbdSTao Lin { 60691799dbdSTao Lin criticalIface->setEntityInterfaceHigh( 60791799dbdSTao Lin threshold.value("CriticalHighDirection", "")); 60891799dbdSTao Lin if (DEBUG) 60991799dbdSTao Lin { 61091799dbdSTao Lin debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath, 61191799dbdSTao Lin "INTF", threshold.value("CriticalHighDirection", "")); 61291799dbdSTao Lin } 61391799dbdSTao Lin } 61491799dbdSTao Lin if (threshold.contains("CriticalLow")) 61591799dbdSTao Lin { 61691799dbdSTao Lin criticalIface->setEntityInterfaceLow( 61791799dbdSTao Lin threshold.value("CriticalLowDirection", "")); 61891799dbdSTao Lin if (DEBUG) 61991799dbdSTao Lin { 62091799dbdSTao Lin debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath, 62191799dbdSTao Lin "INTF", threshold.value("CriticalLowDirection", "")); 62291799dbdSTao Lin } 62391799dbdSTao Lin } 62491799dbdSTao Lin 62591799dbdSTao Lin criticalIface->setEntityPath(entityPath); 62691799dbdSTao Lin if (DEBUG) 62791799dbdSTao Lin { 62891799dbdSTao Lin debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath, 62991799dbdSTao Lin "PATH", entityPath); 63091799dbdSTao Lin } 631a291ce1aSMatt Spinler 632a291ce1aSMatt Spinler criticalIface->criticalHigh(threshold.value( 633a291ce1aSMatt Spinler "CriticalHigh", std::numeric_limits<double>::quiet_NaN())); 634a291ce1aSMatt Spinler criticalIface->criticalLow(threshold.value( 635a291ce1aSMatt Spinler "CriticalLow", std::numeric_limits<double>::quiet_NaN())); 636a291ce1aSMatt Spinler criticalIface->setHighHysteresis( 637a291ce1aSMatt Spinler threshold.value("CriticalHighHysteresis", defaultHysteresis)); 638a291ce1aSMatt Spinler criticalIface->setLowHysteresis( 639a291ce1aSMatt Spinler threshold.value("CriticalLowHysteresis", defaultHysteresis)); 6403e99919bSRashmica Gupta } 6413e99919bSRashmica Gupta 6423e99919bSRashmica Gupta if (threshold.contains("WarningHigh") || threshold.contains("WarningLow")) 6433e99919bSRashmica Gupta { 6443e99919bSRashmica Gupta warningIface = 6453e99919bSRashmica Gupta std::make_unique<Threshold<WarningObject>>(bus, objPath.c_str()); 6463e99919bSRashmica Gupta 64791799dbdSTao Lin if (threshold.contains("WarningHigh")) 64891799dbdSTao Lin { 64991799dbdSTao Lin warningIface->setEntityInterfaceHigh( 65091799dbdSTao Lin threshold.value("WarningHighDirection", "")); 65191799dbdSTao Lin if (DEBUG) 65291799dbdSTao Lin { 65391799dbdSTao Lin debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath, 65491799dbdSTao Lin "INTF", threshold.value("WarningHighDirection", "")); 65591799dbdSTao Lin } 65691799dbdSTao Lin } 65791799dbdSTao Lin if (threshold.contains("WarningLow")) 65891799dbdSTao Lin { 65991799dbdSTao Lin warningIface->setEntityInterfaceLow( 66091799dbdSTao Lin threshold.value("WarningLowDirection", "")); 66191799dbdSTao Lin if (DEBUG) 66291799dbdSTao Lin { 66391799dbdSTao Lin debug("Sensor Threshold:{NAME} = intf:{INTF}", "NAME", objPath, 66491799dbdSTao Lin "INTF", threshold.value("WarningLowDirection", "")); 66591799dbdSTao Lin } 66691799dbdSTao Lin } 66791799dbdSTao Lin 66891799dbdSTao Lin warningIface->setEntityPath(entityPath); 66991799dbdSTao Lin if (DEBUG) 67091799dbdSTao Lin { 67191799dbdSTao Lin debug("Sensor Threshold:{NAME} = path:{PATH}", "NAME", objPath, 67291799dbdSTao Lin "PATH", entityPath); 67391799dbdSTao Lin } 674a291ce1aSMatt Spinler 675a291ce1aSMatt Spinler warningIface->warningHigh(threshold.value( 676a291ce1aSMatt Spinler "WarningHigh", std::numeric_limits<double>::quiet_NaN())); 677a291ce1aSMatt Spinler warningIface->warningLow(threshold.value( 678a291ce1aSMatt Spinler "WarningLow", std::numeric_limits<double>::quiet_NaN())); 679a291ce1aSMatt Spinler warningIface->setHighHysteresis( 680a291ce1aSMatt Spinler threshold.value("WarningHighHysteresis", defaultHysteresis)); 681a291ce1aSMatt Spinler warningIface->setLowHysteresis( 682a291ce1aSMatt Spinler threshold.value("WarningLowHysteresis", defaultHysteresis)); 6833e99919bSRashmica Gupta } 6843e99919bSRashmica Gupta 6853e99919bSRashmica Gupta if (threshold.contains("HardShutdownHigh") || 6863e99919bSRashmica Gupta threshold.contains("HardShutdownLow")) 6873e99919bSRashmica Gupta { 6883e99919bSRashmica Gupta hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>( 6893e99919bSRashmica Gupta bus, objPath.c_str()); 6903e99919bSRashmica Gupta 6913e99919bSRashmica Gupta hardShutdownIface->hardShutdownHigh(threshold.value( 6923e99919bSRashmica Gupta "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN())); 6933e99919bSRashmica Gupta hardShutdownIface->hardShutdownLow(threshold.value( 6943e99919bSRashmica Gupta "HardShutdownLow", std::numeric_limits<double>::quiet_NaN())); 6951dff7dceSRashmica Gupta hardShutdownIface->setHighHysteresis( 6961dff7dceSRashmica Gupta threshold.value("HardShutdownHighHysteresis", defaultHysteresis)); 6971dff7dceSRashmica Gupta hardShutdownIface->setLowHysteresis( 6981dff7dceSRashmica Gupta threshold.value("HardShutdownLowHysteresis", defaultHysteresis)); 6993e99919bSRashmica Gupta } 7003e99919bSRashmica Gupta 7013e99919bSRashmica Gupta if (threshold.contains("SoftShutdownHigh") || 7023e99919bSRashmica Gupta threshold.contains("SoftShutdownLow")) 7033e99919bSRashmica Gupta { 7043e99919bSRashmica Gupta softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>( 7053e99919bSRashmica Gupta bus, objPath.c_str()); 7063e99919bSRashmica Gupta 7073e99919bSRashmica Gupta softShutdownIface->softShutdownHigh(threshold.value( 7083e99919bSRashmica Gupta "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN())); 7093e99919bSRashmica Gupta softShutdownIface->softShutdownLow(threshold.value( 7103e99919bSRashmica Gupta "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN())); 7111dff7dceSRashmica Gupta softShutdownIface->setHighHysteresis( 7121dff7dceSRashmica Gupta threshold.value("SoftShutdownHighHysteresis", defaultHysteresis)); 7131dff7dceSRashmica Gupta softShutdownIface->setLowHysteresis( 7141dff7dceSRashmica Gupta threshold.value("SoftShutdownLowHysteresis", defaultHysteresis)); 7153e99919bSRashmica Gupta } 7163e99919bSRashmica Gupta 7173e99919bSRashmica Gupta if (threshold.contains("PerformanceLossHigh") || 7183e99919bSRashmica Gupta threshold.contains("PerformanceLossLow")) 7193e99919bSRashmica Gupta { 7203e99919bSRashmica Gupta perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>( 7213e99919bSRashmica Gupta bus, objPath.c_str()); 7223e99919bSRashmica Gupta 7233e99919bSRashmica Gupta perfLossIface->performanceLossHigh(threshold.value( 7243e99919bSRashmica Gupta "PerformanceLossHigh", std::numeric_limits<double>::quiet_NaN())); 7253e99919bSRashmica Gupta perfLossIface->performanceLossLow(threshold.value( 7263e99919bSRashmica Gupta "PerformanceLossLow", std::numeric_limits<double>::quiet_NaN())); 7271dff7dceSRashmica Gupta perfLossIface->setHighHysteresis(threshold.value( 7281dff7dceSRashmica Gupta "PerformanceLossHighHysteresis", defaultHysteresis)); 7291dff7dceSRashmica Gupta perfLossIface->setLowHysteresis( 7301dff7dceSRashmica Gupta threshold.value("PerformanceLossLowHysteresis", defaultHysteresis)); 7313e99919bSRashmica Gupta } 7323e99919bSRashmica Gupta } 7333e99919bSRashmica Gupta 734e7efe135SRashmica Gupta ManagedObjectType VirtualSensors::getObjectsFromDBus() 735e7efe135SRashmica Gupta { 736e7efe135SRashmica Gupta ManagedObjectType objects; 737e7efe135SRashmica Gupta 738e7efe135SRashmica Gupta try 739e7efe135SRashmica Gupta { 740f6825b91SNan Zhou auto method = bus.new_method_call("xyz.openbmc_project.EntityManager", 741f6825b91SNan Zhou "/xyz/openbmc_project/inventory", 742e7efe135SRashmica Gupta "org.freedesktop.DBus.ObjectManager", 743e7efe135SRashmica Gupta "GetManagedObjects"); 744e7efe135SRashmica Gupta auto reply = bus.call(method); 745e7efe135SRashmica Gupta reply.read(objects); 746e7efe135SRashmica Gupta } 7478e11cccbSPatrick Williams catch (const sdbusplus::exception_t& ex) 748e7efe135SRashmica Gupta { 749e7efe135SRashmica Gupta // If entity manager isn't running yet, keep going. 750e7efe135SRashmica Gupta if (std::string("org.freedesktop.DBus.Error.ServiceUnknown") != 751e7efe135SRashmica Gupta ex.name()) 752e7efe135SRashmica Gupta { 75371b9c116SMatt Spinler error("Could not reach entity-manager: {ERROR}", "ERROR", ex); 75471b9c116SMatt Spinler throw; 755e7efe135SRashmica Gupta } 756e7efe135SRashmica Gupta } 757e7efe135SRashmica Gupta 758e7efe135SRashmica Gupta return objects; 759e7efe135SRashmica Gupta } 760e7efe135SRashmica Gupta 7618e11cccbSPatrick Williams void VirtualSensors::propertiesChanged(sdbusplus::message_t& msg) 762e7efe135SRashmica Gupta { 763e7efe135SRashmica Gupta std::string path; 764e7efe135SRashmica Gupta PropertyMap properties; 765e7efe135SRashmica Gupta 766e7efe135SRashmica Gupta msg.read(path, properties); 767e7efe135SRashmica Gupta 768e7efe135SRashmica Gupta /* We get multiple callbacks for one sensor. 'Type' is a required field and 769e7efe135SRashmica Gupta * is a unique label so use to to only proceed once per sensor */ 770e7efe135SRashmica Gupta if (properties.contains("Type")) 771e7efe135SRashmica Gupta { 772e7efe135SRashmica Gupta if (isCalculationType(path)) 773e7efe135SRashmica Gupta { 774e7efe135SRashmica Gupta createVirtualSensorsFromDBus(path); 775e7efe135SRashmica Gupta } 776e7efe135SRashmica Gupta } 777e7efe135SRashmica Gupta } 778e7efe135SRashmica Gupta 779abcc94faSVijay Khemka /** @brief Parsing Virtual Sensor config JSON file */ 78032dff21bSPatrick Williams Json VirtualSensors::parseConfigFile() 781abcc94faSVijay Khemka { 78232dff21bSPatrick Williams using path = std::filesystem::path; 78332dff21bSPatrick Williams auto configFile = []() -> path { 78432dff21bSPatrick Williams static constexpr auto name = "virtual_sensor_config.json"; 78532dff21bSPatrick Williams 78632dff21bSPatrick Williams for (auto pathSeg : {std::filesystem::current_path(), 78732dff21bSPatrick Williams path{"/var/lib/phosphor-virtual-sensor"}, 78832dff21bSPatrick Williams path{"/usr/share/phosphor-virtual-sensor"}}) 78932dff21bSPatrick Williams { 79032dff21bSPatrick Williams auto file = pathSeg / name; 79132dff21bSPatrick Williams if (std::filesystem::exists(file)) 79232dff21bSPatrick Williams { 79332dff21bSPatrick Williams return file; 79432dff21bSPatrick Williams } 79532dff21bSPatrick Williams } 79632dff21bSPatrick Williams return name; 79732dff21bSPatrick Williams }(); 79832dff21bSPatrick Williams 799abcc94faSVijay Khemka std::ifstream jsonFile(configFile); 800abcc94faSVijay Khemka if (!jsonFile.is_open()) 801abcc94faSVijay Khemka { 80282b39c66SPatrick Williams error("config JSON file {FILENAME} not found", "FILENAME", configFile); 803e7efe135SRashmica Gupta return {}; 804abcc94faSVijay Khemka } 805abcc94faSVijay Khemka 806abcc94faSVijay Khemka auto data = Json::parse(jsonFile, nullptr, false); 807abcc94faSVijay Khemka if (data.is_discarded()) 808abcc94faSVijay Khemka { 80982b39c66SPatrick Williams error("config readings JSON parser failure with {FILENAME}", "FILENAME", 81082b39c66SPatrick Williams configFile); 811abcc94faSVijay Khemka throw std::exception{}; 812abcc94faSVijay Khemka } 813abcc94faSVijay Khemka 814abcc94faSVijay Khemka return data; 815abcc94faSVijay Khemka } 816abcc94faSVijay Khemka 817e0d371e4SVijay Khemka std::map<std::string, ValueIface::Unit> unitMap = { 818e0d371e4SVijay Khemka {"temperature", ValueIface::Unit::DegreesC}, 819e0d371e4SVijay Khemka {"fan_tach", ValueIface::Unit::RPMS}, 820e0d371e4SVijay Khemka {"voltage", ValueIface::Unit::Volts}, 821e0d371e4SVijay Khemka {"altitude", ValueIface::Unit::Meters}, 822e0d371e4SVijay Khemka {"current", ValueIface::Unit::Amperes}, 823e0d371e4SVijay Khemka {"power", ValueIface::Unit::Watts}, 824e0d371e4SVijay Khemka {"energy", ValueIface::Unit::Joules}, 8252b56ddb3SKumar Thangavel {"utilization", ValueIface::Unit::Percent}, 8264ac7a7f2SRashmica Gupta {"airflow", ValueIface::Unit::CFM}, 8274ac7a7f2SRashmica Gupta {"pressure", ValueIface::Unit::Pascals}}; 828e0d371e4SVijay Khemka 829e7efe135SRashmica Gupta const std::string getSensorTypeFromUnit(const std::string& unit) 830e7efe135SRashmica Gupta { 831e7efe135SRashmica Gupta std::string unitPrefix = "xyz.openbmc_project.Sensor.Value.Unit."; 832e7efe135SRashmica Gupta for (auto [type, unitObj] : unitMap) 833e7efe135SRashmica Gupta { 834e7efe135SRashmica Gupta auto unitPath = ValueIface::convertUnitToString(unitObj); 835e7efe135SRashmica Gupta if (unitPath == (unitPrefix + unit)) 836e7efe135SRashmica Gupta { 837e7efe135SRashmica Gupta return type; 838e7efe135SRashmica Gupta } 839e7efe135SRashmica Gupta } 840e7efe135SRashmica Gupta return ""; 841e7efe135SRashmica Gupta } 842e7efe135SRashmica Gupta 843e7efe135SRashmica Gupta void VirtualSensors::setupMatches() 844e7efe135SRashmica Gupta { 845e7efe135SRashmica Gupta /* Already setup */ 846e7efe135SRashmica Gupta if (!this->matches.empty()) 847e7efe135SRashmica Gupta { 848e7efe135SRashmica Gupta return; 849e7efe135SRashmica Gupta } 850e7efe135SRashmica Gupta 851e7efe135SRashmica Gupta /* Setup matches */ 8528e11cccbSPatrick Williams auto eventHandler = [this](sdbusplus::message_t& message) { 853e7efe135SRashmica Gupta if (message.is_method_error()) 854e7efe135SRashmica Gupta { 85582b39c66SPatrick Williams error("Callback method error"); 856e7efe135SRashmica Gupta return; 857e7efe135SRashmica Gupta } 858e7efe135SRashmica Gupta this->propertiesChanged(message); 859e7efe135SRashmica Gupta }; 860e7efe135SRashmica Gupta 861e7efe135SRashmica Gupta for (const char* iface : calculationIfaces) 862e7efe135SRashmica Gupta { 8638e11cccbSPatrick Williams auto match = std::make_unique<sdbusplus::bus::match_t>( 864e7efe135SRashmica Gupta bus, 865e7efe135SRashmica Gupta sdbusplus::bus::match::rules::propertiesChangedNamespace( 866e7efe135SRashmica Gupta "/xyz/openbmc_project/inventory", iface), 867e7efe135SRashmica Gupta eventHandler); 868e7efe135SRashmica Gupta this->matches.emplace_back(std::move(match)); 869e7efe135SRashmica Gupta } 870e7efe135SRashmica Gupta } 871e7efe135SRashmica Gupta 872e7efe135SRashmica Gupta void VirtualSensors::createVirtualSensorsFromDBus( 873e7efe135SRashmica Gupta const std::string& calculationIface) 874e7efe135SRashmica Gupta { 875e7efe135SRashmica Gupta if (calculationIface.empty()) 876e7efe135SRashmica Gupta { 87782b39c66SPatrick Williams error("No calculation type supplied"); 878e7efe135SRashmica Gupta return; 879e7efe135SRashmica Gupta } 880e7efe135SRashmica Gupta auto objects = getObjectsFromDBus(); 881e7efe135SRashmica Gupta 882e7efe135SRashmica Gupta /* Get virtual sensors config data */ 883e7efe135SRashmica Gupta for (const auto& [path, interfaceMap] : objects) 884e7efe135SRashmica Gupta { 885e7efe135SRashmica Gupta /* Find Virtual Sensor interfaces */ 8862db8d41fSGeorge Liu auto intfIter = interfaceMap.find(calculationIface); 8872db8d41fSGeorge Liu if (intfIter == interfaceMap.end()) 888e7efe135SRashmica Gupta { 889e7efe135SRashmica Gupta continue; 890e7efe135SRashmica Gupta } 8912db8d41fSGeorge Liu 8922db8d41fSGeorge Liu std::string name = path.filename(); 893e7efe135SRashmica Gupta if (name.empty()) 894e7efe135SRashmica Gupta { 89582b39c66SPatrick Williams error("Virtual Sensor name not found in entity manager config"); 896e7efe135SRashmica Gupta continue; 897e7efe135SRashmica Gupta } 898e7efe135SRashmica Gupta if (virtualSensorsMap.contains(name)) 899e7efe135SRashmica Gupta { 90082b39c66SPatrick Williams error("A virtual sensor named {NAME} already exists", "NAME", name); 901e7efe135SRashmica Gupta continue; 902e7efe135SRashmica Gupta } 903e7efe135SRashmica Gupta 904e7efe135SRashmica Gupta /* Extract the virtual sensor type as we need this to initialize the 905e7efe135SRashmica Gupta * sensor */ 9062db8d41fSGeorge Liu std::string sensorType, sensorUnit; 9072db8d41fSGeorge Liu auto propertyMap = intfIter->second; 9082db8d41fSGeorge Liu auto proIter = propertyMap.find("Units"); 9092db8d41fSGeorge Liu if (proIter != propertyMap.end()) 910e7efe135SRashmica Gupta { 9112db8d41fSGeorge Liu sensorUnit = std::get<std::string>(proIter->second); 912e7efe135SRashmica Gupta } 913e7efe135SRashmica Gupta sensorType = getSensorTypeFromUnit(sensorUnit); 914e7efe135SRashmica Gupta if (sensorType.empty()) 915e7efe135SRashmica Gupta { 91682b39c66SPatrick Williams error("Sensor unit type {TYPE} is not supported", "TYPE", 91782b39c66SPatrick Williams sensorUnit); 918e7efe135SRashmica Gupta continue; 919e7efe135SRashmica Gupta } 920e7efe135SRashmica Gupta 921e7efe135SRashmica Gupta try 922e7efe135SRashmica Gupta { 9232db8d41fSGeorge Liu auto objpath = static_cast<std::string>(path); 924e7efe135SRashmica Gupta auto virtObjPath = sensorDbusPath + sensorType + "/" + name; 925e7efe135SRashmica Gupta 926e7efe135SRashmica Gupta auto virtualSensorPtr = std::make_unique<VirtualSensor>( 927e7efe135SRashmica Gupta bus, virtObjPath.c_str(), interfaceMap, name, sensorType, 928dc777015STao Lin calculationIface, objpath); 92982b39c66SPatrick Williams info("Added a new virtual sensor: {NAME} {TYPE}", "NAME", name, 93082b39c66SPatrick Williams "TYPE", sensorType); 931e7efe135SRashmica Gupta virtualSensorPtr->updateVirtualSensor(); 932e7efe135SRashmica Gupta 933e7efe135SRashmica Gupta /* Initialize unit value for virtual sensor */ 934e7efe135SRashmica Gupta virtualSensorPtr->ValueIface::unit(unitMap[sensorType]); 935e7efe135SRashmica Gupta virtualSensorPtr->emit_object_added(); 936e7efe135SRashmica Gupta 937e7efe135SRashmica Gupta virtualSensorsMap.emplace(name, std::move(virtualSensorPtr)); 938e7efe135SRashmica Gupta 939e7efe135SRashmica Gupta /* Setup match for interfaces removed */ 940*ae10c529SPatrick Williams auto intfRemoved = [this, objpath, 941*ae10c529SPatrick Williams name](sdbusplus::message_t& message) { 942e7efe135SRashmica Gupta if (!virtualSensorsMap.contains(name)) 943e7efe135SRashmica Gupta { 944e7efe135SRashmica Gupta return; 945e7efe135SRashmica Gupta } 946e7efe135SRashmica Gupta sdbusplus::message::object_path path; 947e7efe135SRashmica Gupta message.read(path); 948e7efe135SRashmica Gupta if (static_cast<const std::string&>(path) == objpath) 949e7efe135SRashmica Gupta { 95082b39c66SPatrick Williams info("Removed a virtual sensor: {NAME}", "NAME", name); 951e7efe135SRashmica Gupta virtualSensorsMap.erase(name); 952e7efe135SRashmica Gupta } 953e7efe135SRashmica Gupta }; 9548e11cccbSPatrick Williams auto matchOnRemove = std::make_unique<sdbusplus::bus::match_t>( 955e7efe135SRashmica Gupta bus, 956e7efe135SRashmica Gupta sdbusplus::bus::match::rules::interfacesRemoved() + 957e7efe135SRashmica Gupta sdbusplus::bus::match::rules::argNpath(0, objpath), 958e7efe135SRashmica Gupta intfRemoved); 959e7efe135SRashmica Gupta /* TODO: slight race condition here. Check that the config still 960e7efe135SRashmica Gupta * exists */ 961e7efe135SRashmica Gupta this->matches.emplace_back(std::move(matchOnRemove)); 962e7efe135SRashmica Gupta } 963dac2663cSPatrick Williams catch (const std::invalid_argument& ia) 964e7efe135SRashmica Gupta { 96582b39c66SPatrick Williams error("Failed to set up virtual sensor: {ERROR}", "ERROR", ia); 966e7efe135SRashmica Gupta } 967e7efe135SRashmica Gupta } 968e7efe135SRashmica Gupta } 969e7efe135SRashmica Gupta 970abcc94faSVijay Khemka void VirtualSensors::createVirtualSensors() 971abcc94faSVijay Khemka { 972abcc94faSVijay Khemka static const Json empty{}; 973abcc94faSVijay Khemka 97432dff21bSPatrick Williams auto data = parseConfigFile(); 975e7efe135SRashmica Gupta 976abcc94faSVijay Khemka // print values 977abcc94faSVijay Khemka if (DEBUG) 978e7efe135SRashmica Gupta { 979fbd7145eSPatrick Williams debug("JSON: {JSON}", "JSON", data.dump()); 980e7efe135SRashmica Gupta } 981abcc94faSVijay Khemka 982abcc94faSVijay Khemka /* Get virtual sensors config data */ 983abcc94faSVijay Khemka for (const auto& j : data) 984abcc94faSVijay Khemka { 985abcc94faSVijay Khemka auto desc = j.value("Desc", empty); 986abcc94faSVijay Khemka if (!desc.empty()) 987abcc94faSVijay Khemka { 988e7efe135SRashmica Gupta if (desc.value("Config", "") == "D-Bus") 989e7efe135SRashmica Gupta { 990e7efe135SRashmica Gupta /* Look on D-Bus for a virtual sensor config. Set up matches 991e7efe135SRashmica Gupta * first because the configs may not be on D-Bus yet and we 992e7efe135SRashmica Gupta * don't want to miss them */ 993e7efe135SRashmica Gupta setupMatches(); 994e7efe135SRashmica Gupta 995e7efe135SRashmica Gupta if (desc.contains("Type")) 996e7efe135SRashmica Gupta { 99782b39c66SPatrick Williams auto type = desc.value("Type", ""); 99882b39c66SPatrick Williams auto path = "xyz.openbmc_project.Configuration." + type; 99982b39c66SPatrick Williams 1000e7efe135SRashmica Gupta if (!isCalculationType(path)) 1001e7efe135SRashmica Gupta { 100282b39c66SPatrick Williams error("Invalid calculation type {TYPE} supplied.", 100382b39c66SPatrick Williams "TYPE", type); 1004e7efe135SRashmica Gupta continue; 1005e7efe135SRashmica Gupta } 1006e7efe135SRashmica Gupta createVirtualSensorsFromDBus(path); 1007e7efe135SRashmica Gupta } 1008e7efe135SRashmica Gupta continue; 1009e7efe135SRashmica Gupta } 1010e7efe135SRashmica Gupta 1011abcc94faSVijay Khemka std::string sensorType = desc.value("SensorType", ""); 1012abcc94faSVijay Khemka std::string name = desc.value("Name", ""); 1013665a0a29SRashmica Gupta std::replace(name.begin(), name.end(), ' ', '_'); 1014abcc94faSVijay Khemka 1015abcc94faSVijay Khemka if (!name.empty() && !sensorType.empty()) 1016abcc94faSVijay Khemka { 1017e0d371e4SVijay Khemka if (unitMap.find(sensorType) == unitMap.end()) 1018e0d371e4SVijay Khemka { 101982b39c66SPatrick Williams error("Sensor type {TYPE} is not supported", "TYPE", 102082b39c66SPatrick Williams sensorType); 1021e0d371e4SVijay Khemka } 1022e0d371e4SVijay Khemka else 1023e0d371e4SVijay Khemka { 102467d8b9d2SRashmica Gupta if (virtualSensorsMap.find(name) != virtualSensorsMap.end()) 102567d8b9d2SRashmica Gupta { 102682b39c66SPatrick Williams error("A virtual sensor named {NAME} already exists", 102782b39c66SPatrick Williams "NAME", name); 102867d8b9d2SRashmica Gupta continue; 102967d8b9d2SRashmica Gupta } 1030862c3d1eSRashmica Gupta auto objPath = sensorDbusPath + sensorType + "/" + name; 1031abcc94faSVijay Khemka 103232a7156bSVijay Khemka auto virtualSensorPtr = std::make_unique<VirtualSensor>( 103332a7156bSVijay Khemka bus, objPath.c_str(), j, name); 1034abcc94faSVijay Khemka 103582b39c66SPatrick Williams info("Added a new virtual sensor: {NAME}", "NAME", name); 10363ed9a516SVijay Khemka virtualSensorPtr->updateVirtualSensor(); 1037e0d371e4SVijay Khemka 1038e0d371e4SVijay Khemka /* Initialize unit value for virtual sensor */ 1039e0d371e4SVijay Khemka virtualSensorPtr->ValueIface::unit(unitMap[sensorType]); 1040a2fa63a6SRashmica Gupta virtualSensorPtr->emit_object_added(); 1041e0d371e4SVijay Khemka 10423ed9a516SVijay Khemka virtualSensorsMap.emplace(std::move(name), 10433ed9a516SVijay Khemka std::move(virtualSensorPtr)); 1044abcc94faSVijay Khemka } 1045e0d371e4SVijay Khemka } 1046abcc94faSVijay Khemka else 1047abcc94faSVijay Khemka { 104882b39c66SPatrick Williams error( 104982b39c66SPatrick Williams "Sensor type ({TYPE}) or name ({NAME}) not found in config file", 105082b39c66SPatrick Williams "NAME", name, "TYPE", sensorType); 1051abcc94faSVijay Khemka } 1052abcc94faSVijay Khemka } 1053abcc94faSVijay Khemka else 1054abcc94faSVijay Khemka { 105582b39c66SPatrick Williams error("Descriptor for new virtual sensor not found in config file"); 1056abcc94faSVijay Khemka } 1057abcc94faSVijay Khemka } 1058abcc94faSVijay Khemka } 1059abcc94faSVijay Khemka 1060abcc94faSVijay Khemka } // namespace virtualSensor 1061abcc94faSVijay Khemka } // namespace phosphor 1062abcc94faSVijay Khemka 1063abcc94faSVijay Khemka /** 1064abcc94faSVijay Khemka * @brief Main 1065abcc94faSVijay Khemka */ 1066abcc94faSVijay Khemka int main() 1067abcc94faSVijay Khemka { 1068abcc94faSVijay Khemka // Get a handle to system dbus 1069abcc94faSVijay Khemka auto bus = sdbusplus::bus::new_default(); 1070abcc94faSVijay Khemka 10716c19e7d2SMatt Spinler // Add the ObjectManager interface 1072f7ec40aaSEd Tanous sdbusplus::server::manager_t objManager(bus, 1073f7ec40aaSEd Tanous "/xyz/openbmc_project/sensors"); 10746c19e7d2SMatt Spinler 1075abcc94faSVijay Khemka // Create an virtual sensors object 1076abcc94faSVijay Khemka phosphor::virtualSensor::VirtualSensors virtualSensors(bus); 1077abcc94faSVijay Khemka 1078abcc94faSVijay Khemka // Request service bus name 107994921490SGeorge Liu bus.request_name("xyz.openbmc_project.VirtualSensor"); 1080abcc94faSVijay Khemka 1081e667239dSPatrick Williams // Run the dbus loop. 1082e667239dSPatrick Williams bus.process_loop(); 1083abcc94faSVijay Khemka 1084abcc94faSVijay Khemka return 0; 1085abcc94faSVijay Khemka } 1086