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     static FuncMaxIgnoreNaN<double> funcMaxIgnoreNaN;
173     static FuncSumIgnoreNaN<double> funcSumIgnoreNaN;
174     static FuncIfNan<double> funcIfNan;
175 
176     /** @brief Read config from json object and initialize sensor data
177      * for each virtual sensor
178      */
179     void initVirtualSensor(const Json& sensorConfig,
180                            const std::string& objPath);
181 
182     /** @brief Read config from interface map and initialize sensor data
183      * for each virtual sensor
184      */
185     void initVirtualSensor(const InterfaceMap& interfaceMap,
186                            const std::string& objPath,
187                            const std::string& sensorType,
188                            const std::string& calculationType);
189 
190     /** @brief Returns which calculation function or expression to use */
191     double calculateValue(const std::string& sensortype,
192                           const VirtualSensor::ParamMap& paramMap);
193     /** @brief Calculate median value from sensors */
194     double
195         calculateModifiedMedianValue(const VirtualSensor::ParamMap& paramMap);
196     /** @brief Calculate maximum value from sensors */
197     double calculateMaximumValue(const VirtualSensor::ParamMap& paramMap);
198     /** @brief create threshold objects from json config */
199     void createThresholds(const Json& threshold, const std::string& objPath);
200     /** @brief parse config from entity manager **/
201     void parseConfigInterface(const PropertyMap& propertyMap,
202                               const std::string& sensorType,
203                               const std::string& interface);
204 
205     /** @brief Check Sensor threshold and update alarm and log */
206     template <typename V, typename T>
207     void checkThresholds(V value, T& threshold)
208     {
209         if (!threshold)
210             return;
211 
212         static constexpr auto tname = T::element_type::name;
213 
214         auto alarmHigh = threshold->alarmHigh();
215         auto highHysteresis = threshold->getHighHysteresis();
216         if ((!alarmHigh && value >= threshold->high()) ||
217             (alarmHigh && value < (threshold->high() - highHysteresis)))
218         {
219             if (!alarmHigh)
220             {
221                 error("ASSERT: sensor {SENSOR} is above the upper threshold "
222                       "{THRESHOLD}.",
223                       "SENSOR", name, "THRESHOLD", tname);
224                 threshold->alarmHighSignalAsserted(value);
225             }
226             else
227             {
228                 info("DEASSERT: sensor {SENSOR} is under the upper threshold "
229                      "{THRESHOLD}.",
230                      "SENSOR", name, "THRESHOLD", tname);
231                 threshold->alarmHighSignalDeasserted(value);
232             }
233             threshold->alarmHigh(!alarmHigh);
234         }
235 
236         auto alarmLow = threshold->alarmLow();
237         auto lowHysteresis = threshold->getLowHysteresis();
238         if ((!alarmLow && value <= threshold->low()) ||
239             (alarmLow && value > (threshold->low() + lowHysteresis)))
240         {
241             if (!alarmLow)
242             {
243                 error("ASSERT: sensor {SENSOR} is below the lower threshold "
244                       "{THRESHOLD}.",
245                       "SENSOR", name, "THRESHOLD", tname);
246                 threshold->alarmLowSignalAsserted(value);
247             }
248             else
249             {
250                 info("DEASSERT: sensor {SENSOR} is above the lower threshold "
251                      "{THRESHOLD}.",
252                      "SENSOR", name, "THRESHOLD", tname);
253                 threshold->alarmLowSignalDeasserted(value);
254             }
255             threshold->alarmLow(!alarmLow);
256         }
257     }
258 
259     /** @brief Create Association from entityPath*/
260     void createAssociation(const std::string& objPath,
261                            const std::string& entityPath);
262 };
263 
264 class VirtualSensors
265 {
266   public:
267     VirtualSensors() = delete;
268     virtual ~VirtualSensors() = default;
269 
270     /** @brief Constructs VirtualSensors
271      *
272      * @param[in] bus     - Handle to system dbus
273      */
274     explicit VirtualSensors(sdbusplus::bus_t& bus) : bus(bus)
275     {
276         createVirtualSensors();
277     }
278     /** @brief Calls createVirtualSensor when interface added */
279     void propertiesChanged(sdbusplus::message_t& msg);
280 
281   private:
282     /** @brief sdbusplus bus client connection. */
283     sdbusplus::bus_t& bus;
284     /** @brief Get virual sensor config from DBus**/
285     ManagedObjectType getObjectsFromDBus();
286     /** @brief Parsing virtual sensor config JSON file  */
287     Json parseConfigFile();
288 
289     /** @brief Matches for virtual sensors */
290     std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches;
291     /** @brief Map of the object VirtualSensor */
292     std::unordered_map<std::string, std::unique_ptr<VirtualSensor>>
293         virtualSensorsMap;
294 
295     /** @brief Create list of virtual sensors from JSON config*/
296     void createVirtualSensors();
297     /** @brief Create list of virtual sensors from DBus config */
298     void createVirtualSensorsFromDBus(const std::string& calculationType);
299     /** @brief Setup matches for virtual sensors */
300     void setupMatches();
301 };
302 
303 } // namespace virtualSensor
304 } // namespace phosphor
305