xref: /openbmc/phosphor-virtual-sensor/src/virtualSensor.hpp (revision 6272a39308bf6c1945edabf54891157c8079165a)
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