1*6272a393SAlexander Hansen #pragma once 2*6272a393SAlexander Hansen 3*6272a393SAlexander Hansen #include "dbusSensor.hpp" 4*6272a393SAlexander Hansen #include "exprtkTools.hpp" 5*6272a393SAlexander Hansen #include "thresholds.hpp" 6*6272a393SAlexander Hansen 7*6272a393SAlexander Hansen #include <nlohmann/json.hpp> 8*6272a393SAlexander Hansen #include <phosphor-logging/lg2.hpp> 9*6272a393SAlexander Hansen #include <sdbusplus/bus.hpp> 10*6272a393SAlexander Hansen #include <xyz/openbmc_project/Association/Definitions/server.hpp> 11*6272a393SAlexander Hansen #include <xyz/openbmc_project/Sensor/Value/server.hpp> 12*6272a393SAlexander Hansen 13*6272a393SAlexander Hansen #include <map> 14*6272a393SAlexander Hansen #include <string> 15*6272a393SAlexander Hansen 16*6272a393SAlexander Hansen namespace phosphor::virtual_sensor 17*6272a393SAlexander Hansen { 18*6272a393SAlexander Hansen 19*6272a393SAlexander Hansen PHOSPHOR_LOG2_USING_WITH_FLAGS; 20*6272a393SAlexander Hansen 21*6272a393SAlexander Hansen using BasicVariantType = 22*6272a393SAlexander Hansen std::variant<std::string, int64_t, uint64_t, double, int32_t, uint32_t, 23*6272a393SAlexander Hansen int16_t, uint16_t, uint8_t, bool, std::vector<std::string>>; 24*6272a393SAlexander Hansen 25*6272a393SAlexander Hansen using PropertyMap = std::map<std::string, BasicVariantType>; 26*6272a393SAlexander Hansen 27*6272a393SAlexander Hansen using InterfaceMap = std::map<std::string, PropertyMap>; 28*6272a393SAlexander Hansen 29*6272a393SAlexander Hansen using ManagedObjectType = 30*6272a393SAlexander Hansen std::map<sdbusplus::message::object_path, InterfaceMap>; 31*6272a393SAlexander Hansen 32*6272a393SAlexander Hansen using Json = nlohmann::json; 33*6272a393SAlexander Hansen 34*6272a393SAlexander Hansen template <typename... T> 35*6272a393SAlexander Hansen using ServerObject = typename sdbusplus::server::object_t<T...>; 36*6272a393SAlexander Hansen 37*6272a393SAlexander Hansen using ValueIface = sdbusplus::xyz::openbmc_project::Sensor::server::Value; 38*6272a393SAlexander Hansen using ValueObject = ServerObject<ValueIface>; 39*6272a393SAlexander Hansen 40*6272a393SAlexander Hansen using AssociationIface = 41*6272a393SAlexander Hansen sdbusplus::xyz::openbmc_project::Association::server::Definitions; 42*6272a393SAlexander Hansen using AssociationObject = ServerObject<AssociationIface>; 43*6272a393SAlexander Hansen 44*6272a393SAlexander Hansen class SensorParam 45*6272a393SAlexander Hansen { 46*6272a393SAlexander Hansen public: 47*6272a393SAlexander Hansen SensorParam() = delete; 48*6272a393SAlexander Hansen virtual ~SensorParam() = default; 49*6272a393SAlexander Hansen 50*6272a393SAlexander Hansen enum ParamType 51*6272a393SAlexander Hansen { 52*6272a393SAlexander Hansen constParam, 53*6272a393SAlexander Hansen dbusParam 54*6272a393SAlexander Hansen }; 55*6272a393SAlexander Hansen 56*6272a393SAlexander Hansen /** @brief Constructs SensorParam (type = constParam) 57*6272a393SAlexander Hansen * 58*6272a393SAlexander Hansen * @param[in] value - Value of constant parameter 59*6272a393SAlexander Hansen */ SensorParam(double value)60*6272a393SAlexander Hansen explicit SensorParam(double value) : value(value), paramType(constParam) {} 61*6272a393SAlexander Hansen 62*6272a393SAlexander Hansen /** @brief Constructs SensorParam (type = dbusParam) 63*6272a393SAlexander Hansen * 64*6272a393SAlexander Hansen * @param[in] bus - Handle to system dbus 65*6272a393SAlexander Hansen * @param[in] path - The Dbus path of sensor 66*6272a393SAlexander Hansen * @param[in] ctx - sensor context for update 67*6272a393SAlexander Hansen */ SensorParam(sdbusplus::bus_t & bus,const std::string & path,VirtualSensor & ctx)68*6272a393SAlexander Hansen SensorParam(sdbusplus::bus_t& bus, const std::string& path, 69*6272a393SAlexander Hansen VirtualSensor& ctx) : 70*6272a393SAlexander Hansen dbusSensor(std::make_unique<DbusSensor>(bus, path, ctx)), 71*6272a393SAlexander Hansen paramType(dbusParam) 72*6272a393SAlexander Hansen {} 73*6272a393SAlexander Hansen 74*6272a393SAlexander Hansen /** @brief Get sensor value property from D-bus interface */ 75*6272a393SAlexander Hansen double getParamValue(); 76*6272a393SAlexander Hansen 77*6272a393SAlexander Hansen private: 78*6272a393SAlexander Hansen std::unique_ptr<DbusSensor> dbusSensor = nullptr; 79*6272a393SAlexander Hansen 80*6272a393SAlexander Hansen /** @brief virtual sensor value */ 81*6272a393SAlexander Hansen double value = std::numeric_limits<double>::quiet_NaN(); 82*6272a393SAlexander Hansen ParamType paramType; 83*6272a393SAlexander Hansen }; 84*6272a393SAlexander Hansen 85*6272a393SAlexander Hansen class VirtualSensor : public ValueObject 86*6272a393SAlexander Hansen { 87*6272a393SAlexander Hansen public: 88*6272a393SAlexander Hansen VirtualSensor() = delete; 89*6272a393SAlexander Hansen virtual ~VirtualSensor() = default; 90*6272a393SAlexander Hansen 91*6272a393SAlexander Hansen /** @brief Constructs VirtualSensor 92*6272a393SAlexander Hansen * 93*6272a393SAlexander Hansen * @param[in] bus - Handle to system dbus 94*6272a393SAlexander Hansen * @param[in] objPath - The Dbus path of sensor 95*6272a393SAlexander Hansen * @param[in] sensorConfig - Json object for sensor config 96*6272a393SAlexander Hansen * @param[in] name - Sensor name 97*6272a393SAlexander Hansen * @param[in] type - sensor type/unit 98*6272a393SAlexander Hansen */ VirtualSensor(sdbusplus::bus_t & bus,const char * objPath,const Json & sensorConfig,const std::string & name,const std::string & type)99*6272a393SAlexander Hansen VirtualSensor(sdbusplus::bus_t& bus, const char* objPath, 100*6272a393SAlexander Hansen const Json& sensorConfig, const std::string& name, 101*6272a393SAlexander Hansen const std::string& type) : 102*6272a393SAlexander Hansen ValueObject(bus, objPath, action::defer_emit), bus(bus), name(name), 103*6272a393SAlexander Hansen objPath(objPath) 104*6272a393SAlexander Hansen { 105*6272a393SAlexander Hansen initVirtualSensor(sensorConfig, objPath, type); 106*6272a393SAlexander Hansen } 107*6272a393SAlexander Hansen 108*6272a393SAlexander Hansen /** @brief Constructs VirtualSensor 109*6272a393SAlexander Hansen * 110*6272a393SAlexander Hansen * @param[in] bus - Handle to system dbus 111*6272a393SAlexander Hansen * @param[in] objPath - The Dbus path of sensor 112*6272a393SAlexander Hansen * @param[in] ifacemap - All the sensor information 113*6272a393SAlexander Hansen * @param[in] name - Virtual sensor name 114*6272a393SAlexander Hansen * @param[in] type - Virtual sensor type/unit 115*6272a393SAlexander Hansen * @param[in] calcType - Calculation used to calculate sensor value 116*6272a393SAlexander Hansen * @param[in] entityPath - Virtual sensor path in entityManager Dbus 117*6272a393SAlexander Hansen * 118*6272a393SAlexander Hansen */ VirtualSensor(sdbusplus::bus_t & bus,const char * objPath,const InterfaceMap & ifacemap,const std::string & name,const std::string & type,const std::string & calculationType,const std::string & entityPath)119*6272a393SAlexander Hansen VirtualSensor(sdbusplus::bus_t& bus, const char* objPath, 120*6272a393SAlexander Hansen const InterfaceMap& ifacemap, const std::string& name, 121*6272a393SAlexander Hansen const std::string& type, const std::string& calculationType, 122*6272a393SAlexander Hansen const std::string& entityPath) : 123*6272a393SAlexander Hansen ValueObject(bus, objPath, action::defer_emit), bus(bus), name(name), 124*6272a393SAlexander Hansen objPath(objPath), entityPath(entityPath) 125*6272a393SAlexander Hansen { 126*6272a393SAlexander Hansen initVirtualSensor(ifacemap, objPath, type, calculationType); 127*6272a393SAlexander Hansen } 128*6272a393SAlexander Hansen 129*6272a393SAlexander Hansen /** @brief Set sensor value */ 130*6272a393SAlexander Hansen void setSensorValue(double value); 131*6272a393SAlexander Hansen 132*6272a393SAlexander Hansen /** @brief Update sensor at regular intrval */ 133*6272a393SAlexander Hansen void updateVirtualSensor(); 134*6272a393SAlexander Hansen 135*6272a393SAlexander Hansen /** @brief Check if sensor value is in valid range */ 136*6272a393SAlexander Hansen bool sensorInRange(double value); 137*6272a393SAlexander Hansen 138*6272a393SAlexander Hansen /** @brief Map of list of parameters */ 139*6272a393SAlexander Hansen using ParamMap = 140*6272a393SAlexander Hansen std::unordered_map<std::string, std::unique_ptr<SensorParam>>; 141*6272a393SAlexander Hansen ParamMap paramMap; 142*6272a393SAlexander Hansen 143*6272a393SAlexander Hansen private: 144*6272a393SAlexander Hansen /** @brief sdbusplus bus client connection. */ 145*6272a393SAlexander Hansen sdbusplus::bus_t& bus; 146*6272a393SAlexander Hansen /** @brief name of sensor */ 147*6272a393SAlexander Hansen std::string name; 148*6272a393SAlexander Hansen /** @brief unit of sensor */ 149*6272a393SAlexander Hansen ValueIface::Unit units; 150*6272a393SAlexander Hansen /** @brief object path of this sensor */ 151*6272a393SAlexander Hansen std::string objPath; 152*6272a393SAlexander Hansen 153*6272a393SAlexander Hansen /** @brief Virtual sensor path in entityManager Dbus. 154*6272a393SAlexander Hansen * This value is used to set thresholds/create association 155*6272a393SAlexander Hansen */ 156*6272a393SAlexander Hansen std::string entityPath; 157*6272a393SAlexander Hansen /** @brief Expression string for virtual sensor value calculations */ 158*6272a393SAlexander Hansen std::string exprStr; 159*6272a393SAlexander Hansen /** @brief symbol table from exprtk */ 160*6272a393SAlexander Hansen exprtk::symbol_table<double> symbols{}; 161*6272a393SAlexander Hansen /** @brief expression from exprtk to calculate sensor value */ 162*6272a393SAlexander Hansen exprtk::expression<double> expression{}; 163*6272a393SAlexander Hansen /** @brief The vecops package so the expression can use vectors */ 164*6272a393SAlexander Hansen exprtk::rtl::vecops::package<double> vecopsPackage; 165*6272a393SAlexander Hansen /** @brief The maximum valid value for an input sensor **/ 166*6272a393SAlexander Hansen double maxValidInput = std::numeric_limits<double>::infinity(); 167*6272a393SAlexander Hansen /** @brief The minimum valid value for an input sensor **/ 168*6272a393SAlexander Hansen double minValidInput = -std::numeric_limits<double>::infinity(); 169*6272a393SAlexander Hansen 170*6272a393SAlexander Hansen /** @brief The critical threshold interface object */ 171*6272a393SAlexander Hansen std::unique_ptr<Threshold<CriticalObject>> criticalIface; 172*6272a393SAlexander Hansen /** @brief The warning threshold interface object */ 173*6272a393SAlexander Hansen std::unique_ptr<Threshold<WarningObject>> warningIface; 174*6272a393SAlexander Hansen /** @brief The soft shutdown threshold interface object */ 175*6272a393SAlexander Hansen std::unique_ptr<Threshold<SoftShutdownObject>> softShutdownIface; 176*6272a393SAlexander Hansen /** @brief The hard shutdown threshold interface object */ 177*6272a393SAlexander Hansen std::unique_ptr<Threshold<HardShutdownObject>> hardShutdownIface; 178*6272a393SAlexander Hansen /** @brief The performance loss threshold interface object */ 179*6272a393SAlexander Hansen std::unique_ptr<Threshold<PerformanceLossObject>> perfLossIface; 180*6272a393SAlexander Hansen 181*6272a393SAlexander Hansen /** @brief The association interface object */ 182*6272a393SAlexander Hansen std::unique_ptr<AssociationObject> associationIface; 183*6272a393SAlexander Hansen 184*6272a393SAlexander Hansen static FuncMaxIgnoreNaN<double> funcMaxIgnoreNaN; 185*6272a393SAlexander Hansen static FuncSumIgnoreNaN<double> funcSumIgnoreNaN; 186*6272a393SAlexander Hansen static FuncIfNan<double> funcIfNan; 187*6272a393SAlexander Hansen 188*6272a393SAlexander Hansen /** @brief Read config from json object and initialize sensor data 189*6272a393SAlexander Hansen * for each virtual sensor 190*6272a393SAlexander Hansen */ 191*6272a393SAlexander Hansen void initVirtualSensor(const Json& sensorConfig, const std::string& objPath, 192*6272a393SAlexander Hansen const std::string& type); 193*6272a393SAlexander Hansen 194*6272a393SAlexander Hansen /** @brief Read config from interface map and initialize sensor data 195*6272a393SAlexander Hansen * for each virtual sensor 196*6272a393SAlexander Hansen */ 197*6272a393SAlexander Hansen void initVirtualSensor(const InterfaceMap& interfaceMap, 198*6272a393SAlexander Hansen const std::string& objPath, 199*6272a393SAlexander Hansen const std::string& sensorType, 200*6272a393SAlexander Hansen const std::string& calculationType); 201*6272a393SAlexander Hansen 202*6272a393SAlexander Hansen /** @brief Returns which calculation function or expression to use */ 203*6272a393SAlexander Hansen double calculateValue(const std::string& sensortype, 204*6272a393SAlexander Hansen const VirtualSensor::ParamMap& paramMap); 205*6272a393SAlexander Hansen /** @brief create threshold objects from json config */ 206*6272a393SAlexander Hansen void createThresholds(const Json& threshold, const std::string& objPath, 207*6272a393SAlexander Hansen ValueIface::Unit units); 208*6272a393SAlexander Hansen /** @brief parse config from entity manager **/ 209*6272a393SAlexander Hansen void parseConfigInterface(const PropertyMap& propertyMap, 210*6272a393SAlexander Hansen const std::string& sensorType, 211*6272a393SAlexander Hansen const std::string& interface); 212*6272a393SAlexander Hansen 213*6272a393SAlexander Hansen /** @brief Check Sensor threshold and update alarm and log. Returns 214*6272a393SAlexander Hansen * true if the threshold range has no alarms set. change will be 215*6272a393SAlexander Hansen * set if a change to the alarms were detected, else will be left 216*6272a393SAlexander Hansen * unchanged */ 217*6272a393SAlexander Hansen template <typename V, typename T> checkThresholds(V value,T & threshold,bool & change)218*6272a393SAlexander Hansen bool checkThresholds(V value, T& threshold, bool& change) 219*6272a393SAlexander Hansen { 220*6272a393SAlexander Hansen if (!threshold) 221*6272a393SAlexander Hansen return true; 222*6272a393SAlexander Hansen 223*6272a393SAlexander Hansen static constexpr auto tname = T::element_type::name; 224*6272a393SAlexander Hansen 225*6272a393SAlexander Hansen auto alarmHigh = threshold->alarmHigh(); 226*6272a393SAlexander Hansen auto highHysteresis = threshold->getHighHysteresis(); 227*6272a393SAlexander Hansen if ((!alarmHigh && value >= threshold->high()) || 228*6272a393SAlexander Hansen (alarmHigh && value < (threshold->high() - highHysteresis))) 229*6272a393SAlexander Hansen { 230*6272a393SAlexander Hansen change = true; 231*6272a393SAlexander Hansen if (!alarmHigh) 232*6272a393SAlexander Hansen { 233*6272a393SAlexander Hansen error("ASSERT: sensor {SENSOR} is above the upper threshold " 234*6272a393SAlexander Hansen "{THRESHOLD}.", 235*6272a393SAlexander Hansen "SENSOR", name, "THRESHOLD", tname); 236*6272a393SAlexander Hansen threshold->alarmHighSignalAsserted(value); 237*6272a393SAlexander Hansen } 238*6272a393SAlexander Hansen else 239*6272a393SAlexander Hansen { 240*6272a393SAlexander Hansen info("DEASSERT: sensor {SENSOR} is under the upper threshold " 241*6272a393SAlexander Hansen "{THRESHOLD}.", 242*6272a393SAlexander Hansen "SENSOR", name, "THRESHOLD", tname); 243*6272a393SAlexander Hansen threshold->alarmHighSignalDeasserted(value); 244*6272a393SAlexander Hansen } 245*6272a393SAlexander Hansen alarmHigh = !alarmHigh; 246*6272a393SAlexander Hansen threshold->alarmHigh(alarmHigh); 247*6272a393SAlexander Hansen } 248*6272a393SAlexander Hansen 249*6272a393SAlexander Hansen auto alarmLow = threshold->alarmLow(); 250*6272a393SAlexander Hansen auto lowHysteresis = threshold->getLowHysteresis(); 251*6272a393SAlexander Hansen if ((!alarmLow && value <= threshold->low()) || 252*6272a393SAlexander Hansen (alarmLow && value > (threshold->low() + lowHysteresis))) 253*6272a393SAlexander Hansen { 254*6272a393SAlexander Hansen change = true; 255*6272a393SAlexander Hansen if (!alarmLow) 256*6272a393SAlexander Hansen { 257*6272a393SAlexander Hansen error("ASSERT: sensor {SENSOR} is below the lower threshold " 258*6272a393SAlexander Hansen "{THRESHOLD}.", 259*6272a393SAlexander Hansen "SENSOR", name, "THRESHOLD", tname); 260*6272a393SAlexander Hansen threshold->alarmLowSignalAsserted(value); 261*6272a393SAlexander Hansen } 262*6272a393SAlexander Hansen else 263*6272a393SAlexander Hansen { 264*6272a393SAlexander Hansen info("DEASSERT: sensor {SENSOR} is above the lower threshold " 265*6272a393SAlexander Hansen "{THRESHOLD}.", 266*6272a393SAlexander Hansen "SENSOR", name, "THRESHOLD", tname); 267*6272a393SAlexander Hansen threshold->alarmLowSignalDeasserted(value); 268*6272a393SAlexander Hansen } 269*6272a393SAlexander Hansen alarmLow = !alarmLow; 270*6272a393SAlexander Hansen threshold->alarmLow(alarmLow); 271*6272a393SAlexander Hansen } 272*6272a393SAlexander Hansen return !alarmHigh && !alarmLow; 273*6272a393SAlexander Hansen } 274*6272a393SAlexander Hansen 275*6272a393SAlexander Hansen /** @brief Create Association from entityPath*/ 276*6272a393SAlexander Hansen void createAssociation(const std::string& objPath, 277*6272a393SAlexander Hansen const std::string& entityPath); 278*6272a393SAlexander Hansen }; 279*6272a393SAlexander Hansen 280*6272a393SAlexander Hansen class VirtualSensors 281*6272a393SAlexander Hansen { 282*6272a393SAlexander Hansen public: 283*6272a393SAlexander Hansen VirtualSensors() = delete; 284*6272a393SAlexander Hansen virtual ~VirtualSensors() = default; 285*6272a393SAlexander Hansen 286*6272a393SAlexander Hansen /** @brief Constructs VirtualSensors 287*6272a393SAlexander Hansen * 288*6272a393SAlexander Hansen * @param[in] bus - Handle to system dbus 289*6272a393SAlexander Hansen */ VirtualSensors(sdbusplus::bus_t & bus)290*6272a393SAlexander Hansen explicit VirtualSensors(sdbusplus::bus_t& bus) : bus(bus) 291*6272a393SAlexander Hansen { 292*6272a393SAlexander Hansen createVirtualSensors(); 293*6272a393SAlexander Hansen } 294*6272a393SAlexander Hansen /** @brief Calls createVirtualSensor when interface added */ 295*6272a393SAlexander Hansen void propertiesChanged(sdbusplus::message_t& msg); 296*6272a393SAlexander Hansen 297*6272a393SAlexander Hansen private: 298*6272a393SAlexander Hansen /** @brief sdbusplus bus client connection. */ 299*6272a393SAlexander Hansen sdbusplus::bus_t& bus; 300*6272a393SAlexander Hansen /** @brief Get virtual sensor config from DBus**/ 301*6272a393SAlexander Hansen ManagedObjectType getObjectsFromDBus(); 302*6272a393SAlexander Hansen /** @brief Parsing virtual sensor config JSON file */ 303*6272a393SAlexander Hansen Json parseConfigFile(); 304*6272a393SAlexander Hansen 305*6272a393SAlexander Hansen /** @brief Matches for virtual sensors */ 306*6272a393SAlexander Hansen std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches; 307*6272a393SAlexander Hansen /** @brief Map of the object VirtualSensor */ 308*6272a393SAlexander Hansen std::unordered_map<std::string, std::unique_ptr<VirtualSensor>> 309*6272a393SAlexander Hansen virtualSensorsMap; 310*6272a393SAlexander Hansen 311*6272a393SAlexander Hansen /** @brief Create list of virtual sensors from JSON config*/ 312*6272a393SAlexander Hansen void createVirtualSensors(); 313*6272a393SAlexander Hansen /** @brief Create list of virtual sensors from DBus config */ 314*6272a393SAlexander Hansen void createVirtualSensorsFromDBus(const std::string& calculationType); 315*6272a393SAlexander Hansen /** @brief Setup matches for virtual sensors */ 316*6272a393SAlexander Hansen void setupMatches(); 317*6272a393SAlexander Hansen }; 318*6272a393SAlexander Hansen 319*6272a393SAlexander Hansen } // namespace phosphor::virtual_sensor 320