1 #pragma once
2 
3 #include "logging.hpp"
4 #include "power_interface.hpp"
5 
6 #include <fmt/format.h>
7 
8 #include <sdeventplus/clock.hpp>
9 #include <sdeventplus/event.hpp>
10 #include <sdeventplus/utility/timer.hpp>
11 
12 #include <chrono>
13 
14 namespace phosphor::fan::monitor
15 {
16 
17 /**
18  * @class PowerOffAction
19  *
20  * This is the base class for a power off action, which is
21  * used by the PowerOffRule class to do different types of
22  * power offs based on fan failures.
23  *
24  * The power off is started with the start() method, and the
25  * derived class may or may not allow it to be stopped with
26  * the cancel() method, which is really only useful when
27  * there is a delay before the power off.
28  *
29  * It uses the PowerInterfaceBase object pointer to perform
30  * the D-Bus call to do the power off, so it can be mocked
31  * for testing.
32  */
33 class PowerOffAction
34 {
35   public:
36     using PrePowerOffFunc = std::function<void()>;
37 
38     PowerOffAction() = delete;
39     virtual ~PowerOffAction() = default;
40     PowerOffAction(const PowerOffAction&) = delete;
41     PowerOffAction& operator=(const PowerOffAction&) = delete;
42     PowerOffAction(PowerOffAction&&) = delete;
43     PowerOffAction& operator=(PowerOffAction&&) = delete;
44 
45     /**
46      * @brief Constructor
47      *
48      * @param[in] name - The action name.  Used for tracing.
49      * @param[in] powerInterface - The object used to invoke the power off.
50      * @param[in] powerOffFunc - A function to call right before the power
51      *                           off occurs (after any delays).  May be
52      *                           empty if no function is necessary.
53      */
54     PowerOffAction(const std::string& name,
55                    std::shared_ptr<PowerInterfaceBase> powerInterface,
56                    PrePowerOffFunc& powerOffFunc) :
57         _name(name),
58         _powerIface(std::move(powerInterface)),
59         _event(sdeventplus::Event::get_default()),
60         _prePowerOffFunc(powerOffFunc)
61     {}
62 
63     /**
64      * @brief Starts the power off.
65      *
66      * Though this occurs in the child class, usually this
67      * involves starting a timer and then powering off when it
68      * times out.
69      */
70     virtual void start() = 0;
71 
72     /**
73      * @brief Attempts to cancel the power off, if the derived
74      *        class allows it, and assuming the power off hasn't
75      *        already happened.
76      *
77      * The 'force' parameter is mainly for use when something else
78      * powered off the system so this action doesn't need to run
79      * anymore even if it isn't usually cancelable.
80      *
81      * @param[in] force - If the cancel should be forced
82      *
83      * @return bool - If the cancel was allowed/successful
84      */
85     virtual bool cancel(bool force) = 0;
86 
87     /**
88      * @brief If the power off action is currently in progress, which
89      *        usually means it's still in the delay time before the
90      *        power off D-Bus command is executed.
91      *
92      * @return bool - If the action is active
93      */
94     bool active() const
95     {
96         return _active;
97     }
98 
99     /**
100      * @brief Returns the name of the action
101      *
102      * @return const std::string& - The name
103      */
104     const std::string& name() const
105     {
106         return _name;
107     }
108 
109   protected:
110     /**
111      * @brief The name of the action, which is set by the
112      *        derived class.
113      */
114     const std::string _name;
115 
116     /**
117      * @brief If the action is currently active or not.
118      */
119     bool _active = false;
120 
121     /**
122      * @brief The object used to invoke the power off with.
123      */
124     std::shared_ptr<PowerInterfaceBase> _powerIface;
125 
126     /**
127      * @brief The event loop object. Needed by timers.
128      */
129     sdeventplus::Event _event;
130 
131     /**
132      * @brief A function that will be called right before
133      *        the power off.
134      */
135     PrePowerOffFunc _prePowerOffFunc;
136 };
137 
138 /**
139  * @class HardPowerOff
140  *
141  * This class is derived from the PowerOffAction class
142  * and will execute a hard power off after some delay.
143  */
144 class HardPowerOff : public PowerOffAction
145 {
146   public:
147     HardPowerOff() = delete;
148     ~HardPowerOff() = default;
149     HardPowerOff(const HardPowerOff&) = delete;
150     HardPowerOff& operator=(const HardPowerOff&) = delete;
151     HardPowerOff(HardPowerOff&&) = delete;
152     HardPowerOff& operator=(HardPowerOff&&) = delete;
153 
154     /**
155      * @brief Constructor
156      *
157      * @param[in] delay - The amount of time in seconds to wait before
158      *                    doing the power off
159      * @param[in] powerInterface - The object to use to do the power off
160      * @param[in] func - A function to call right before the power
161      *                   off occurs (after the delay).  May be
162      *                   empty if no function is necessary.
163      */
164     HardPowerOff(uint32_t delay,
165                  std::shared_ptr<PowerInterfaceBase> powerInterface,
166                  PrePowerOffFunc func) :
167         PowerOffAction("Hard Power Off: " + std::to_string(delay) + "s",
168                        powerInterface, func),
169         _delay(delay),
170         _timer(_event, std::bind(std::mem_fn(&HardPowerOff::powerOff), this))
171     {}
172 
173     /**
174      * @brief Starts a timer upon the expiration of which the
175      *        hard power off will be done.
176      */
177     void start() override
178     {
179         _timer.restartOnce(_delay);
180     }
181 
182     /**
183      * @brief Cancels the timer.  This is always allowed.
184      *
185      * @param[in] force - If the cancel should be forced or not
186      *                    (not checked in this case)
187      * @return bool - Always returns true
188      */
189     bool cancel(bool) override
190     {
191         if (_timer.isEnabled())
192         {
193             _timer.setEnabled(false);
194         }
195 
196         // Can always be canceled
197         return true;
198     }
199 
200     /**
201      * @brief Performs the hard power off.
202      */
203     void powerOff()
204     {
205 
206         if (_prePowerOffFunc)
207         {
208             _prePowerOffFunc();
209         }
210 
211         getLogger().log(
212             fmt::format("Action '{}' executing hard power off", name()));
213         _powerIface->hardPowerOff();
214     }
215 
216   private:
217     /**
218      * @brief The number of seconds to wait between starting the
219      *        action and doing the power off.
220      */
221     std::chrono::seconds _delay;
222 
223     /**
224      * @brief The Timer object used to handle the delay.
225      */
226     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
227 };
228 
229 /**
230  * @class SoftPowerOff
231  *
232  * This class is derived from the PowerOffAction class
233  * and will execute a soft power off after some delay.
234  */
235 class SoftPowerOff : public PowerOffAction
236 {
237   public:
238     SoftPowerOff() = delete;
239     ~SoftPowerOff() = default;
240     SoftPowerOff(const SoftPowerOff&) = delete;
241     SoftPowerOff& operator=(const SoftPowerOff&) = delete;
242     SoftPowerOff(SoftPowerOff&&) = delete;
243     SoftPowerOff& operator=(SoftPowerOff&&) = delete;
244 
245     /**
246      * @brief Constructor
247      *
248      * @param[in] delay - The amount of time in seconds to wait before
249      *                    doing the power off
250      * @param[in] powerInterface - The object to use to do the power off
251      * @param[in] func - A function to call right before the power
252      *                   off occurs (after the delay).  May be
253      *                   empty if no function is necessary.
254      */
255     SoftPowerOff(uint32_t delay,
256                  std::shared_ptr<PowerInterfaceBase> powerInterface,
257                  PrePowerOffFunc func) :
258         PowerOffAction("Soft Power Off: " + std::to_string(delay) + "s",
259                        powerInterface, func),
260         _delay(delay),
261         _timer(_event, std::bind(std::mem_fn(&SoftPowerOff::powerOff), this))
262     {}
263 
264     /**
265      * @brief Starts a timer upon the expiration of which the
266      *        soft power off will be done.
267      */
268     void start() override
269     {
270         _timer.restartOnce(_delay);
271     }
272 
273     /**
274      * @brief Cancels the timer.  This is always allowed.
275      *
276      * @param[in] force - If the cancel should be forced or not
277      *                    (not checked in this case)
278      * @return bool - Always returns true
279      */
280     bool cancel(bool) override
281     {
282         if (_timer.isEnabled())
283         {
284             _timer.setEnabled(false);
285         }
286 
287         // Can always be canceled
288         return true;
289     }
290 
291     /**
292      * @brief Performs the soft power off.
293      */
294     void powerOff()
295     {
296         if (_prePowerOffFunc)
297         {
298             _prePowerOffFunc();
299         }
300 
301         getLogger().log(
302             fmt::format("Action '{}' executing soft power off", name()));
303         _powerIface->softPowerOff();
304     }
305 
306   private:
307     /**
308      * @brief The number of seconds to wait between starting the
309      *        action and doing the power off.
310      */
311     std::chrono::seconds _delay;
312 
313     /**
314      * @brief The Timer object used to handle the delay.
315      */
316     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _timer;
317 };
318 
319 /**
320  * @class EpowPowerOff
321  *
322  * Still TODO, but has a cancelable service mode delay followed
323  * by an uncancelable meltdown delay followed by a hard power off, with
324  * some sort of EPOW alert in there as well.
325  */
326 class EpowPowerOff : public PowerOffAction
327 {
328   public:
329     EpowPowerOff() = delete;
330     ~EpowPowerOff() = default;
331     EpowPowerOff(const EpowPowerOff&) = delete;
332     EpowPowerOff& operator=(const EpowPowerOff&) = delete;
333     EpowPowerOff(EpowPowerOff&&) = delete;
334     EpowPowerOff& operator=(EpowPowerOff&&) = delete;
335 
336     EpowPowerOff(uint32_t serviceModeDelay, uint32_t meltdownDelay,
337                  std::shared_ptr<PowerInterfaceBase> powerInterface,
338                  PrePowerOffFunc func) :
339         PowerOffAction("EPOW Power Off: " + std::to_string(serviceModeDelay) +
340                            "s/" + std::to_string(meltdownDelay) + "s",
341                        powerInterface, func),
342         _serviceModeDelay(serviceModeDelay), _meltdownDelay(meltdownDelay)
343     {}
344 
345     void start() override
346     {
347         // TODO
348     }
349 
350     bool cancel(bool) override
351     {
352         // TODO
353         return true;
354     }
355 
356   private:
357     std::chrono::seconds _serviceModeDelay;
358     std::chrono::seconds _meltdownDelay;
359 };
360 } // namespace phosphor::fan::monitor
361