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