1 /**
2  * Copyright © 2021 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include "sensors.hpp"
19 
20 #include <sdbusplus/bus.hpp>
21 #include <sdbusplus/server/object.hpp>
22 #include <xyz/openbmc_project/Association/Definitions/server.hpp>
23 #include <xyz/openbmc_project/Sensor/Value/server.hpp>
24 #include <xyz/openbmc_project/State/Decorator/Availability/server.hpp>
25 #include <xyz/openbmc_project/State/Decorator/OperationalStatus/server.hpp>
26 
27 #include <chrono>
28 #include <memory>
29 #include <string>
30 #include <tuple>
31 #include <vector>
32 
33 namespace phosphor::power::regulators
34 {
35 
36 /**
37  * Define simple name for the generated C++ class that implements the
38  * xyz.openbmc_project.Sensor.Value interface.
39  */
40 using ValueInterface = sdbusplus::xyz::openbmc_project::Sensor::server::Value;
41 
42 /**
43  * Define simple name for the generated C++ class that implements the
44  * xyz.openbmc_project.State.Decorator.OperationalStatus interface.
45  */
46 using OperationalStatusInterface = sdbusplus::xyz::openbmc_project::State::
47     Decorator::server::OperationalStatus;
48 
49 /**
50  * Define simple name for the generated C++ class that implements the
51  * xyz.openbmc_project.State.Decorator.Availability interface.
52  */
53 using AvailabilityInterface =
54     sdbusplus::xyz::openbmc_project::State::Decorator::server::Availability;
55 
56 /**
57  * Define simple name for the generated C++ class that implements the
58  * xyz.openbmc_project.Association.Definitions interface.
59  */
60 using AssociationDefinitionsInterface =
61     sdbusplus::xyz::openbmc_project::Association::server::Definitions;
62 
63 /**
64  * Define simple name for the sdbusplus object_t class that implements all
65  * the necessary D-Bus interfaces via templates/multiple inheritance.
66  */
67 using DBusSensorObject =
68     sdbusplus::server::object_t<ValueInterface, OperationalStatusInterface,
69                                 AvailabilityInterface,
70                                 AssociationDefinitionsInterface>;
71 
72 /**
73  * Define simple name for the generated C++ enum that implements the
74  * valid sensor Unit values on D-Bus.
75  */
76 using Unit = sdbusplus::xyz::openbmc_project::Sensor::server::Value::Unit;
77 
78 /**
79  * Define simple name for the tuple used to create D-Bus associations.
80  */
81 using AssocationTuple = std::tuple<std::string, std::string, std::string>;
82 
83 /**
84  * Root object path for sensors.
85  */
86 constexpr const char* sensorsObjectPath = "/xyz/openbmc_project/sensors";
87 
88 /**
89  * @class DBusSensor
90  *
91  * This class represents a voltage regulator sensor on D-Bus.
92  *
93  * Each voltage rail in the system may provide multiple types of sensor data,
94  * such as temperature, output voltage, and output current.  A DBusSensor tracks
95  * one of these data types for a voltage rail.
96  */
97 class DBusSensor
98 {
99   public:
100     // Specify which compiler-generated methods we want
101     DBusSensor() = delete;
102     DBusSensor(const DBusSensor&) = delete;
103     DBusSensor(DBusSensor&&) = delete;
104     DBusSensor& operator=(const DBusSensor&) = delete;
105     DBusSensor& operator=(DBusSensor&&) = delete;
106     virtual ~DBusSensor() = default;
107 
108     /**
109      * Constructor.
110      *
111      * Throws an exception if an error occurs.
112      *
113      * @param bus D-Bus bus object
114      * @param name sensor name
115      * @param type sensor type
116      * @param value sensor value
117      * @param rail voltage rail associated with this sensor
118      * @param deviceInventoryPath D-Bus inventory path of the voltage regulator
119      *                            device that produces the rail
120      * @param chassisInventoryPath D-Bus inventory path of the chassis that
121      *                             contains the voltage regulator device
122      */
123     explicit DBusSensor(sdbusplus::bus_t& bus, const std::string& name,
124                         SensorType type, double value, const std::string& rail,
125                         const std::string& deviceInventoryPath,
126                         const std::string& chassisInventoryPath);
127 
128     /**
129      * Disable this sensor.
130      *
131      * Updates the sensor properties on D-Bus to indicate it is no longer
132      * receiving value updates.
133      *
134      * This method is normally called when the system is being powered off.
135      * Sensors are not read when the system is powered off.
136      */
137     void disable();
138 
139     /**
140      * Return the last time this sensor was updated.
141      *
142      * @return last update time
143      */
144     const std::chrono::system_clock::time_point& getLastUpdateTime() const
145     {
146         return lastUpdateTime;
147     }
148 
149     /**
150      * Return the sensor name.
151      *
152      * @return sensor name
153      */
154     const std::string& getName() const
155     {
156         return name;
157     }
158 
159     /**
160      * Return the voltage regulator rail associated with this sensor.
161      *
162      * @return rail
163      */
164     const std::string& getRail() const
165     {
166         return rail;
167     }
168 
169     /**
170      * Return the sensor type.
171      *
172      * @return sensor type
173      */
174     SensorType getType() const
175     {
176         return type;
177     }
178 
179     /**
180      * Set this sensor to the error state.
181      *
182      * Updates the sensor properties on D-Bus to indicate an error occurred and
183      * the sensor value could not be read.
184      */
185     void setToErrorState();
186 
187     /**
188      * Set the value of this sensor.
189      *
190      * Do not specify the value NaN.  This special value is used internally to
191      * indicate the sensor has been disabled or is in the error state.  Call the
192      * disable() or setToErrorState() method instead so that all affected D-Bus
193      * interfaces are updated correctly.
194      *
195      * @param value new sensor value
196      */
197     void setValue(double value);
198 
199   private:
200     /**
201      * Sensor value update policy.
202      *
203      * Determines whether a new sensor value should replace the current value on
204      * D-Bus.
205      */
206     enum class ValueUpdatePolicy : unsigned char
207     {
208         /**
209          * Hysteresis value update policy.
210          *
211          * The sensor value will only be updated if the new value differs from
212          * the current value by at least the hysteresis amount.  This avoids
213          * constant D-Bus traffic due to insignificant value changes.
214          */
215         hysteresis,
216 
217         /**
218          * Highest value update policy.
219          *
220          * The sensor value will only be updated if the new value is higher than
221          * the current value.
222          *
223          * Some sensors contain the highest value observed by the voltage
224          * regulator, such as the highest temperature or highest output voltage.
225          * The regulator internally calculates this value since it can poll the
226          * value very quickly and can catch transient events.
227          *
228          * When the sensor is read from the regulator, the regulator will often
229          * clear its internal value.  It will begin calculating a new highest
230          * value.  For this reason, the D-Bus sensor value is set to the highest
231          * value that has been read across all monitoring cycles.
232          *
233          * The D-Bus sensor value is cleared when the sensor is disabled.  This
234          * normally occurs when the system is powered off.  Thus, the D-Bus
235          * sensor value is normally the highest value read since the system was
236          * powered on.
237          */
238         highest,
239 
240         /**
241          * Lowest value update policy.
242          *
243          * The sensor value will only be updated if the new value is lower than
244          * the current value.
245          *
246          * Some sensors contain the lowest value observed by the voltage
247          * regulator, such as the lowest output current or lowest output
248          * voltage.  The regulator internally calculates this value since it can
249          * poll the value very quickly and can catch transient events.
250          *
251          * When the sensor is read from the regulator, the regulator will often
252          * clear its internal value.  It will begin calculating a new lowest
253          * value.  For this reason, the D-Bus sensor value is set to the lowest
254          * value that has been read across all monitoring cycles.
255          *
256          * The D-Bus sensor value is cleared when the sensor is disabled.  This
257          * normally occurs when the system is powered off.  Thus, the D-Bus
258          * sensor value is normally the lowest value read since the system was
259          * powered on.
260          */
261         lowest
262     };
263 
264     /**
265      * Get the D-Bus associations to create for this sensor.
266      *
267      * @param deviceInventoryPath D-Bus inventory path of the voltage regulator
268      *                            device that produces the rail
269      * @param chassisInventoryPath D-Bus inventory path of the chassis that
270      *                             contains the voltage regulator device
271      */
272     std::vector<AssocationTuple>
273         getAssociations(const std::string& deviceInventoryPath,
274                         const std::string& chassisInventoryPath);
275 
276     /**
277      * Get sensor properties that are based on the sensor type.
278      *
279      * The properties are returned in output parameters.
280      *
281      * Also initializes some data members whose value is based on the sensor
282      * type.
283      *
284      * @param objectPath returns the object path of this sensor
285      * @param unit returns the D-Bus unit for the sensor value
286      * @param minValue returns the minimum sensor value
287      * @param maxValue returns the maximum sensor value
288      */
289     void getTypeBasedProperties(std::string& objectPath, Unit& unit,
290                                 double& minValue, double& maxValue);
291 
292     /**
293      * Set the last time this sensor was updated.
294      */
295     void setLastUpdateTime()
296     {
297         lastUpdateTime = std::chrono::system_clock::now();
298     }
299 
300     /**
301      * Set the sensor value on D-Bus to NaN.
302      */
303     void setValueToNaN();
304 
305     /**
306      * Returns whether to update the sensor value on D-Bus with the specified
307      * new value.
308      *
309      * @param value new sensor value
310      * @return true if value should be updated on D-Bus, false otherwise
311      */
312     bool shouldUpdateValue(double value);
313 
314     /**
315      * D-Bus bus object.
316      */
317     sdbusplus::bus_t& bus;
318 
319     /**
320      * Sensor name.
321      */
322     std::string name{};
323 
324     /**
325      * Sensor type.
326      */
327     SensorType type;
328 
329     /**
330      * Voltage regulator rail associated with this sensor.
331      */
332     std::string rail{};
333 
334     /**
335      * Sensor value update policy.
336      */
337     ValueUpdatePolicy updatePolicy{ValueUpdatePolicy::hysteresis};
338 
339     /**
340      * Hysteresis value.
341      *
342      * Only used when updatePolicy is hysteresis.
343      */
344     double hysteresis{0.0};
345 
346     /**
347      * sdbusplus object_t class that implements all the necessary D-Bus
348      * interfaces via templates and multiple inheritance.
349      */
350     std::unique_ptr<DBusSensorObject> dbusObject{};
351 
352     /**
353      * Last time this sensor was updated.
354      */
355     std::chrono::system_clock::time_point lastUpdateTime{};
356 };
357 
358 } // namespace phosphor::power::regulators
359