1 #pragma once
2 
3 #include <fmt/format.h>
4 
5 #include <phosphor-logging/log.hpp>
6 #include <sdbusplus/bus.hpp>
7 #include <sdbusplus/server.hpp>
8 #include <sdeventplus/clock.hpp>
9 #include <sdeventplus/event.hpp>
10 #include <sdeventplus/utility/timer.hpp>
11 
12 #include <chrono>
13 #include <utility>
14 
15 namespace phosphor
16 {
17 namespace fan
18 {
19 namespace monitor
20 {
21 
22 class Fan;
23 
24 constexpr auto FAN_SENSOR_PATH = "/xyz/openbmc_project/sensors/fan_tach/";
25 
26 /**
27  * The mode fan monitor will run in:
28  *   - init - only do the initialization steps
29  *   - monitor - run normal monitoring algorithm
30  */
31 enum class Mode
32 {
33     init,
34     monitor
35 };
36 
37 /**
38  * The mode that the timer is running in:
39  *   - func - Transition to functional state timer
40  *   - nonfunc - Transition to nonfunctional state timer
41  */
42 enum class TimerMode
43 {
44     func,
45     nonfunc
46 };
47 
48 /**
49  * The mode that the method is running in:
50  *   - time - Use a percentage based deviation
51  *   - count - Run up/down count fault detection
52  */
53 enum MethodMode
54 {
55     timebased = 0,
56     count
57 };
58 
59 /**
60  * @class TachSensor
61  *
62  * This class represents the sensor that reads a tach value.
63  * It may also support a Target, which is the property used to
64  * set a speed.  Since it doesn't necessarily have a Target, it
65  * won't for sure know if it is running too slow, so it leaves
66  * that determination to other code.
67  *
68  * This class has a parent Fan object that knows about all
69  * sensors for that fan.
70  */
71 class TachSensor
72 {
73   public:
74     TachSensor() = delete;
75     TachSensor(const TachSensor&) = delete;
76     // TachSensor is not moveable since the this pointer is used as systemd
77     // callback context.
78     TachSensor(TachSensor&&) = delete;
79     TachSensor& operator=(const TachSensor&) = delete;
80     TachSensor& operator=(TachSensor&&) = delete;
81     ~TachSensor() = default;
82 
83     /**
84      * @brief Constructor
85      *
86      * @param[in] mode - mode of fan monitor
87      * @param[in] bus - the dbus object
88      * @param[in] fan - the parent fan object
89      * @param[in] id - the id of the sensor
90      * @param[in] hasTarget - if the sensor supports
91      *                        setting the speed
92      * @param[in] funcDelay - Delay to mark functional
93      * @param[in] interface - the interface of the target
94      * @param[in] factor - the factor of the sensor target
95      * @param[in] offset - the offset of the sensor target
96      * @param[in] method - the method of out of range
97      * @param[in] threshold - the threshold of counter method
98      * @param[in] timeout - Normal timeout value to use
99      * @param[in] errorDelay - Delay in seconds before creating an error
100      *                         or std::nullopt if no errors.
101      * @param[in] countInterval - In count mode interval
102      *
103      * @param[in] event - Event loop reference
104      */
105     TachSensor(Mode mode, sdbusplus::bus::bus& bus, Fan& fan,
106                const std::string& id, bool hasTarget, size_t funcDelay,
107                const std::string& interface, double factor, int64_t offset,
108                size_t method, size_t threshold, size_t timeout,
109                const std::optional<size_t>& errorDelay, size_t countInterval,
110                const sdeventplus::Event& event);
111 
112     /**
113      * @brief Reads a property from the input message and stores it in value.
114      *        T is the value type.
115      *
116      *        Note: This can only be called once per message.
117      *
118      * @param[in] msg - the dbus message that contains the data
119      * @param[in] interface - the interface the property is on
120      * @param[in] propertName - the name of the property
121      * @param[out] value - the value to store the property value in
122      */
123     template <typename T>
124     static void readPropertyFromMessage(sdbusplus::message::message& msg,
125                                         const std::string& interface,
126                                         const std::string& propertyName,
127                                         T& value)
128     {
129         std::string sensor;
130         std::map<std::string, std::variant<T>> data;
131         msg.read(sensor, data);
132 
133         if (sensor.compare(interface) == 0)
134         {
135             auto propertyMap = data.find(propertyName);
136             if (propertyMap != data.end())
137             {
138                 value = std::get<T>(propertyMap->second);
139             }
140         }
141     }
142 
143     /**
144      * @brief Returns the target speed value
145      */
146     uint64_t getTarget() const;
147 
148     /**
149      * @brief Returns the input speed value
150      */
151     inline double getInput() const
152     {
153         return _tachInput;
154     }
155 
156     /**
157      * @brief Returns true if sensor has a target
158      */
159     inline bool hasTarget() const
160     {
161         return _hasTarget;
162     }
163 
164     /**
165      * @brief Returns the interface of the sensor target
166      */
167     inline std::string getInterface() const
168     {
169         return _interface;
170     }
171 
172     /**
173      * @brief Returns the factor of the sensor target
174      */
175     inline double getFactor() const
176     {
177         return _factor;
178     }
179 
180     /**
181      * @brief Returns the offset of the sensor target
182      */
183     inline int64_t getOffset() const
184     {
185         return _offset;
186     }
187 
188     /**
189      * @brief Returns the method of out of range
190      */
191     inline size_t getMethod() const
192     {
193         return _method;
194     }
195 
196     /**
197      * @brief Returns the threshold of count method
198      */
199     inline size_t getThreshold() const
200     {
201         return _threshold;
202     }
203 
204     /**
205      * Set the sensor faulted counter
206      */
207     void setCounter(bool count);
208 
209     /**
210      * @brief Returns the sensor faulted count
211      */
212     inline size_t getCounter() const
213     {
214         return _counter;
215     }
216 
217     /**
218      * Returns true if the hardware behind this
219      * sensor is considered working OK/functional.
220      */
221     inline bool functional() const
222     {
223         return _functional;
224     }
225 
226     /**
227      * Set the functional status and update inventory to match
228      */
229     void setFunctional(bool functional);
230 
231     /**
232      * @brief Says if the timer is running or not
233      *
234      * @return bool - if timer is currently running
235      */
236     inline bool timerRunning()
237     {
238         return _timer.isEnabled();
239     }
240 
241     /**
242      * @brief Stops the timer when the given mode differs and starts
243      * the associated timer for the mode given if not already running
244      *
245      * @param[in] mode - mode of timer to start
246      */
247     void startTimer(TimerMode mode);
248 
249     /**
250      * @brief Stops the timer
251      */
252     inline void stopTimer()
253     {
254         phosphor::logging::log<phosphor::logging::level::DEBUG>(
255             fmt::format("Stop running timer on tach sensor {}.", _name)
256                 .c_str());
257         _timer.setEnabled(false);
258     }
259 
260     /**
261      * @brief Says if the count timer is running
262      *
263      * @return bool - If count timer running
264      */
265     inline bool countTimerRunning() const
266     {
267         return _countTimer && _countTimer->isEnabled();
268     }
269 
270     /**
271      * @brief Stops the count timer
272      */
273     void stopCountTimer();
274 
275     /**
276      * @brief Starts the count timer
277      */
278     void startCountTimer();
279 
280     /**
281      * @brief Return the given timer mode's delay time
282      *
283      * @param[in] mode - mode of timer to get delay time for
284      */
285     std::chrono::microseconds getDelay(TimerMode mode);
286 
287     /**
288      * Returns the sensor name
289      */
290     inline const std::string& name() const
291     {
292         return _name;
293     };
294 
295     /**
296      * @brief Says if the error timer is running
297      *
298      * @return bool - If the timer is running
299      */
300     bool errorTimerRunning() const
301     {
302         if (_errorTimer && _errorTimer->isEnabled())
303         {
304             return true;
305         }
306         return false;
307     }
308 
309     /**
310      * @brief Get the current allowed range of speeds
311      *
312      * @param[in] deviation - The configured deviation(in percent) allowed
313      *
314      * @return pair - Min/Max range of speeds allowed
315      */
316     std::pair<uint64_t, uint64_t> getRange(const size_t deviation) const;
317 
318     /**
319      * @brief Processes the current state of the sensor
320      */
321     void processState();
322 
323     /**
324      * @brief Resets the monitoring method of the sensor
325      */
326     void resetMethod();
327 
328     /**
329      * @brief Refreshes the tach input and target values by
330      *        reading them from D-Bus.
331      */
332     void updateTachAndTarget();
333 
334   private:
335     /**
336      * @brief Returns the match string to use for matching
337      *        on a properties changed signal.
338      */
339     std::string getMatchString(const std::string& interface);
340 
341     /**
342      * @brief Reads the Target property and stores in _tachTarget.
343      *        Also calls Fan::tachChanged().
344      *
345      * @param[in] msg - the dbus message
346      */
347     void handleTargetChange(sdbusplus::message::message& msg);
348 
349     /**
350      * @brief Reads the Value property and stores in _tachInput.
351      *        Also calls Fan::tachChanged().
352      *
353      * @param[in] msg - the dbus message
354      */
355     void handleTachChange(sdbusplus::message::message& msg);
356 
357     /**
358      * @brief Updates the Functional property in the inventory
359      *        for this tach sensor based on the value passed in.
360      *
361      * @param[in] functional - If the Functional property should
362      *                         be set to true or false.
363      */
364     void updateInventory(bool functional);
365 
366     /**
367      * @brief the dbus object
368      */
369     sdbusplus::bus::bus& _bus;
370 
371     /**
372      * @brief Reference to the parent Fan object
373      */
374     Fan& _fan;
375 
376     /**
377      * @brief The name of the sensor, including the full path
378      *
379      * For example /xyz/openbmc_project/sensors/fan_tach/fan0
380      */
381     const std::string _name;
382 
383     /**
384      * @brief The inventory name of the sensor, including the full path
385      */
386     const std::string _invName;
387 
388     /**
389      * @brief If functional (not too slow).  The parent
390      *        fan object sets this.
391      */
392     bool _functional;
393 
394     /**
395      * @brief If the sensor has a Target property (can set speed)
396      */
397     const bool _hasTarget;
398 
399     /**
400      * @brief Amount of time to delay updating to functional
401      */
402     const size_t _funcDelay;
403 
404     /**
405      * @brief The interface that the target implements
406      */
407     const std::string _interface;
408 
409     /**
410      * @brief The factor of target to get fan rpm
411      */
412     const double _factor;
413 
414     /**
415      * @brief The offset of target to get fan rpm
416      */
417     const int64_t _offset;
418 
419     /**
420      * @brief The method of out of range
421      */
422     const size_t _method;
423 
424     /**
425      * @brief The threshold for count method
426      */
427     const size_t _threshold;
428 
429     /**
430      * @brief The counter for count method
431      */
432     size_t _counter = 0;
433 
434     /**
435      * @brief The input speed, from the Value dbus property
436      */
437     double _tachInput = 0;
438 
439     /**
440      * @brief The current target speed, from the Target dbus property
441      *        (if applicable)
442      */
443     uint64_t _tachTarget = 0;
444 
445     /**
446      * @brief The timeout value to use
447      */
448     const size_t _timeout;
449 
450     /**
451      * @brief Mode that current timer is in
452      */
453     TimerMode _timerMode;
454 
455     /**
456      * The timer object
457      */
458     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
459 
460     /**
461      * @brief The match object for the Value properties changed signal
462      */
463     std::unique_ptr<sdbusplus::server::match::match> tachSignal;
464 
465     /**
466      * @brief The match object for the Target properties changed signal
467      */
468     std::unique_ptr<sdbusplus::server::match::match> targetSignal;
469 
470     /**
471      * @brief The number of seconds to wait between a sensor being set
472      *        to nonfunctional and creating an error for it.
473      *
474      * If std::nullopt, no errors will be created.
475      */
476     const std::optional<size_t> _errorDelay;
477 
478     /**
479      * @brief The timer that uses _errorDelay.  When it expires an error
480      *        will be created for a faulted fan sensor (rotor).
481      *
482      * If _errorDelay is std::nullopt, then this won't be created.
483      */
484     std::unique_ptr<
485         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
486         _errorTimer;
487 
488     /**
489      * @brief The interval, in seconds, to use for the timer that runs
490      *        the checks when using the 'count' method.
491      */
492     size_t _countInterval;
493 
494     /**
495      * @brief The timer used by the 'count' method for determining
496      *        functional status.
497      */
498     std::unique_ptr<
499         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
500         _countTimer;
501 };
502 
503 } // namespace monitor
504 } // namespace fan
505 } // namespace phosphor
506