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