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