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