Move GPIO presence object to its own files

This commit moves and renames the PresenceSensor to PresenceGpio
from TachSensors to its own file so that it can be used for more
than just fans.
It is currently only used for GPIO presence monitoring for fans.

This commit maintains the same functionality as the original code.
Once the objects is created for a GPIO, it will be monitoring for
gpiod::line_request::EVENT_BOTH_EDGES changes. The user can call
isPresent() anytime to read the presence status.

Interface:
    EventPresenceGpio(const std::string& iDeviceType,
                      const std::string& iDeviceName,
                      const std::string& gpioName, bool inverted,
                      boost::asio::io_context& io);
Sample usage:
    presenceGpio = std::make_unique<EventPresenceGpio>(
        "Fan", "Fan4b", "FAN4_PRESENCE_R_N", true, io);
    if (presenceGpio->isPresent())
    {
        // Fan is present
    }

Testing: Unable to test this update because no hardware supporting
event driven GPIO detection available.

Change-Id: I1f1a4cbab39d3e3ab38b30288f6aa199ee0cfe3c
Signed-off-by: Chris Cain <cjcain@us.ibm.com>
diff --git a/src/PresenceGpio.cpp b/src/PresenceGpio.cpp
new file mode 100644
index 0000000..6c528d7
--- /dev/null
+++ b/src/PresenceGpio.cpp
@@ -0,0 +1,118 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include "PresenceGpio.hpp"
+
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/posix/stream_descriptor.hpp>
+#include <gpiod.hpp>
+
+#include <iostream>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <system_error>
+
+PresenceGpio::~PresenceGpio()
+{
+    gpioLine.release();
+}
+
+void PresenceGpio::updateAndTracePresence()
+{
+    status = (gpioLine.get_value() != 0);
+    if (status)
+    {
+        logPresent(deviceName);
+    }
+    else
+    {
+        logRemoved(deviceName);
+    }
+}
+
+EventPresenceGpio::EventPresenceGpio(
+    const std::string& iDeviceType, const std::string& iDeviceName,
+    const std::string& gpioName, bool inverted, boost::asio::io_context& io) :
+    PresenceGpio(iDeviceType, iDeviceName), gpioFd(io)
+{
+    gpioLine = gpiod::find_line(gpioName);
+    if (!gpioLine)
+    {
+        std::cerr << "Error requesting gpio: " << gpioName << "\n";
+        return;
+    }
+
+    try
+    {
+        gpioLine.request(
+            {deviceType + "Sensor", gpiod::line_request::EVENT_BOTH_EDGES,
+             inverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
+        updateAndTracePresence();
+
+        int gpioLineFd = gpioLine.event_get_fd();
+        if (gpioLineFd < 0)
+        {
+            std::cerr << "Failed to get " << gpioName << " fd\n";
+            throw std::runtime_error("Failed to get GPIO fd " + gpioName);
+        }
+
+        gpioFd.assign(gpioLineFd);
+    }
+    catch (const std::system_error& e)
+    {
+        std::cerr << "Error reading gpio " << gpioName << ": " << e.what()
+                  << "\n";
+        return;
+    }
+
+    monitorPresence();
+}
+
+void EventPresenceGpio::monitorPresence()
+{
+    std::weak_ptr<EventPresenceGpio> weakRef = weak_from_this();
+    gpioFd.async_wait(
+        boost::asio::posix::stream_descriptor::wait_read,
+        [weakRef](const boost::system::error_code& ec) {
+            std::shared_ptr<EventPresenceGpio> self = weakRef.lock();
+            if (!self)
+            {
+                std::cerr << "Failed to get lock for eventPresenceGpio: "
+                          << ec.message() << "\n";
+                return;
+            }
+            if (ec)
+            {
+                if (ec != boost::system::errc::bad_file_descriptor)
+                {
+                    std::cerr
+                        << "Error on event presence device " << self->deviceName
+                        << ": " << ec.message() << "\n";
+                }
+                return;
+            }
+            self->read();
+            self->monitorPresence();
+        });
+}
+
+void EventPresenceGpio::read()
+{
+    // Read is invoked when an edge event is detected by monitorPresence
+    gpioLine.event_read();
+    updateAndTracePresence();
+}