sensors: Align source structure away from anti-patterns

The anti-patterns document comments on source structure, specifically
on placing internal headers in a parallel subtree[1]. dbus-sensors is an
example of violating this anti-pattern, so fix it.

[1]: https://github.com/openbmc/docs/blob/master/anti-patterns.md#placing-internal-headers-in-a-parallel-subtree

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Change-Id: I50ecaddd53fa9c9b7a0441af9de5e60bd94e47c6
diff --git a/src/TachSensor.hpp b/src/TachSensor.hpp
new file mode 100644
index 0000000..92c5202
--- /dev/null
+++ b/src/TachSensor.hpp
@@ -0,0 +1,126 @@
+#pragma once
+
+#include "Thresholds.hpp"
+#include "sensor.hpp"
+
+#include <boost/asio/random_access_file.hpp>
+#include <boost/container/flat_map.hpp>
+#include <boost/container/flat_set.hpp>
+#include <gpiod.hpp>
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+
+class PresenceSensor
+{
+  public:
+    PresenceSensor(const std::string& gpioName, bool inverted,
+                   boost::asio::io_service& io, const std::string& name);
+    ~PresenceSensor();
+
+    void monitorPresence(void);
+    void read(void);
+    bool getValue(void) const;
+
+  private:
+    bool status = true;
+    gpiod::line gpioLine;
+    boost::asio::posix::stream_descriptor gpioFd;
+    std::string name;
+};
+
+namespace redundancy
+{
+constexpr const char* full = "Full";
+constexpr const char* degraded = "Degraded";
+constexpr const char* failed = "Failed";
+} // namespace redundancy
+
+class RedundancySensor
+{
+  public:
+    RedundancySensor(size_t count, const std::vector<std::string>& children,
+                     sdbusplus::asio::object_server& objectServer,
+                     const std::string& sensorConfiguration);
+    ~RedundancySensor();
+
+    void update(const std::string& name, bool failed);
+
+  private:
+    size_t count;
+    std::string state = redundancy::full;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> iface;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> association;
+    sdbusplus::asio::object_server& objectServer;
+    boost::container::flat_map<std::string, bool> statuses;
+};
+
+class TachSensor :
+    public Sensor,
+    public std::enable_shared_from_this<TachSensor>
+{
+  public:
+    TachSensor(const std::string& path, const std::string& objectType,
+               sdbusplus::asio::object_server& objectServer,
+               std::shared_ptr<sdbusplus::asio::connection>& conn,
+               std::unique_ptr<PresenceSensor>&& presence,
+               std::optional<RedundancySensor>* redundancy,
+               boost::asio::io_service& io, const std::string& fanName,
+               std::vector<thresholds::Threshold>&& thresholds,
+               const std::string& sensorConfiguration,
+               const std::pair<double, double>& limits,
+               const PowerState& powerState,
+               const std::optional<std::string>& led);
+    ~TachSensor() override;
+    void setupRead();
+
+  private:
+    // Ordering is important here; readBuf is first so that it's not destroyed
+    // while async operations from other member fields might still be using it.
+    std::array<char, 128> readBuf{};
+    sdbusplus::asio::object_server& objServer;
+    std::optional<RedundancySensor>* redundancy;
+    std::unique_ptr<PresenceSensor> presence;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface;
+    std::shared_ptr<sdbusplus::asio::dbus_interface> itemAssoc;
+    boost::asio::random_access_file inputDev;
+    boost::asio::steady_timer waitTimer;
+    std::string path;
+    std::optional<std::string> led;
+    bool ledState = false;
+
+    void handleResponse(const boost::system::error_code& err, size_t bytesRead);
+    void restartRead(size_t pollTime);
+    void checkThresholds(void) override;
+};
+
+inline void logFanInserted(const std::string& device)
+{
+    const auto* msg = "OpenBMC.0.1.FanInserted";
+    lg2::error("Fan Inserted", "REDFISH_MESSAGE_ID", msg,
+               "REDFISH_MESSAGE_ARGS", device);
+}
+
+inline void logFanRemoved(const std::string& device)
+{
+    const auto* msg = "OpenBMC.0.1.FanRemoved";
+    lg2::error("Fan Removed", "REDFISH_MESSAGE_ID", msg, "REDFISH_MESSAGE_ARGS",
+               device);
+}
+
+inline void logFanRedundancyLost(void)
+{
+    const auto* msg = "OpenBMC.0.1.FanRedundancyLost";
+    lg2::error("Fan Inserted", "REDFISH_MESSAGE_ID", msg);
+}
+
+inline void logFanRedundancyRestored(void)
+{
+    const auto* msg = "OpenBMC.0.1.FanRedundancyRegained";
+    lg2::error("Fan Removed", "REDFISH_MESSAGE_ID", msg);
+}