1abcc94faSVijay Khemka #include "virtualSensor.hpp" 2abcc94faSVijay Khemka 3abcc94faSVijay Khemka #include "config.hpp" 4abcc94faSVijay Khemka 582b39c66SPatrick Williams #include <phosphor-logging/lg2.hpp> 6abcc94faSVijay Khemka 7abcc94faSVijay Khemka #include <fstream> 8abcc94faSVijay Khemka 9abcc94faSVijay Khemka static constexpr bool DEBUG = false; 10abcc94faSVijay Khemka static constexpr auto busName = "xyz.openbmc_project.VirtualSensor"; 11abcc94faSVijay Khemka static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/"; 12e7efe135SRashmica Gupta static constexpr auto entityManagerBusName = 13e7efe135SRashmica Gupta "xyz.openbmc_project.EntityManager"; 14e7efe135SRashmica Gupta static constexpr auto vsThresholdsIfaceSuffix = ".Thresholds"; 15304fd0e4SRashmica Gupta static constexpr std::array<const char*, 1> calculationIfaces = { 16304fd0e4SRashmica Gupta "xyz.openbmc_project.Configuration.ModifiedMedian"}; 171dff7dceSRashmica Gupta static constexpr auto defaultHysteresis = 0; 18abcc94faSVijay Khemka 1982b39c66SPatrick Williams PHOSPHOR_LOG2_USING_WITH_FLAGS; 20abcc94faSVijay Khemka 2151f898e2SVijay Khemka int handleDbusSignal(sd_bus_message* msg, void* usrData, sd_bus_error*) 2251f898e2SVijay Khemka { 2351f898e2SVijay Khemka if (usrData == nullptr) 2451f898e2SVijay Khemka { 2551f898e2SVijay Khemka throw std::runtime_error("Invalid match"); 2651f898e2SVijay Khemka } 2751f898e2SVijay Khemka 288e11cccbSPatrick Williams auto sdbpMsg = sdbusplus::message_t(msg); 2951f898e2SVijay Khemka std::string msgIfce; 3051f898e2SVijay Khemka std::map<std::string, std::variant<int64_t, double, bool>> msgData; 3151f898e2SVijay Khemka 3251f898e2SVijay Khemka sdbpMsg.read(msgIfce, msgData); 3351f898e2SVijay Khemka 3451f898e2SVijay Khemka if (msgData.find("Value") != msgData.end()) 3551f898e2SVijay Khemka { 3651f898e2SVijay Khemka using namespace phosphor::virtualSensor; 3751f898e2SVijay Khemka VirtualSensor* obj = static_cast<VirtualSensor*>(usrData); 3851f898e2SVijay Khemka // TODO(openbmc/phosphor-virtual-sensor#1): updateVirtualSensor should 3951f898e2SVijay Khemka // be changed to take the information we got from the signal, to avoid 4051f898e2SVijay Khemka // having to do numerous dbus queries. 4151f898e2SVijay Khemka obj->updateVirtualSensor(); 4251f898e2SVijay Khemka } 4351f898e2SVijay Khemka return 0; 4451f898e2SVijay Khemka } 4551f898e2SVijay Khemka 46abcc94faSVijay Khemka namespace phosphor 47abcc94faSVijay Khemka { 48abcc94faSVijay Khemka namespace virtualSensor 49abcc94faSVijay Khemka { 50abcc94faSVijay Khemka 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 { 17805b1d417SRashmica Gupta auto sev = 17905b1d417SRashmica Gupta getNumberFromConfig<uint64_t>(propertyMap, "Severity", 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 193e7efe135SRashmica Gupta void parseThresholds(Json& thresholds, const PropertyMap& propertyMap) 194e7efe135SRashmica Gupta { 195e7efe135SRashmica Gupta std::string direction; 196e7efe135SRashmica Gupta 197e7efe135SRashmica Gupta auto value = getNumberFromConfig<double>(propertyMap, "Value", true); 198e7efe135SRashmica Gupta 19905b1d417SRashmica Gupta auto severity = getSeverityField(propertyMap); 20005b1d417SRashmica Gupta 20105b1d417SRashmica Gupta if (auto itr = propertyMap.find("Direction"); itr != propertyMap.end()) 202e7efe135SRashmica Gupta { 203e7efe135SRashmica Gupta direction = std::get<std::string>(itr->second); 204e7efe135SRashmica Gupta } 205e7efe135SRashmica Gupta 206e7efe135SRashmica Gupta auto threshold = getThresholdType(direction, severity); 207e7efe135SRashmica Gupta thresholds[threshold] = value; 2081dff7dceSRashmica Gupta 2091dff7dceSRashmica Gupta auto hysteresis = 2101dff7dceSRashmica Gupta getNumberFromConfig<double>(propertyMap, "Hysteresis", false); 2111dff7dceSRashmica Gupta if (hysteresis != std::numeric_limits<double>::quiet_NaN()) 2121dff7dceSRashmica Gupta { 2131dff7dceSRashmica Gupta thresholds[threshold + "Hysteresis"] = hysteresis; 2141dff7dceSRashmica Gupta } 215e7efe135SRashmica Gupta } 216e7efe135SRashmica Gupta 217e7efe135SRashmica Gupta void VirtualSensor::parseConfigInterface(const PropertyMap& propertyMap, 218e7efe135SRashmica Gupta const std::string& sensorType, 219e7efe135SRashmica Gupta const std::string& interface) 220e7efe135SRashmica Gupta { 221e7efe135SRashmica Gupta /* Parse sensors / DBus params */ 222e7efe135SRashmica Gupta if (auto itr = propertyMap.find("Sensors"); itr != propertyMap.end()) 223e7efe135SRashmica Gupta { 224e7efe135SRashmica Gupta auto sensors = std::get<std::vector<std::string>>(itr->second); 225e7efe135SRashmica Gupta for (auto sensor : sensors) 226e7efe135SRashmica Gupta { 227e7efe135SRashmica Gupta std::replace(sensor.begin(), sensor.end(), ' ', '_'); 228e7efe135SRashmica Gupta auto sensorObjPath = sensorDbusPath + sensorType + "/" + sensor; 229e7efe135SRashmica Gupta 230e7efe135SRashmica Gupta auto paramPtr = 231e7efe135SRashmica Gupta std::make_unique<SensorParam>(bus, sensorObjPath, this); 232e7efe135SRashmica Gupta symbols.create_variable(sensor); 233e7efe135SRashmica Gupta paramMap.emplace(std::move(sensor), std::move(paramPtr)); 234e7efe135SRashmica Gupta } 235e7efe135SRashmica Gupta } 236e7efe135SRashmica Gupta /* Get expression string */ 237e7efe135SRashmica Gupta if (!isCalculationType(interface)) 238e7efe135SRashmica Gupta { 239e7efe135SRashmica Gupta throw std::invalid_argument("Invalid expression in interface"); 240e7efe135SRashmica Gupta } 241e7efe135SRashmica Gupta exprStr = interface; 242e7efe135SRashmica Gupta 243e7efe135SRashmica Gupta /* Get optional min and max input and output values */ 244e7efe135SRashmica Gupta ValueIface::maxValue( 245e7efe135SRashmica Gupta getNumberFromConfig<double>(propertyMap, "MaxValue", false)); 246e7efe135SRashmica Gupta ValueIface::minValue( 247e7efe135SRashmica Gupta getNumberFromConfig<double>(propertyMap, "MinValue", false)); 248e7efe135SRashmica Gupta maxValidInput = 249190f6d06SJiaqing Zhao getNumberFromConfig<double>(propertyMap, "MaxValidInput", false, 250190f6d06SJiaqing Zhao std::numeric_limits<double>::infinity()); 251e7efe135SRashmica Gupta minValidInput = 252190f6d06SJiaqing Zhao getNumberFromConfig<double>(propertyMap, "MinValidInput", false, 253190f6d06SJiaqing Zhao -std::numeric_limits<double>::infinity()); 254e7efe135SRashmica Gupta } 255e7efe135SRashmica Gupta 256ce675228SMatt Spinler void VirtualSensor::initVirtualSensor(const Json& sensorConfig, 257ce675228SMatt Spinler const std::string& objPath) 258abcc94faSVijay Khemka { 259abcc94faSVijay Khemka static const Json empty{}; 260abcc94faSVijay Khemka 261abcc94faSVijay Khemka /* Get threshold values if defined in config */ 262abcc94faSVijay Khemka auto threshold = sensorConfig.value("Threshold", empty); 263f15189e3SMatt Spinler 2643e99919bSRashmica Gupta createThresholds(threshold, objPath); 265abcc94faSVijay Khemka 266f6443742SHarvey Wu /* Get MaxValue, MinValue setting if defined in config */ 267f6443742SHarvey Wu auto confDesc = sensorConfig.value("Desc", empty); 268f6443742SHarvey Wu if (auto maxConf = confDesc.find("MaxValue"); 269f6443742SHarvey Wu maxConf != confDesc.end() && maxConf->is_number()) 270f6443742SHarvey Wu { 271f6443742SHarvey Wu ValueIface::maxValue(maxConf->get<double>()); 272f6443742SHarvey Wu } 273f6443742SHarvey Wu if (auto minConf = confDesc.find("MinValue"); 274f6443742SHarvey Wu minConf != confDesc.end() && minConf->is_number()) 275f6443742SHarvey Wu { 276f6443742SHarvey Wu ValueIface::minValue(minConf->get<double>()); 277f6443742SHarvey Wu } 278f6443742SHarvey Wu 2790fcf0e1cSLei YU /* Get optional association */ 2800fcf0e1cSLei YU auto assocJson = sensorConfig.value("Associations", empty); 2810fcf0e1cSLei YU if (!assocJson.empty()) 2820fcf0e1cSLei YU { 2830fcf0e1cSLei YU auto assocs = getAssociationsFromJson(assocJson); 2840fcf0e1cSLei YU if (!assocs.empty()) 2850fcf0e1cSLei YU { 2860fcf0e1cSLei YU associationIface = 2870fcf0e1cSLei YU std::make_unique<AssociationObject>(bus, objPath.c_str()); 2880fcf0e1cSLei YU associationIface->associations(assocs); 2890fcf0e1cSLei YU } 2900fcf0e1cSLei YU } 2910fcf0e1cSLei YU 292abcc94faSVijay Khemka /* Get expression string */ 29303c4c8e2SPatrick Williams static constexpr auto exprKey = "Expression"; 29403c4c8e2SPatrick Williams if (sensorConfig.contains(exprKey)) 29503c4c8e2SPatrick Williams { 296a959678cSPatrick Williams auto& ref = sensorConfig.at(exprKey); 29703c4c8e2SPatrick Williams if (ref.is_array()) 29803c4c8e2SPatrick Williams { 29903c4c8e2SPatrick Williams exprStr = std::string{}; 30003c4c8e2SPatrick Williams for (auto& s : ref) 30103c4c8e2SPatrick Williams { 30203c4c8e2SPatrick Williams exprStr += s; 30303c4c8e2SPatrick Williams } 30403c4c8e2SPatrick Williams } 30503c4c8e2SPatrick Williams else if (ref.is_string()) 30603c4c8e2SPatrick Williams { 30703c4c8e2SPatrick Williams exprStr = std::string{ref}; 30803c4c8e2SPatrick Williams } 30903c4c8e2SPatrick Williams } 310abcc94faSVijay Khemka 311abcc94faSVijay Khemka /* Get all the parameter listed in configuration */ 312abcc94faSVijay Khemka auto params = sensorConfig.value("Params", empty); 313abcc94faSVijay Khemka 314abcc94faSVijay Khemka /* Check for constant parameter */ 315abcc94faSVijay Khemka const auto& consParams = params.value("ConstParam", empty); 316abcc94faSVijay Khemka if (!consParams.empty()) 317abcc94faSVijay Khemka { 318abcc94faSVijay Khemka for (auto& j : consParams) 319abcc94faSVijay Khemka { 320abcc94faSVijay Khemka if (j.find("ParamName") != j.end()) 321abcc94faSVijay Khemka { 322abcc94faSVijay Khemka auto paramPtr = std::make_unique<SensorParam>(j["Value"]); 3233ed9a516SVijay Khemka std::string name = j["ParamName"]; 3243ed9a516SVijay Khemka symbols.create_variable(name); 3253ed9a516SVijay Khemka paramMap.emplace(std::move(name), std::move(paramPtr)); 326abcc94faSVijay Khemka } 327abcc94faSVijay Khemka else 328abcc94faSVijay Khemka { 329abcc94faSVijay Khemka /* Invalid configuration */ 330abcc94faSVijay Khemka throw std::invalid_argument( 331abcc94faSVijay Khemka "ParamName not found in configuration"); 332abcc94faSVijay Khemka } 333abcc94faSVijay Khemka } 334abcc94faSVijay Khemka } 335abcc94faSVijay Khemka 3367452a867SVijay Khemka /* Check for dbus parameter */ 3377452a867SVijay Khemka auto dbusParams = params.value("DbusParam", empty); 3387452a867SVijay Khemka if (!dbusParams.empty()) 3397452a867SVijay Khemka { 3407452a867SVijay Khemka for (auto& j : dbusParams) 3417452a867SVijay Khemka { 3427452a867SVijay Khemka /* Get parameter dbus sensor descriptor */ 3437452a867SVijay Khemka auto desc = j.value("Desc", empty); 3447452a867SVijay Khemka if ((!desc.empty()) && (j.find("ParamName") != j.end())) 3457452a867SVijay Khemka { 3467452a867SVijay Khemka std::string sensorType = desc.value("SensorType", ""); 3477452a867SVijay Khemka std::string name = desc.value("Name", ""); 3487452a867SVijay Khemka 3497452a867SVijay Khemka if (!sensorType.empty() && !name.empty()) 3507452a867SVijay Khemka { 3511204b433SGeorge Liu auto path = sensorDbusPath + sensorType + "/" + name; 3527452a867SVijay Khemka 35351f898e2SVijay Khemka auto paramPtr = 3541204b433SGeorge Liu std::make_unique<SensorParam>(bus, path, this); 3551204b433SGeorge Liu std::string paramName = j["ParamName"]; 3561204b433SGeorge Liu symbols.create_variable(paramName); 3571204b433SGeorge Liu paramMap.emplace(std::move(paramName), std::move(paramPtr)); 3587452a867SVijay Khemka } 3597452a867SVijay Khemka } 3607452a867SVijay Khemka } 3617452a867SVijay Khemka } 362abcc94faSVijay Khemka 3633ed9a516SVijay Khemka symbols.add_constants(); 3649f1ef4f5SMatt Spinler symbols.add_package(vecopsPackage); 3653ed9a516SVijay Khemka expression.register_symbol_table(symbols); 3663ed9a516SVijay Khemka 3673ed9a516SVijay Khemka /* parser from exprtk */ 3683ed9a516SVijay Khemka exprtk::parser<double> parser{}; 369ddc6dcd6SMatt Spinler if (!parser.compile(exprStr, expression)) 370ddc6dcd6SMatt Spinler { 37182b39c66SPatrick Williams error("Expression compilation failed"); 372ddc6dcd6SMatt Spinler 373ddc6dcd6SMatt Spinler for (std::size_t i = 0; i < parser.error_count(); ++i) 374ddc6dcd6SMatt Spinler { 37582b39c66SPatrick Williams auto err = parser.get_error(i); 37682b39c66SPatrick Williams error("Error parsing token at {POSITION}: {ERROR}", "POSITION", 37782b39c66SPatrick Williams err.token.position, "TYPE", 37882b39c66SPatrick Williams exprtk::parser_error::to_str(err.mode), "ERROR", 37982b39c66SPatrick Williams err.diagnostic); 380ddc6dcd6SMatt Spinler } 381ddc6dcd6SMatt Spinler throw std::runtime_error("Expression compilation failed"); 382ddc6dcd6SMatt Spinler } 3833ed9a516SVijay Khemka 384abcc94faSVijay Khemka /* Print all parameters for debug purpose only */ 385abcc94faSVijay Khemka if (DEBUG) 386abcc94faSVijay Khemka printParams(paramMap); 387abcc94faSVijay Khemka } 388abcc94faSVijay Khemka 389*dc777015STao Lin void VirtualSensor::createAssociation(const std::string& objPath, 390*dc777015STao Lin const std::string& entityPath) 391*dc777015STao Lin { 392*dc777015STao Lin if (objPath.empty() || entityPath.empty()) 393*dc777015STao Lin { 394*dc777015STao Lin return; 395*dc777015STao Lin } 396*dc777015STao Lin 397*dc777015STao Lin std::filesystem::path p(entityPath); 398*dc777015STao Lin auto assocsDbus = 399*dc777015STao Lin AssociationList{{"chassis", "all_sensors", p.parent_path().string()}}; 400*dc777015STao Lin associationIface = 401*dc777015STao Lin std::make_unique<AssociationObject>(bus, objPath.c_str()); 402*dc777015STao Lin associationIface->associations(assocsDbus); 403*dc777015STao Lin } 404*dc777015STao Lin 405e7efe135SRashmica Gupta void VirtualSensor::initVirtualSensor(const InterfaceMap& interfaceMap, 406e7efe135SRashmica Gupta const std::string& objPath, 407e7efe135SRashmica Gupta const std::string& sensorType, 408e7efe135SRashmica Gupta const std::string& calculationIface) 409e7efe135SRashmica Gupta { 410e7efe135SRashmica Gupta Json thresholds; 411e7efe135SRashmica Gupta const std::string vsThresholdsIntf = 412e7efe135SRashmica Gupta calculationIface + vsThresholdsIfaceSuffix; 413e7efe135SRashmica Gupta 414e7efe135SRashmica Gupta for (const auto& [interface, propertyMap] : interfaceMap) 415e7efe135SRashmica Gupta { 416e7efe135SRashmica Gupta /* Each threshold is on it's own interface with a number as a suffix 417e7efe135SRashmica Gupta * eg xyz.openbmc_project.Configuration.ModifiedMedian.Thresholds1 */ 418e7efe135SRashmica Gupta if (interface.find(vsThresholdsIntf) != std::string::npos) 419e7efe135SRashmica Gupta { 420e7efe135SRashmica Gupta parseThresholds(thresholds, propertyMap); 421e7efe135SRashmica Gupta } 422e7efe135SRashmica Gupta else if (interface == calculationIface) 423e7efe135SRashmica Gupta { 424e7efe135SRashmica Gupta parseConfigInterface(propertyMap, sensorType, interface); 425e7efe135SRashmica Gupta } 426e7efe135SRashmica Gupta } 427e7efe135SRashmica Gupta 428e7efe135SRashmica Gupta createThresholds(thresholds, objPath); 429e7efe135SRashmica Gupta symbols.add_constants(); 430e7efe135SRashmica Gupta symbols.add_package(vecopsPackage); 431e7efe135SRashmica Gupta expression.register_symbol_table(symbols); 432e7efe135SRashmica Gupta 433*dc777015STao Lin createAssociation(objPath, entityPath); 434e7efe135SRashmica Gupta /* Print all parameters for debug purpose only */ 435e7efe135SRashmica Gupta if (DEBUG) 436e7efe135SRashmica Gupta { 437e7efe135SRashmica Gupta printParams(paramMap); 438e7efe135SRashmica Gupta } 439e7efe135SRashmica Gupta } 440e7efe135SRashmica Gupta 441abcc94faSVijay Khemka void VirtualSensor::setSensorValue(double value) 442abcc94faSVijay Khemka { 443543bf668SPatrick Williams value = std::clamp(value, ValueIface::minValue(), ValueIface::maxValue()); 444abcc94faSVijay Khemka ValueIface::value(value); 445abcc94faSVijay Khemka } 446abcc94faSVijay Khemka 447304fd0e4SRashmica Gupta double VirtualSensor::calculateValue(const std::string& calculation, 448304fd0e4SRashmica Gupta const VirtualSensor::ParamMap& paramMap) 449e7efe135SRashmica Gupta { 450304fd0e4SRashmica Gupta auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(), 451304fd0e4SRashmica Gupta calculation); 452304fd0e4SRashmica Gupta if (itr == calculationIfaces.end()) 453304fd0e4SRashmica Gupta { 454e7efe135SRashmica Gupta return std::numeric_limits<double>::quiet_NaN(); 455e7efe135SRashmica Gupta } 456304fd0e4SRashmica Gupta else if (calculation == "xyz.openbmc_project.Configuration.ModifiedMedian") 457304fd0e4SRashmica Gupta { 458304fd0e4SRashmica Gupta return calculateModifiedMedianValue(paramMap); 459304fd0e4SRashmica Gupta } 460304fd0e4SRashmica Gupta return std::numeric_limits<double>::quiet_NaN(); 461304fd0e4SRashmica Gupta } 462304fd0e4SRashmica Gupta 463304fd0e4SRashmica Gupta bool VirtualSensor::sensorInRange(double value) 464304fd0e4SRashmica Gupta { 465304fd0e4SRashmica Gupta if (value <= this->maxValidInput && value >= this->minValidInput) 466304fd0e4SRashmica Gupta { 467304fd0e4SRashmica Gupta return true; 468304fd0e4SRashmica Gupta } 469304fd0e4SRashmica Gupta return false; 470304fd0e4SRashmica Gupta } 471e7efe135SRashmica Gupta 472abcc94faSVijay Khemka void VirtualSensor::updateVirtualSensor() 4733ed9a516SVijay Khemka { 4743ed9a516SVijay Khemka for (auto& param : paramMap) 4753ed9a516SVijay Khemka { 4763ed9a516SVijay Khemka auto& name = param.first; 4773ed9a516SVijay Khemka auto& data = param.second; 4783ed9a516SVijay Khemka if (auto var = symbols.get_variable(name)) 4793ed9a516SVijay Khemka { 4803ed9a516SVijay Khemka var->ref() = data->getParamValue(); 4813ed9a516SVijay Khemka } 4823ed9a516SVijay Khemka else 4833ed9a516SVijay Khemka { 4843ed9a516SVijay Khemka /* Invalid parameter */ 4853ed9a516SVijay Khemka throw std::invalid_argument("ParamName not found in symbols"); 4863ed9a516SVijay Khemka } 4873ed9a516SVijay Khemka } 488e7efe135SRashmica Gupta auto itr = 489e7efe135SRashmica Gupta std::find(calculationIfaces.begin(), calculationIfaces.end(), exprStr); 490304fd0e4SRashmica Gupta auto val = (itr == calculationIfaces.end()) 491304fd0e4SRashmica Gupta ? expression.value() 492304fd0e4SRashmica Gupta : calculateValue(exprStr, paramMap); 49332a7156bSVijay Khemka 49432a7156bSVijay Khemka /* Set sensor value to dbus interface */ 4953ed9a516SVijay Khemka setSensorValue(val); 49632a7156bSVijay Khemka 4973ed9a516SVijay Khemka if (DEBUG) 498e7efe135SRashmica Gupta { 499fbd7145eSPatrick Williams debug("Sensor {NAME} = {VALUE}", "NAME", this->name, "VALUE", val); 500e7efe135SRashmica Gupta } 50132a7156bSVijay Khemka 5028f5e6119SMatt Spinler /* Check sensor thresholds and log required message */ 503b306b03dSMatt Spinler checkThresholds(val, perfLossIface); 504fdb826d5SPatrick Williams checkThresholds(val, warningIface); 505fdb826d5SPatrick Williams checkThresholds(val, criticalIface); 506fdb826d5SPatrick Williams checkThresholds(val, softShutdownIface); 507fdb826d5SPatrick Williams checkThresholds(val, hardShutdownIface); 5083ed9a516SVijay Khemka } 509abcc94faSVijay Khemka 510304fd0e4SRashmica Gupta double VirtualSensor::calculateModifiedMedianValue( 511304fd0e4SRashmica Gupta const VirtualSensor::ParamMap& paramMap) 512304fd0e4SRashmica Gupta { 513304fd0e4SRashmica Gupta std::vector<double> values; 514304fd0e4SRashmica Gupta 515304fd0e4SRashmica Gupta for (auto& param : paramMap) 516304fd0e4SRashmica Gupta { 517304fd0e4SRashmica Gupta auto& name = param.first; 518304fd0e4SRashmica Gupta if (auto var = symbols.get_variable(name)) 519304fd0e4SRashmica Gupta { 520304fd0e4SRashmica Gupta if (!sensorInRange(var->ref())) 521304fd0e4SRashmica Gupta { 522304fd0e4SRashmica Gupta continue; 523304fd0e4SRashmica Gupta } 524304fd0e4SRashmica Gupta values.push_back(var->ref()); 525304fd0e4SRashmica Gupta } 526304fd0e4SRashmica Gupta } 527304fd0e4SRashmica Gupta 528304fd0e4SRashmica Gupta size_t size = values.size(); 529304fd0e4SRashmica Gupta std::sort(values.begin(), values.end()); 530304fd0e4SRashmica Gupta switch (size) 531304fd0e4SRashmica Gupta { 532304fd0e4SRashmica Gupta case 2: 533304fd0e4SRashmica Gupta /* Choose biggest value */ 534304fd0e4SRashmica Gupta return values.at(1); 535304fd0e4SRashmica Gupta case 0: 536304fd0e4SRashmica Gupta return std::numeric_limits<double>::quiet_NaN(); 537304fd0e4SRashmica Gupta default: 538304fd0e4SRashmica Gupta /* Choose median value */ 539304fd0e4SRashmica Gupta if (size % 2 == 0) 540304fd0e4SRashmica Gupta { 541304fd0e4SRashmica Gupta // Average of the two middle values 542304fd0e4SRashmica Gupta return (values.at(size / 2) + values.at(size / 2 - 1)) / 2; 543304fd0e4SRashmica Gupta } 544304fd0e4SRashmica Gupta else 545304fd0e4SRashmica Gupta { 546304fd0e4SRashmica Gupta return values.at((size - 1) / 2); 547304fd0e4SRashmica Gupta } 548304fd0e4SRashmica Gupta } 549304fd0e4SRashmica Gupta } 550304fd0e4SRashmica Gupta 5513e99919bSRashmica Gupta void VirtualSensor::createThresholds(const Json& threshold, 5523e99919bSRashmica Gupta const std::string& objPath) 5533e99919bSRashmica Gupta { 5543e99919bSRashmica Gupta if (threshold.empty()) 5553e99919bSRashmica Gupta { 5563e99919bSRashmica Gupta return; 5573e99919bSRashmica Gupta } 5583e99919bSRashmica Gupta // Only create the threshold interfaces if 5593e99919bSRashmica Gupta // at least one of their values is present. 5603e99919bSRashmica Gupta if (threshold.contains("CriticalHigh") || threshold.contains("CriticalLow")) 5613e99919bSRashmica Gupta { 5623e99919bSRashmica Gupta criticalIface = 5633e99919bSRashmica Gupta std::make_unique<Threshold<CriticalObject>>(bus, objPath.c_str()); 5643e99919bSRashmica Gupta 5653e99919bSRashmica Gupta criticalIface->criticalHigh(threshold.value( 5663e99919bSRashmica Gupta "CriticalHigh", std::numeric_limits<double>::quiet_NaN())); 5673e99919bSRashmica Gupta criticalIface->criticalLow(threshold.value( 5683e99919bSRashmica Gupta "CriticalLow", std::numeric_limits<double>::quiet_NaN())); 5691dff7dceSRashmica Gupta criticalIface->setHighHysteresis( 5701dff7dceSRashmica Gupta threshold.value("CriticalHighHysteresis", defaultHysteresis)); 5711dff7dceSRashmica Gupta criticalIface->setLowHysteresis( 5721dff7dceSRashmica Gupta threshold.value("CriticalLowHysteresis", defaultHysteresis)); 5733e99919bSRashmica Gupta } 5743e99919bSRashmica Gupta 5753e99919bSRashmica Gupta if (threshold.contains("WarningHigh") || threshold.contains("WarningLow")) 5763e99919bSRashmica Gupta { 5773e99919bSRashmica Gupta warningIface = 5783e99919bSRashmica Gupta std::make_unique<Threshold<WarningObject>>(bus, objPath.c_str()); 5793e99919bSRashmica Gupta 5803e99919bSRashmica Gupta warningIface->warningHigh(threshold.value( 5813e99919bSRashmica Gupta "WarningHigh", std::numeric_limits<double>::quiet_NaN())); 5823e99919bSRashmica Gupta warningIface->warningLow(threshold.value( 5833e99919bSRashmica Gupta "WarningLow", std::numeric_limits<double>::quiet_NaN())); 5841dff7dceSRashmica Gupta warningIface->setHighHysteresis( 5851dff7dceSRashmica Gupta threshold.value("WarningHighHysteresis", defaultHysteresis)); 5861dff7dceSRashmica Gupta warningIface->setLowHysteresis( 5871dff7dceSRashmica Gupta threshold.value("WarningLowHysteresis", defaultHysteresis)); 5883e99919bSRashmica Gupta } 5893e99919bSRashmica Gupta 5903e99919bSRashmica Gupta if (threshold.contains("HardShutdownHigh") || 5913e99919bSRashmica Gupta threshold.contains("HardShutdownLow")) 5923e99919bSRashmica Gupta { 5933e99919bSRashmica Gupta hardShutdownIface = std::make_unique<Threshold<HardShutdownObject>>( 5943e99919bSRashmica Gupta bus, objPath.c_str()); 5953e99919bSRashmica Gupta 5963e99919bSRashmica Gupta hardShutdownIface->hardShutdownHigh(threshold.value( 5973e99919bSRashmica Gupta "HardShutdownHigh", std::numeric_limits<double>::quiet_NaN())); 5983e99919bSRashmica Gupta hardShutdownIface->hardShutdownLow(threshold.value( 5993e99919bSRashmica Gupta "HardShutdownLow", std::numeric_limits<double>::quiet_NaN())); 6001dff7dceSRashmica Gupta hardShutdownIface->setHighHysteresis( 6011dff7dceSRashmica Gupta threshold.value("HardShutdownHighHysteresis", defaultHysteresis)); 6021dff7dceSRashmica Gupta hardShutdownIface->setLowHysteresis( 6031dff7dceSRashmica Gupta threshold.value("HardShutdownLowHysteresis", defaultHysteresis)); 6043e99919bSRashmica Gupta } 6053e99919bSRashmica Gupta 6063e99919bSRashmica Gupta if (threshold.contains("SoftShutdownHigh") || 6073e99919bSRashmica Gupta threshold.contains("SoftShutdownLow")) 6083e99919bSRashmica Gupta { 6093e99919bSRashmica Gupta softShutdownIface = std::make_unique<Threshold<SoftShutdownObject>>( 6103e99919bSRashmica Gupta bus, objPath.c_str()); 6113e99919bSRashmica Gupta 6123e99919bSRashmica Gupta softShutdownIface->softShutdownHigh(threshold.value( 6133e99919bSRashmica Gupta "SoftShutdownHigh", std::numeric_limits<double>::quiet_NaN())); 6143e99919bSRashmica Gupta softShutdownIface->softShutdownLow(threshold.value( 6153e99919bSRashmica Gupta "SoftShutdownLow", std::numeric_limits<double>::quiet_NaN())); 6161dff7dceSRashmica Gupta softShutdownIface->setHighHysteresis( 6171dff7dceSRashmica Gupta threshold.value("SoftShutdownHighHysteresis", defaultHysteresis)); 6181dff7dceSRashmica Gupta softShutdownIface->setLowHysteresis( 6191dff7dceSRashmica Gupta threshold.value("SoftShutdownLowHysteresis", defaultHysteresis)); 6203e99919bSRashmica Gupta } 6213e99919bSRashmica Gupta 6223e99919bSRashmica Gupta if (threshold.contains("PerformanceLossHigh") || 6233e99919bSRashmica Gupta threshold.contains("PerformanceLossLow")) 6243e99919bSRashmica Gupta { 6253e99919bSRashmica Gupta perfLossIface = std::make_unique<Threshold<PerformanceLossObject>>( 6263e99919bSRashmica Gupta bus, objPath.c_str()); 6273e99919bSRashmica Gupta 6283e99919bSRashmica Gupta perfLossIface->performanceLossHigh(threshold.value( 6293e99919bSRashmica Gupta "PerformanceLossHigh", std::numeric_limits<double>::quiet_NaN())); 6303e99919bSRashmica Gupta perfLossIface->performanceLossLow(threshold.value( 6313e99919bSRashmica Gupta "PerformanceLossLow", std::numeric_limits<double>::quiet_NaN())); 6321dff7dceSRashmica Gupta perfLossIface->setHighHysteresis(threshold.value( 6331dff7dceSRashmica Gupta "PerformanceLossHighHysteresis", defaultHysteresis)); 6341dff7dceSRashmica Gupta perfLossIface->setLowHysteresis( 6351dff7dceSRashmica Gupta threshold.value("PerformanceLossLowHysteresis", defaultHysteresis)); 6363e99919bSRashmica Gupta } 6373e99919bSRashmica Gupta } 6383e99919bSRashmica Gupta 639e7efe135SRashmica Gupta ManagedObjectType VirtualSensors::getObjectsFromDBus() 640e7efe135SRashmica Gupta { 641e7efe135SRashmica Gupta ManagedObjectType objects; 642e7efe135SRashmica Gupta 643e7efe135SRashmica Gupta try 644e7efe135SRashmica Gupta { 645e7efe135SRashmica Gupta auto method = bus.new_method_call(entityManagerBusName, "/", 646e7efe135SRashmica Gupta "org.freedesktop.DBus.ObjectManager", 647e7efe135SRashmica Gupta "GetManagedObjects"); 648e7efe135SRashmica Gupta auto reply = bus.call(method); 649e7efe135SRashmica Gupta reply.read(objects); 650e7efe135SRashmica Gupta } 6518e11cccbSPatrick Williams catch (const sdbusplus::exception_t& ex) 652e7efe135SRashmica Gupta { 653e7efe135SRashmica Gupta // If entity manager isn't running yet, keep going. 654e7efe135SRashmica Gupta if (std::string("org.freedesktop.DBus.Error.ServiceUnknown") != 655e7efe135SRashmica Gupta ex.name()) 656e7efe135SRashmica Gupta { 657e7efe135SRashmica Gupta throw ex.name(); 658e7efe135SRashmica Gupta } 659e7efe135SRashmica Gupta } 660e7efe135SRashmica Gupta 661e7efe135SRashmica Gupta return objects; 662e7efe135SRashmica Gupta } 663e7efe135SRashmica Gupta 6648e11cccbSPatrick Williams void VirtualSensors::propertiesChanged(sdbusplus::message_t& msg) 665e7efe135SRashmica Gupta { 666e7efe135SRashmica Gupta std::string path; 667e7efe135SRashmica Gupta PropertyMap properties; 668e7efe135SRashmica Gupta 669e7efe135SRashmica Gupta msg.read(path, properties); 670e7efe135SRashmica Gupta 671e7efe135SRashmica Gupta /* We get multiple callbacks for one sensor. 'Type' is a required field and 672e7efe135SRashmica Gupta * is a unique label so use to to only proceed once per sensor */ 673e7efe135SRashmica Gupta if (properties.contains("Type")) 674e7efe135SRashmica Gupta { 675e7efe135SRashmica Gupta if (isCalculationType(path)) 676e7efe135SRashmica Gupta { 677e7efe135SRashmica Gupta createVirtualSensorsFromDBus(path); 678e7efe135SRashmica Gupta } 679e7efe135SRashmica Gupta } 680e7efe135SRashmica Gupta } 681e7efe135SRashmica Gupta 682abcc94faSVijay Khemka /** @brief Parsing Virtual Sensor config JSON file */ 6831204b433SGeorge Liu Json VirtualSensors::parseConfigFile(const std::string& configFile) 684abcc94faSVijay Khemka { 685abcc94faSVijay Khemka std::ifstream jsonFile(configFile); 686abcc94faSVijay Khemka if (!jsonFile.is_open()) 687abcc94faSVijay Khemka { 68882b39c66SPatrick Williams error("config JSON file {FILENAME} not found", "FILENAME", configFile); 689e7efe135SRashmica Gupta return {}; 690abcc94faSVijay Khemka } 691abcc94faSVijay Khemka 692abcc94faSVijay Khemka auto data = Json::parse(jsonFile, nullptr, false); 693abcc94faSVijay Khemka if (data.is_discarded()) 694abcc94faSVijay Khemka { 69582b39c66SPatrick Williams error("config readings JSON parser failure with {FILENAME}", "FILENAME", 69682b39c66SPatrick Williams configFile); 697abcc94faSVijay Khemka throw std::exception{}; 698abcc94faSVijay Khemka } 699abcc94faSVijay Khemka 700abcc94faSVijay Khemka return data; 701abcc94faSVijay Khemka } 702abcc94faSVijay Khemka 703e0d371e4SVijay Khemka std::map<std::string, ValueIface::Unit> unitMap = { 704e0d371e4SVijay Khemka {"temperature", ValueIface::Unit::DegreesC}, 705e0d371e4SVijay Khemka {"fan_tach", ValueIface::Unit::RPMS}, 706e0d371e4SVijay Khemka {"voltage", ValueIface::Unit::Volts}, 707e0d371e4SVijay Khemka {"altitude", ValueIface::Unit::Meters}, 708e0d371e4SVijay Khemka {"current", ValueIface::Unit::Amperes}, 709e0d371e4SVijay Khemka {"power", ValueIface::Unit::Watts}, 710e0d371e4SVijay Khemka {"energy", ValueIface::Unit::Joules}, 7112b56ddb3SKumar Thangavel {"utilization", ValueIface::Unit::Percent}, 7124ac7a7f2SRashmica Gupta {"airflow", ValueIface::Unit::CFM}, 7134ac7a7f2SRashmica Gupta {"pressure", ValueIface::Unit::Pascals}}; 714e0d371e4SVijay Khemka 715e7efe135SRashmica Gupta const std::string getSensorTypeFromUnit(const std::string& unit) 716e7efe135SRashmica Gupta { 717e7efe135SRashmica Gupta std::string unitPrefix = "xyz.openbmc_project.Sensor.Value.Unit."; 718e7efe135SRashmica Gupta for (auto [type, unitObj] : unitMap) 719e7efe135SRashmica Gupta { 720e7efe135SRashmica Gupta auto unitPath = ValueIface::convertUnitToString(unitObj); 721e7efe135SRashmica Gupta if (unitPath == (unitPrefix + unit)) 722e7efe135SRashmica Gupta { 723e7efe135SRashmica Gupta return type; 724e7efe135SRashmica Gupta } 725e7efe135SRashmica Gupta } 726e7efe135SRashmica Gupta return ""; 727e7efe135SRashmica Gupta } 728e7efe135SRashmica Gupta 729e7efe135SRashmica Gupta void VirtualSensors::setupMatches() 730e7efe135SRashmica Gupta { 731e7efe135SRashmica Gupta /* Already setup */ 732e7efe135SRashmica Gupta if (!this->matches.empty()) 733e7efe135SRashmica Gupta { 734e7efe135SRashmica Gupta return; 735e7efe135SRashmica Gupta } 736e7efe135SRashmica Gupta 737e7efe135SRashmica Gupta /* Setup matches */ 7388e11cccbSPatrick Williams auto eventHandler = [this](sdbusplus::message_t& message) { 739e7efe135SRashmica Gupta if (message.is_method_error()) 740e7efe135SRashmica Gupta { 74182b39c66SPatrick Williams error("Callback method error"); 742e7efe135SRashmica Gupta return; 743e7efe135SRashmica Gupta } 744e7efe135SRashmica Gupta this->propertiesChanged(message); 745e7efe135SRashmica Gupta }; 746e7efe135SRashmica Gupta 747e7efe135SRashmica Gupta for (const char* iface : calculationIfaces) 748e7efe135SRashmica Gupta { 7498e11cccbSPatrick Williams auto match = std::make_unique<sdbusplus::bus::match_t>( 750e7efe135SRashmica Gupta bus, 751e7efe135SRashmica Gupta sdbusplus::bus::match::rules::propertiesChangedNamespace( 752e7efe135SRashmica Gupta "/xyz/openbmc_project/inventory", iface), 753e7efe135SRashmica Gupta eventHandler); 754e7efe135SRashmica Gupta this->matches.emplace_back(std::move(match)); 755e7efe135SRashmica Gupta } 756e7efe135SRashmica Gupta } 757e7efe135SRashmica Gupta 758e7efe135SRashmica Gupta void VirtualSensors::createVirtualSensorsFromDBus( 759e7efe135SRashmica Gupta const std::string& calculationIface) 760e7efe135SRashmica Gupta { 761e7efe135SRashmica Gupta if (calculationIface.empty()) 762e7efe135SRashmica Gupta { 76382b39c66SPatrick Williams error("No calculation type supplied"); 764e7efe135SRashmica Gupta return; 765e7efe135SRashmica Gupta } 766e7efe135SRashmica Gupta auto objects = getObjectsFromDBus(); 767e7efe135SRashmica Gupta 768e7efe135SRashmica Gupta /* Get virtual sensors config data */ 769e7efe135SRashmica Gupta for (const auto& [path, interfaceMap] : objects) 770e7efe135SRashmica Gupta { 771e7efe135SRashmica Gupta auto objpath = static_cast<std::string>(path); 772e7efe135SRashmica Gupta std::string name = path.filename(); 773e7efe135SRashmica Gupta std::string sensorType, sensorUnit; 774e7efe135SRashmica Gupta 775e7efe135SRashmica Gupta /* Find Virtual Sensor interfaces */ 776e7efe135SRashmica Gupta if (!interfaceMap.contains(calculationIface)) 777e7efe135SRashmica Gupta { 778e7efe135SRashmica Gupta continue; 779e7efe135SRashmica Gupta } 780e7efe135SRashmica Gupta if (name.empty()) 781e7efe135SRashmica Gupta { 78282b39c66SPatrick Williams error("Virtual Sensor name not found in entity manager config"); 783e7efe135SRashmica Gupta continue; 784e7efe135SRashmica Gupta } 785e7efe135SRashmica Gupta if (virtualSensorsMap.contains(name)) 786e7efe135SRashmica Gupta { 78782b39c66SPatrick Williams error("A virtual sensor named {NAME} already exists", "NAME", name); 788e7efe135SRashmica Gupta continue; 789e7efe135SRashmica Gupta } 790e7efe135SRashmica Gupta 791e7efe135SRashmica Gupta /* Extract the virtual sensor type as we need this to initialize the 792e7efe135SRashmica Gupta * sensor */ 793e7efe135SRashmica Gupta for (const auto& [interface, propertyMap] : interfaceMap) 794e7efe135SRashmica Gupta { 795e7efe135SRashmica Gupta if (interface != calculationIface) 796e7efe135SRashmica Gupta { 797e7efe135SRashmica Gupta continue; 798e7efe135SRashmica Gupta } 799e7efe135SRashmica Gupta auto itr = propertyMap.find("Units"); 800e7efe135SRashmica Gupta if (itr != propertyMap.end()) 801e7efe135SRashmica Gupta { 802e7efe135SRashmica Gupta sensorUnit = std::get<std::string>(itr->second); 803e7efe135SRashmica Gupta break; 804e7efe135SRashmica Gupta } 805e7efe135SRashmica Gupta } 806e7efe135SRashmica Gupta sensorType = getSensorTypeFromUnit(sensorUnit); 807e7efe135SRashmica Gupta if (sensorType.empty()) 808e7efe135SRashmica Gupta { 80982b39c66SPatrick Williams error("Sensor unit type {TYPE} is not supported", "TYPE", 81082b39c66SPatrick Williams sensorUnit); 811e7efe135SRashmica Gupta continue; 812e7efe135SRashmica Gupta } 813e7efe135SRashmica Gupta 814e7efe135SRashmica Gupta try 815e7efe135SRashmica Gupta { 816e7efe135SRashmica Gupta auto virtObjPath = sensorDbusPath + sensorType + "/" + name; 817e7efe135SRashmica Gupta 818e7efe135SRashmica Gupta auto virtualSensorPtr = std::make_unique<VirtualSensor>( 819e7efe135SRashmica Gupta bus, virtObjPath.c_str(), interfaceMap, name, sensorType, 820*dc777015STao Lin calculationIface, objpath); 82182b39c66SPatrick Williams info("Added a new virtual sensor: {NAME} {TYPE}", "NAME", name, 82282b39c66SPatrick Williams "TYPE", sensorType); 823e7efe135SRashmica Gupta virtualSensorPtr->updateVirtualSensor(); 824e7efe135SRashmica Gupta 825e7efe135SRashmica Gupta /* Initialize unit value for virtual sensor */ 826e7efe135SRashmica Gupta virtualSensorPtr->ValueIface::unit(unitMap[sensorType]); 827e7efe135SRashmica Gupta virtualSensorPtr->emit_object_added(); 828e7efe135SRashmica Gupta 829e7efe135SRashmica Gupta virtualSensorsMap.emplace(name, std::move(virtualSensorPtr)); 830e7efe135SRashmica Gupta 831e7efe135SRashmica Gupta /* Setup match for interfaces removed */ 832e7efe135SRashmica Gupta auto intfRemoved = [this, objpath, 8338e11cccbSPatrick Williams name](sdbusplus::message_t& message) { 834e7efe135SRashmica Gupta if (!virtualSensorsMap.contains(name)) 835e7efe135SRashmica Gupta { 836e7efe135SRashmica Gupta return; 837e7efe135SRashmica Gupta } 838e7efe135SRashmica Gupta sdbusplus::message::object_path path; 839e7efe135SRashmica Gupta message.read(path); 840e7efe135SRashmica Gupta if (static_cast<const std::string&>(path) == objpath) 841e7efe135SRashmica Gupta { 84282b39c66SPatrick Williams info("Removed a virtual sensor: {NAME}", "NAME", name); 843e7efe135SRashmica Gupta virtualSensorsMap.erase(name); 844e7efe135SRashmica Gupta } 845e7efe135SRashmica Gupta }; 8468e11cccbSPatrick Williams auto matchOnRemove = std::make_unique<sdbusplus::bus::match_t>( 847e7efe135SRashmica Gupta bus, 848e7efe135SRashmica Gupta sdbusplus::bus::match::rules::interfacesRemoved() + 849e7efe135SRashmica Gupta sdbusplus::bus::match::rules::argNpath(0, objpath), 850e7efe135SRashmica Gupta intfRemoved); 851e7efe135SRashmica Gupta /* TODO: slight race condition here. Check that the config still 852e7efe135SRashmica Gupta * exists */ 853e7efe135SRashmica Gupta this->matches.emplace_back(std::move(matchOnRemove)); 854e7efe135SRashmica Gupta } 855dac2663cSPatrick Williams catch (const std::invalid_argument& ia) 856e7efe135SRashmica Gupta { 85782b39c66SPatrick Williams error("Failed to set up virtual sensor: {ERROR}", "ERROR", ia); 858e7efe135SRashmica Gupta } 859e7efe135SRashmica Gupta } 860e7efe135SRashmica Gupta } 861e7efe135SRashmica Gupta 862abcc94faSVijay Khemka void VirtualSensors::createVirtualSensors() 863abcc94faSVijay Khemka { 864abcc94faSVijay Khemka static const Json empty{}; 865abcc94faSVijay Khemka 866abcc94faSVijay Khemka auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE); 867e7efe135SRashmica Gupta 868abcc94faSVijay Khemka // print values 869abcc94faSVijay Khemka if (DEBUG) 870e7efe135SRashmica Gupta { 871fbd7145eSPatrick Williams debug("JSON: {JSON}", "JSON", data.dump()); 872e7efe135SRashmica Gupta } 873abcc94faSVijay Khemka 874abcc94faSVijay Khemka /* Get virtual sensors config data */ 875abcc94faSVijay Khemka for (const auto& j : data) 876abcc94faSVijay Khemka { 877abcc94faSVijay Khemka auto desc = j.value("Desc", empty); 878abcc94faSVijay Khemka if (!desc.empty()) 879abcc94faSVijay Khemka { 880e7efe135SRashmica Gupta if (desc.value("Config", "") == "D-Bus") 881e7efe135SRashmica Gupta { 882e7efe135SRashmica Gupta /* Look on D-Bus for a virtual sensor config. Set up matches 883e7efe135SRashmica Gupta * first because the configs may not be on D-Bus yet and we 884e7efe135SRashmica Gupta * don't want to miss them */ 885e7efe135SRashmica Gupta setupMatches(); 886e7efe135SRashmica Gupta 887e7efe135SRashmica Gupta if (desc.contains("Type")) 888e7efe135SRashmica Gupta { 88982b39c66SPatrick Williams auto type = desc.value("Type", ""); 89082b39c66SPatrick Williams auto path = "xyz.openbmc_project.Configuration." + type; 89182b39c66SPatrick Williams 892e7efe135SRashmica Gupta if (!isCalculationType(path)) 893e7efe135SRashmica Gupta { 89482b39c66SPatrick Williams error("Invalid calculation type {TYPE} supplied.", 89582b39c66SPatrick Williams "TYPE", type); 896e7efe135SRashmica Gupta continue; 897e7efe135SRashmica Gupta } 898e7efe135SRashmica Gupta createVirtualSensorsFromDBus(path); 899e7efe135SRashmica Gupta } 900e7efe135SRashmica Gupta continue; 901e7efe135SRashmica Gupta } 902e7efe135SRashmica Gupta 903abcc94faSVijay Khemka std::string sensorType = desc.value("SensorType", ""); 904abcc94faSVijay Khemka std::string name = desc.value("Name", ""); 905665a0a29SRashmica Gupta std::replace(name.begin(), name.end(), ' ', '_'); 906abcc94faSVijay Khemka 907abcc94faSVijay Khemka if (!name.empty() && !sensorType.empty()) 908abcc94faSVijay Khemka { 909e0d371e4SVijay Khemka if (unitMap.find(sensorType) == unitMap.end()) 910e0d371e4SVijay Khemka { 91182b39c66SPatrick Williams error("Sensor type {TYPE} is not supported", "TYPE", 91282b39c66SPatrick Williams sensorType); 913e0d371e4SVijay Khemka } 914e0d371e4SVijay Khemka else 915e0d371e4SVijay Khemka { 91667d8b9d2SRashmica Gupta if (virtualSensorsMap.find(name) != virtualSensorsMap.end()) 91767d8b9d2SRashmica Gupta { 91882b39c66SPatrick Williams error("A virtual sensor named {NAME} already exists", 91982b39c66SPatrick Williams "NAME", name); 92067d8b9d2SRashmica Gupta continue; 92167d8b9d2SRashmica Gupta } 922862c3d1eSRashmica Gupta auto objPath = sensorDbusPath + sensorType + "/" + name; 923abcc94faSVijay Khemka 92432a7156bSVijay Khemka auto virtualSensorPtr = std::make_unique<VirtualSensor>( 92532a7156bSVijay Khemka bus, objPath.c_str(), j, name); 926abcc94faSVijay Khemka 92782b39c66SPatrick Williams info("Added a new virtual sensor: {NAME}", "NAME", name); 9283ed9a516SVijay Khemka virtualSensorPtr->updateVirtualSensor(); 929e0d371e4SVijay Khemka 930e0d371e4SVijay Khemka /* Initialize unit value for virtual sensor */ 931e0d371e4SVijay Khemka virtualSensorPtr->ValueIface::unit(unitMap[sensorType]); 932a2fa63a6SRashmica Gupta virtualSensorPtr->emit_object_added(); 933e0d371e4SVijay Khemka 9343ed9a516SVijay Khemka virtualSensorsMap.emplace(std::move(name), 9353ed9a516SVijay Khemka std::move(virtualSensorPtr)); 936abcc94faSVijay Khemka } 937e0d371e4SVijay Khemka } 938abcc94faSVijay Khemka else 939abcc94faSVijay Khemka { 94082b39c66SPatrick Williams error( 94182b39c66SPatrick Williams "Sensor type ({TYPE}) or name ({NAME}) not found in config file", 94282b39c66SPatrick Williams "NAME", name, "TYPE", sensorType); 943abcc94faSVijay Khemka } 944abcc94faSVijay Khemka } 945abcc94faSVijay Khemka else 946abcc94faSVijay Khemka { 94782b39c66SPatrick Williams error("Descriptor for new virtual sensor not found in config file"); 948abcc94faSVijay Khemka } 949abcc94faSVijay Khemka } 950abcc94faSVijay Khemka } 951abcc94faSVijay Khemka 952abcc94faSVijay Khemka } // namespace virtualSensor 953abcc94faSVijay Khemka } // namespace phosphor 954abcc94faSVijay Khemka 955abcc94faSVijay Khemka /** 956abcc94faSVijay Khemka * @brief Main 957abcc94faSVijay Khemka */ 958abcc94faSVijay Khemka int main() 959abcc94faSVijay Khemka { 960abcc94faSVijay Khemka // Get a handle to system dbus 961abcc94faSVijay Khemka auto bus = sdbusplus::bus::new_default(); 962abcc94faSVijay Khemka 9636c19e7d2SMatt Spinler // Add the ObjectManager interface 9648e11cccbSPatrick Williams sdbusplus::server::manager_t objManager(bus, "/"); 9656c19e7d2SMatt Spinler 966abcc94faSVijay Khemka // Create an virtual sensors object 967abcc94faSVijay Khemka phosphor::virtualSensor::VirtualSensors virtualSensors(bus); 968abcc94faSVijay Khemka 969abcc94faSVijay Khemka // Request service bus name 970abcc94faSVijay Khemka bus.request_name(busName); 971abcc94faSVijay Khemka 972e667239dSPatrick Williams // Run the dbus loop. 973e667239dSPatrick Williams bus.process_loop(); 974abcc94faSVijay Khemka 975abcc94faSVijay Khemka return 0; 976abcc94faSVijay Khemka } 977