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 
63     /** @brief Constructs SensorParam (type = dbusParam)
64      *
65      * @param[in] bus     - Handle to system dbus
66      * @param[in] path    - The Dbus path of sensor
67      * @param[in] ctx     - sensor context for update
68      */
69     SensorParam(sdbusplus::bus_t& bus, const std::string& path, void* 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     double value = 0;
80     ParamType paramType;
81 };
82 
83 class VirtualSensor : public ValueObject
84 {
85   public:
86     VirtualSensor() = delete;
87     virtual ~VirtualSensor() = default;
88 
89     /** @brief Constructs VirtualSensor
90      *
91      * @param[in] bus          - Handle to system dbus
92      * @param[in] objPath      - The Dbus path of sensor
93      * @param[in] sensorConfig - Json object for sensor config
94      */
95     VirtualSensor(sdbusplus::bus_t& bus, const char* objPath,
96                   const Json& sensorConfig, const std::string& name) :
97         ValueObject(bus, objPath, action::defer_emit),
98         bus(bus), name(name)
99     {
100         initVirtualSensor(sensorConfig, objPath);
101     }
102 
103     /** @brief Constructs VirtualSensor
104      *
105      * @param[in] bus          - Handle to system dbus
106      * @param[in] objPath      - The Dbus path of sensor
107      * @param[in] ifacemap     - All the sensor information
108      * @param[in] name         - Virtual sensor name
109      * @param[in] type         - Virtual sensor type/unit
110      * @param[in] calcType     - Calculation used to calculate sensor value
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         ValueObject(bus, objPath, action::defer_emit),
117         bus(bus), name(name)
118     {
119         initVirtualSensor(ifacemap, objPath, type, calculationType);
120     }
121 
122     /** @brief Set sensor value */
123     void setSensorValue(double value);
124     /** @brief Update sensor at regular intrval */
125     void updateVirtualSensor();
126     /** @brief Check if sensor value is in valid range */
127     bool sensorInRange(double value);
128 
129     /** @brief Map of list of parameters */
130     using ParamMap =
131         std::unordered_map<std::string, std::unique_ptr<SensorParam>>;
132     ParamMap paramMap;
133 
134   private:
135     /** @brief sdbusplus bus client connection. */
136     sdbusplus::bus_t& bus;
137     /** @brief name of sensor */
138     std::string name;
139     /** @brief Expression string for virtual sensor value calculations */
140     std::string exprStr;
141     /** @brief symbol table from exprtk */
142     exprtk::symbol_table<double> symbols{};
143     /** @brief expression from exprtk to calculate sensor value */
144     exprtk::expression<double> expression{};
145     /** @brief The vecops package so the expression can use vectors */
146     exprtk::rtl::vecops::package<double> vecopsPackage;
147     /** @brief The maximum valid value for an input sensor **/
148     double maxValidInput = std::numeric_limits<double>::infinity();
149     /** @brief The minimum valid value for an input sensor **/
150     double minValidInput = -std::numeric_limits<double>::infinity();
151 
152     /** @brief The critical threshold interface object */
153     std::unique_ptr<Threshold<CriticalObject>> criticalIface;
154     /** @brief The warning threshold interface object */
155     std::unique_ptr<Threshold<WarningObject>> warningIface;
156     /** @brief The soft shutdown threshold interface object */
157     std::unique_ptr<Threshold<SoftShutdownObject>> softShutdownIface;
158     /** @brief The hard shutdown threshold interface object */
159     std::unique_ptr<Threshold<HardShutdownObject>> hardShutdownIface;
160     /** @brief The performance loss threshold interface object */
161     std::unique_ptr<Threshold<PerformanceLossObject>> perfLossIface;
162 
163     /** @brief The association interface object */
164     std::unique_ptr<AssociationObject> associationIface;
165 
166     /** @brief Read config from json object and initialize sensor data
167      * for each virtual sensor
168      */
169     void initVirtualSensor(const Json& sensorConfig,
170                            const std::string& objPath);
171 
172     /** @brief Read config from interface map and initialize sensor data
173      * for each virtual sensor
174      */
175     void initVirtualSensor(const InterfaceMap& interfaceMap,
176                            const std::string& objPath,
177                            const std::string& sensorType,
178                            const std::string& calculationType);
179 
180     /** @brief Returns which calculation function or expression to use */
181     double calculateValue(const std::string& sensortype,
182                           const VirtualSensor::ParamMap& paramMap);
183     /** @brief Calculate median value from sensors */
184     double
185         calculateModifiedMedianValue(const VirtualSensor::ParamMap& paramMap);
186     /** @brief create threshold objects from json config */
187     void createThresholds(const Json& threshold, const std::string& objPath);
188     /** @brief parse config from entity manager **/
189     void parseConfigInterface(const PropertyMap& propertyMap,
190                               const std::string& sensorType,
191                               const std::string& interface);
192 
193     /** @brief Check Sensor threshold and update alarm and log */
194     template <typename V, typename T>
195     void checkThresholds(V value, T& threshold)
196     {
197         if (!threshold)
198             return;
199 
200         static constexpr auto tname = T::element_type::name;
201 
202         auto alarmHigh = threshold->alarmHigh();
203         auto highHysteresis = threshold->getHighHysteresis();
204         if ((!alarmHigh && value >= threshold->high()) ||
205             (alarmHigh && value < (threshold->high() - highHysteresis)))
206         {
207             if (!alarmHigh)
208             {
209                 error("ASSERT: sensor {SENSOR} is above the upper threshold "
210                       "{THRESHOLD}.",
211                       "SENSOR", name, "THRESHOLD", tname);
212                 threshold->alarmHighSignalAsserted(value);
213             }
214             else
215             {
216                 info("DEASSERT: sensor {SENSOR} is under the upper threshold "
217                      "{THRESHOLD}.",
218                      "SENSOR", name, "THRESHOLD", tname);
219                 threshold->alarmHighSignalDeasserted(value);
220             }
221             threshold->alarmHigh(!alarmHigh);
222         }
223 
224         auto alarmLow = threshold->alarmLow();
225         auto lowHysteresis = threshold->getLowHysteresis();
226         if ((!alarmLow && value <= threshold->low()) ||
227             (alarmLow && value > (threshold->low() + lowHysteresis)))
228         {
229             if (!alarmLow)
230             {
231                 error("ASSERT: sensor {SENSOR} is below the lower threshold "
232                       "{THRESHOLD}.",
233                       "SENSOR", name, "THRESHOLD", tname);
234                 threshold->alarmLowSignalAsserted(value);
235             }
236             else
237             {
238                 info("DEASSERT: sensor {SENSOR} is above the lower threshold "
239                      "{THRESHOLD}.",
240                      "SENSOR", name, "THRESHOLD", tname);
241                 threshold->alarmLowSignalDeasserted(value);
242             }
243             threshold->alarmLow(!alarmLow);
244         }
245     }
246 };
247 
248 class VirtualSensors
249 {
250   public:
251     VirtualSensors() = delete;
252     virtual ~VirtualSensors() = default;
253 
254     /** @brief Constructs VirtualSensors
255      *
256      * @param[in] bus     - Handle to system dbus
257      */
258     explicit VirtualSensors(sdbusplus::bus_t& bus) : bus(bus)
259     {
260         createVirtualSensors();
261     }
262     /** @brief Calls createVirtualSensor when interface added */
263     void propertiesChanged(sdbusplus::message_t& msg);
264 
265   private:
266     /** @brief sdbusplus bus client connection. */
267     sdbusplus::bus_t& bus;
268     /** @brief Get virual sensor config from DBus**/
269     ManagedObjectType getObjectsFromDBus();
270     /** @brief Parsing virtual sensor config JSON file  */
271     Json parseConfigFile(const std::string& configFile);
272 
273     /** @brief Matches for virtual sensors */
274     std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches;
275     /** @brief Map of the object VirtualSensor */
276     std::unordered_map<std::string, std::unique_ptr<VirtualSensor>>
277         virtualSensorsMap;
278 
279     /** @brief Create list of virtual sensors from JSON config*/
280     void createVirtualSensors();
281     /** @brief Create list of virtual sensors from DBus config */
282     void createVirtualSensorsFromDBus(const std::string& calculationType);
283     /** @brief Setup matches for virtual sensors */
284     void setupMatches();
285 };
286 
287 } // namespace virtualSensor
288 } // namespace phosphor
289