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