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