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