PEL: On new PEL, send to host if necessary

When a new PEL comes in, send it to the host now if the host
is up and the class is currently idle.

The PLDM command will be dispatched in a standalone function
called from the event loop so that this function, which may
be part of the 'Create new log' D-Bus method response, can
return first.

Also added testcases to start verifying these paths now that
there is a full mock of the HostInterface class.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: Ib30a99cc61be9205122287ba310bc315d762e863
diff --git a/extensions/openpower-pels/host_notifier.cpp b/extensions/openpower-pels/host_notifier.cpp
index 8fe6955..f2d951f 100644
--- a/extensions/openpower-pels/host_notifier.cpp
+++ b/extensions/openpower-pels/host_notifier.cpp
@@ -155,7 +155,41 @@
 
     _pelQueue.push_back(pel.id());
 
-    // TODO: Check if a send is needed now
+    if (!_dataIface.isHostUp())
+    {
+        return;
+    }
+
+    // Dispatch a command now if there isn't currently a command
+    // in progress and this is the first log in the queue or it
+    // previously gave up from a hard failure.
+    auto inProgress = (_inProgressPEL != 0) || _hostIface->cmdInProgress() ||
+                      _retryTimer.isEnabled();
+
+    auto firstPEL = _pelQueue.size() == 1;
+    auto gaveUp = _retryCount >= maxRetryAttempts;
+
+    if (!inProgress && (firstPEL || gaveUp))
+    {
+        _retryCount = 0;
+
+        // Send a log, but from the event loop, not from here.
+        scheduleDispatch();
+    }
+}
+
+void HostNotifier::scheduleDispatch()
+{
+    _dispatcher = std::make_unique<sdeventplus::source::Defer>(
+        _hostIface->getEvent(), std::bind(std::mem_fn(&HostNotifier::dispatch),
+                                          this, std::placeholders::_1));
+}
+
+void HostNotifier::dispatch(sdeventplus::source::EventBase& source)
+{
+    _dispatcher.reset();
+
+    doNewLogNotify();
 }
 
 void HostNotifier::doNewLogNotify()
diff --git a/extensions/openpower-pels/host_notifier.hpp b/extensions/openpower-pels/host_notifier.hpp
index 127eb03..3e50c83 100644
--- a/extensions/openpower-pels/host_notifier.hpp
+++ b/extensions/openpower-pels/host_notifier.hpp
@@ -6,6 +6,7 @@
 
 #include <deque>
 #include <sdeventplus/clock.hpp>
+#include <sdeventplus/source/event.hpp>
 #include <sdeventplus/utility/timer.hpp>
 
 namespace openpower::pels
@@ -86,6 +87,10 @@
      * @brief This function gets called by the Repository class
      *        when a new PEL is added to it.
      *
+     * This function puts the PEL on the queue to be sent up if it
+     * needs it, and possibly dispatch the send if the conditions call
+     * for it.
+     *
      * @param[in] pel - The new PEL
      */
     void newLogCallback(const PEL& pel);
@@ -108,6 +113,20 @@
     void doNewLogNotify();
 
     /**
+     * @brief Creates the event object to handle sending the PLDM
+     *        command from the event loop.
+     */
+    void scheduleDispatch();
+
+    /**
+     * @brief Kicks off the PLDM send, but called from the event
+     *        loop.
+     *
+     * @param[in] source - The event source object
+     */
+    void dispatch(sdeventplus::source::EventBase& source);
+
+    /**
      * @brief Called when the host changes state.
      *
      * If the new state is host up and there are PELs to send, it
@@ -192,6 +211,13 @@
      * @brief The command retry timer.
      */
     sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> _retryTimer;
+
+    /**
+     * @brief The object used to dispatch a new PEL send from the
+     *        event loop, so the calling function can be returned from
+     *        first.
+     */
+    std::unique_ptr<sdeventplus::source::Defer> _dispatcher;
 };
 
 } // namespace openpower::pels