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