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(
129         sdbusplus::message_t& msg, const std::string& interface,
130         const std::string& propertyName, 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      */
getInput() const154     inline double getInput() const
155     {
156         return _tachInput;
157     }
158 
159     /**
160      * @brief Returns true if sensor has a target
161      */
hasTarget() const162     inline bool hasTarget() const
163     {
164         return _hasTarget;
165     }
166 
167     /**
168      * @brief Returns the interface of the sensor target
169      */
getInterface() const170     inline std::string getInterface() const
171     {
172         return _interface;
173     }
174 
175     /**
176      * @brief Returns true if sensor has a D-Bus owner
177      */
hasOwner() const178     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      */
setOwner(bool val)188     inline void setOwner(bool val)
189     {
190         _hasOwner = val;
191     }
192 
193     /**
194      * @brief Returns the factor of the sensor target
195      */
getFactor() const196     inline double getFactor() const
197     {
198         return _factor;
199     }
200 
201     /**
202      * @brief Returns a reference to the sensor's Fan object
203      */
getFan() const204     inline Fan& getFan() const
205     {
206         return _fan;
207     }
208 
209     /**
210      * @brief Returns the offset of the sensor target
211      */
getOffset() const212     inline int64_t getOffset() const
213     {
214         return _offset;
215     }
216 
217     /**
218      * @brief Returns the method of out of range
219      */
getMethod() const220     inline size_t getMethod() const
221     {
222         return _method;
223     }
224 
225     /**
226      * @brief Returns the threshold of count method
227      */
getThreshold() const228     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      */
getCounter() const241     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      */
functional() const250     inline bool functional() const
251     {
252         return _functional;
253     }
254 
255     /**
256      * Set the functional status and update inventory to match
257      *
258      * @param[in] functional - The new state
259      * @param[in] skipErrorTimer - If setting the sensor to
260      *            nonfunctional, don't start the error timer.
261      */
262     void setFunctional(bool functional, bool skipErrorTimer = false);
263 
264     /**
265      * @brief Says if the timer is running or not
266      *
267      * @return bool - if timer is currently running
268      */
timerRunning()269     inline bool timerRunning()
270     {
271         return _timer.isEnabled();
272     }
273 
274     /**
275      * @brief Stops the timer when the given mode differs and starts
276      * the associated timer for the mode given if not already running
277      *
278      * @param[in] mode - mode of timer to start
279      */
280     void startTimer(TimerMode mode);
281 
282     /**
283      * @brief Stops the timer
284      */
stopTimer()285     inline void stopTimer()
286     {
287         phosphor::logging::log<phosphor::logging::level::DEBUG>(
288             std::format("Stop running timer on tach sensor {}.", _name)
289                 .c_str());
290         _timer.setEnabled(false);
291     }
292 
293     /**
294      * @brief Says if the count timer is running
295      *
296      * @return bool - If count timer running
297      */
countTimerRunning() const298     inline bool countTimerRunning() const
299     {
300         return _countTimer && _countTimer->isEnabled();
301     }
302 
303     /**
304      * @brief Stops the count timer
305      */
306     void stopCountTimer();
307 
308     /**
309      * @brief Starts the count timer
310      */
311     void startCountTimer();
312 
313     /**
314      * @brief Return the given timer mode's delay time
315      *
316      * @param[in] mode - mode of timer to get delay time for
317      */
318     std::chrono::microseconds getDelay(TimerMode mode);
319 
320     /**
321      * Returns the sensor name
322      */
name() const323     inline const std::string& name() const
324     {
325         return _name;
326     };
327 
328     /**
329      * @brief Says if the error timer is running
330      *
331      * @return bool - If the timer is running
332      */
errorTimerRunning() const333     bool errorTimerRunning() const
334     {
335         if (_errorTimer && _errorTimer->isEnabled())
336         {
337             return true;
338         }
339         return false;
340     }
341 
342     /**
343      * @brief Get the current allowed range of speeds
344      *
345      * @param[in] lowerDeviation - The configured lower deviation(in percent)
346      *                             allowed
347      * @param[in] upperDeviation - The configured upper deviation(in percent)
348      *                             allowed
349      *
350      * @return pair - Min/Max(optional) range of speeds allowed
351      */
352     std::pair<uint64_t, std::optional<uint64_t>> getRange(
353         const size_t lowerDeviation, const size_t upperDeviation) const;
354 
355     /**
356      * @brief Processes the current state of the sensor
357      */
358     void processState();
359 
360     /**
361      * @brief Resets the monitoring method of the sensor
362      */
363     void resetMethod();
364 
365     /**
366      * @brief Refreshes the tach input and target values by
367      *        reading them from D-Bus.
368      */
369     void updateTachAndTarget();
370 
371     /**
372      * @brief return the previous tach values
373      */
getPrevTach() const374     const std::deque<uint64_t>& getPrevTach() const
375     {
376         return _prevTachs;
377     }
378 
379     /**
380      * @brief return the previous target values
381      */
getPrevTarget() const382     const std::deque<uint64_t>& getPrevTarget() const
383     {
384         return _prevTargets;
385     }
386 
387   private:
388     /**
389      * @brief Returns the match string to use for matching
390      *        on a properties changed signal.
391      */
392     std::string getMatchString(const std::optional<std::string> path,
393                                const std::string& interface);
394 
395     /**
396      * @brief Reads the Target property and stores in _tachTarget.
397      *        Also calls Fan::tachChanged().
398      *
399      * @param[in] msg - the dbus message
400      */
401     void handleTargetChange(sdbusplus::message_t& msg);
402 
403     /**
404      * @brief Reads the Value property and stores in _tachInput.
405      *        Also calls Fan::tachChanged().
406      *
407      * @param[in] msg - the dbus message
408      */
409     void handleTachChange(sdbusplus::message_t& msg);
410 
411     /**
412      * @brief Updates the Functional property in the inventory
413      *        for this tach sensor based on the value passed in.
414      *
415      * @param[in] functional - If the Functional property should
416      *                         be set to true or false.
417      */
418     void updateInventory(bool functional);
419 
420     /**
421      * @brief the dbus object
422      */
423     sdbusplus::bus_t& _bus;
424 
425     /**
426      * @brief Reference to the parent Fan object
427      */
428     Fan& _fan;
429 
430     /**
431      * @brief The name of the sensor, including the full path
432      *
433      * For example /xyz/openbmc_project/sensors/fan_tach/fan0
434      */
435     const std::string _name;
436 
437     /**
438      * @brief The inventory name of the sensor, including the full path
439      */
440     const std::string _invName;
441 
442     /**
443      * @brief If functional (not too slow).  The parent
444      *        fan object sets this.
445      */
446     bool _functional = true;
447 
448     /**
449      * @brief If the sensor has a Target property (can set speed)
450      */
451     const bool _hasTarget;
452 
453     /**
454      * @brief If the sensor has a D-Bus owner
455      */
456     bool _hasOwner = true;
457 
458     /**
459      * @brief Amount of time to delay updating to functional
460      */
461     const size_t _funcDelay;
462 
463     /**
464      * @brief The interface that the target implements
465      */
466     const std::string _interface;
467 
468     /**
469      * @brief The object path to set sensor's target
470      */
471     const std::string _path;
472 
473     /**
474      * @brief The factor of target to get fan rpm
475      */
476     const double _factor;
477 
478     /**
479      * @brief The offset of target to get fan rpm
480      */
481     const int64_t _offset;
482 
483     /**
484      * @brief The method of out of range
485      */
486     const size_t _method;
487 
488     /**
489      * @brief The threshold for count method
490      */
491     const size_t _threshold;
492 
493     /**
494      * @brief Whether to ignore being above the max or not
495      */
496     const bool _ignoreAboveMax;
497 
498     /**
499      * @brief The counter for count method
500      */
501     size_t _counter = 0;
502 
503     /**
504      * @brief The input speed, from the Value dbus property
505      */
506     double _tachInput = 0;
507 
508     /**
509      * @brief The current target speed, from the Target dbus property
510      *        (if applicable)
511      */
512     uint64_t _tachTarget = 0;
513 
514     /**
515      * @brief The timeout value to use
516      */
517     const size_t _timeout;
518 
519     /**
520      * @brief Mode that current timer is in
521      */
522     TimerMode _timerMode;
523 
524     /**
525      * The timer object
526      */
527     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
528 
529     /**
530      * @brief The match object for the Value properties changed signal
531      */
532     std::unique_ptr<sdbusplus::bus::match_t> tachSignal;
533 
534     /**
535      * @brief The match object for the Target properties changed signal
536      */
537     std::unique_ptr<sdbusplus::bus::match_t> targetSignal;
538 
539     /**
540      * @brief The number of seconds to wait between a sensor being set
541      *        to nonfunctional and creating an error for it.
542      *
543      * If std::nullopt, no errors will be created.
544      */
545     const std::optional<size_t> _errorDelay;
546 
547     /**
548      * @brief The timer that uses _errorDelay.  When it expires an error
549      *        will be created for a faulted fan sensor (rotor).
550      *
551      * If _errorDelay is std::nullopt, then this won't be created.
552      */
553     std::unique_ptr<
554         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
555         _errorTimer;
556 
557     /**
558      * @brief The interval, in seconds, to use for the timer that runs
559      *        the checks when using the 'count' method.
560      */
561     size_t _countInterval;
562 
563     /**
564      * @brief The timer used by the 'count' method for determining
565      *        functional status.
566      */
567     std::unique_ptr<
568         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
569         _countTimer;
570 
571     /**
572      * @brief record of previous targets
573      */
574     std::deque<uint64_t> _prevTargets;
575 
576     /**
577      * @brief record of previous tach readings
578      */
579     std::deque<uint64_t> _prevTachs;
580 };
581 
582 } // namespace monitor
583 } // namespace fan
584 } // namespace phosphor
585