Fan and TachSensor class introduction

A Fan object has one or more TachSensor objects.

The TachSensor class is used to keep track of the
the actual and expected speeds.  It only tracks
expected speeds if the _hasTarget attribute is true.

Future commits will add more functionality.

Change-Id: I9bb5fac39f25c5c31c18457ebedf82838fcf6641
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/monitor/Makefile.am b/monitor/Makefile.am
index ceec3dd..26b66b5 100644
--- a/monitor/Makefile.am
+++ b/monitor/Makefile.am
@@ -5,8 +5,10 @@
 	phosphor-fan-monitor
 
 phosphor_fan_monitor_SOURCES = \
+	fan.cpp \
 	generated.cpp \
-	main.cpp
+	main.cpp \
+	tach_sensor.cpp
 
 phosphor_fan_monitor_LDADD = \
 	$(SDBUSPLUS_LIBS) \
diff --git a/monitor/fan.cpp b/monitor/fan.cpp
new file mode 100644
index 0000000..b29eaab
--- /dev/null
+++ b/monitor/fan.cpp
@@ -0,0 +1,113 @@
+/**
+ * Copyright © 2017 IBM 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 <algorithm>
+#include <phosphor-logging/log.hpp>
+#include "fan.hpp"
+#include "types.hpp"
+
+namespace phosphor
+{
+namespace fan
+{
+namespace monitor
+{
+
+using namespace phosphor::logging;
+
+Fan::Fan(sdbusplus::bus::bus& bus,
+         std::shared_ptr<sd_event>&  events,
+         const FanDefinition& def) :
+    _bus(bus),
+    _name(std::get<fanNameField>(def)),
+    _deviation(std::get<fanDeviationField>(def)),
+    _numSensorFailsForNonFunc(std::get<numSensorFailsForNonfuncField>(def))
+{
+    auto& sensors = std::get<sensorListField>(def);
+
+    for (auto& s : sensors)
+    {
+        _sensors.emplace_back(
+            std::make_unique<TachSensor>(bus,
+                                         *this,
+                                         std::get<sensorNameField>(s),
+                                         std::get<hasTargetField>(s),
+                                         std::get<timeoutField>(def)));
+
+    }
+
+}
+
+
+uint64_t Fan::getTargetSpeed(const TachSensor& sensor)
+{
+    uint64_t target = 0;
+
+    if (sensor.hasTarget())
+    {
+        target = sensor.getTarget();
+    }
+    else
+    {
+        //The sensor doesn't support a target,
+        //so get it from another sensor.
+        auto s = std::find_if(_sensors.begin(), _sensors.end(),
+                              [](const auto& s)
+                              {
+                                  return s->hasTarget();
+                              });
+
+        if (s != _sensors.end())
+        {
+            target = (*s)->getTarget();
+        }
+    }
+
+    return target;
+}
+
+
+bool Fan::tooManySensorsNonfunctional()
+{
+    size_t numFailed =  std::count_if(_sensors.begin(), _sensors.end(),
+                                      [](const auto& s)
+                                      {
+                                          return !s->functional();
+                                      });
+
+    return (numFailed >= _numSensorFailsForNonFunc);
+}
+
+
+bool Fan::outOfRange(const TachSensor& sensor)
+{
+    auto actual = static_cast<uint64_t>(sensor.getInput());
+    auto target = getTargetSpeed(sensor);
+
+    uint64_t min = target * (100 - _deviation) / 100;
+    uint64_t max = target * (100 + _deviation) / 100;
+
+    if ((actual < min) || (actual > max))
+    {
+        return true;
+    }
+
+    return false;
+}
+
+
+}
+}
+}
diff --git a/monitor/fan.hpp b/monitor/fan.hpp
new file mode 100644
index 0000000..6cbd370
--- /dev/null
+++ b/monitor/fan.hpp
@@ -0,0 +1,134 @@
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+#include <tuple>
+#include <vector>
+#include "tach_sensor.hpp"
+#include "types.hpp"
+
+namespace phosphor
+{
+namespace fan
+{
+namespace monitor
+{
+
+
+/**
+ * @class Fan
+ *
+ * Represents a fan, which can contain 1 or more sensors which
+ * loosely correspond to rotors.  See below.
+ *
+ * There is a sensor when hwmon exposes one, which means there is a
+ * speed value to be read.  Sometimes there is a sensor per rotor,
+ * and other times multiple rotors just use 1 sensor total where
+ * the sensor reports the slowest speed of all of the rotors.
+ *
+ * A rotor's speed is set by writing the Target value of a sensor.
+ * Sometimes each sensor in a fan supports having a Target, and other
+ * times not all of them do.  A TachSensor object knows if it supports
+ * the Target property.
+ *
+ * The strategy for monitoring fan speeds is as follows:
+ *
+ * Every time a Target (new speed written) or Input (actual speed read)
+ * sensor changes, check if the input value is within some range of the target
+ * value.  If it isn't, start a timer at the end of which the sensor will be
+ * set to not functional.  If enough sensors in the fan are now nonfunctional,
+ * set the whole fan to nonfunctional in the inventory.
+ *
+ * When sensor inputs come back within a specified range of the target,
+ * stop its timer if running, make the sensor functional again if it wasn't,
+ * and if enough sensors in the fan are now functional set the whole fan
+ * back to functional in the inventory.
+ */
+class Fan
+{
+
+    public:
+
+        Fan() = delete;
+        Fan(const Fan&) = delete;
+        Fan(Fan&&) = default;
+        Fan& operator=(const Fan&) = delete;
+        Fan& operator=(Fan&&) = default;
+        ~Fan() = default;
+
+        /**
+         * @brief Constructor
+         *
+         * @param bus - the dbus object
+         * @param events - pointer to sd_event object
+         * @param def - the fan definition structure
+         */
+        Fan(sdbusplus::bus::bus& bus,
+            std::shared_ptr<sd_event>& events,
+            const FanDefinition& def);
+
+
+    private:
+
+        /**
+         * @brief Returns the target speed of the sensor
+         *
+         * If the sensor itself doesn't have a target, it finds
+         * the target speed from another sensor.
+         *
+         * @param[in] sensor - the sensor to get the target speed for
+         */
+        uint64_t getTargetSpeed(const TachSensor& sensor);
+
+        /**
+         * @brief Returns true if the sensor input is not within
+         * some deviation of the target.
+         *
+         * @param[in] sensor - the sensor to check
+         */
+        bool outOfRange(const TachSensor& sensor);
+
+        /**
+         * @brief Returns true if too many sensors are nonfunctional
+         *        as defined by _numSensorFailsForNonFunc
+         */
+        bool tooManySensorsNonfunctional();
+
+
+        /**
+         * @brief the dbus object
+         */
+        sdbusplus::bus::bus& _bus;
+
+        /**
+         * @brief The inventory name of the fan
+         */
+        const std::string _name;
+
+        /**
+         * @brief The percentage that the input speed must be below
+         *        the target speed to be considered an error.
+         *        Between 0 and 100.
+         */
+        const size_t _deviation;
+
+        /**
+         * The number of sensors that must be nonfunctional at the
+         * same time in order for the fan to be set to nonfunctional
+         * in the inventory.
+         */
+        const size_t _numSensorFailsForNonFunc;
+
+        /**
+         * @brief The current functional state of the fan
+         */
+        bool _functional = true;
+
+        /**
+         * The sensor objects for the fan
+         */
+        std::vector<std::unique_ptr<TachSensor>> _sensors;
+};
+
+}
+}
+}
diff --git a/monitor/tach_sensor.cpp b/monitor/tach_sensor.cpp
new file mode 100644
index 0000000..3264ddf
--- /dev/null
+++ b/monitor/tach_sensor.cpp
@@ -0,0 +1,49 @@
+/**
+ * Copyright © 2017 IBM 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 <phosphor-logging/log.hpp>
+#include "fan.hpp"
+#include "tach_sensor.hpp"
+#include "../utility.hpp"
+
+namespace phosphor
+{
+namespace fan
+{
+namespace monitor
+{
+
+constexpr auto FAN_SENSOR_PATH = "/xyz/openbmc_project/sensors/fan_tach/";
+
+
+TachSensor::TachSensor(sdbusplus::bus::bus& bus,
+                       Fan& fan,
+                       const std::string& id,
+                       bool hasTarget,
+                       size_t timeout) :
+
+    _bus(bus),
+    _fan(fan),
+    _name(FAN_SENSOR_PATH + id),
+    _hasTarget(hasTarget),
+    _timeout(timeout)
+{
+    //TODO
+}
+
+
+}
+}
+}
diff --git a/monitor/tach_sensor.hpp b/monitor/tach_sensor.hpp
new file mode 100644
index 0000000..df340f5
--- /dev/null
+++ b/monitor/tach_sensor.hpp
@@ -0,0 +1,146 @@
+#pragma once
+
+#include <chrono>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server.hpp>
+
+namespace phosphor
+{
+namespace fan
+{
+namespace monitor
+{
+
+class Fan;
+
+
+/**
+ * @class TachSensor
+ *
+ * This class represents the sensor that reads a tach value.
+ * It may also support a Target, which is the property used to
+ * set a speed.  Since it doesn't necessarily have a Target, it
+ * won't for sure know if it is running too slow, so it leaves
+ * that determination to other code.
+ *
+ * This class has a parent Fan object that knows about all
+ * sensors for that fan.
+ */
+class TachSensor
+{
+    public:
+
+        TachSensor() = delete;
+        TachSensor(const TachSensor&) = delete;
+        TachSensor(TachSensor&&) = default;
+        TachSensor& operator=(const TachSensor&) = delete;
+        TachSensor& operator=(TachSensor&&) = default;
+        ~TachSensor() = default;
+
+        /**
+         * @brief Constructor
+         *
+         * @param[in] bus - the dbus object
+         * @param[in] fan - the parent fan object
+         * @param[in] id - the id of the sensor
+         * @param[in] hasTarget - if the sensor supports
+         *                        setting the speed
+         * @param[in] timeout - Normal timeout value to use
+         */
+        TachSensor(sdbusplus::bus::bus& bus,
+                   Fan& fan,
+                   const std::string& id,
+                   bool hasTarget,
+                   size_t timeout);
+
+        /**
+         * @brief Returns the target speed value
+         */
+        inline uint64_t getTarget() const
+        {
+            return _tachTarget;
+        }
+
+        /**
+         * @brief Returns the input speed value
+         */
+        inline int64_t getInput() const
+        {
+            return _tachInput;
+        }
+
+        /**
+         * @brief Returns true if sensor has a target
+         */
+        inline bool hasTarget() const
+        {
+            return _hasTarget;
+        }
+
+        /**
+         * Returns true if the hardware behind this
+         * sensor is considered working OK/functional.
+         */
+        inline bool functional() const
+        {
+            return _functional;
+        }
+
+        /**
+         * Sets functional status
+         */
+        inline void setFunctional(bool functional)
+        {
+            _functional = functional;
+        }
+
+    private:
+
+        /**
+         * @brief the dbus object
+         */
+        sdbusplus::bus::bus& _bus;
+
+        /**
+         * @brief Reference to the parent Fan object
+         */
+        Fan& _fan;
+
+        /**
+         * @brief The name of the sensor, including the full path
+         *
+         * For example /xyz/openbmc_project/sensors/fan_tach/fan0
+         */
+        const std::string _name;
+
+        /**
+         * @brief If functional (not too slow).  The parent
+         *        fan object sets this.
+         */
+        bool _functional = true;
+
+        /**
+         * @brief If the sensor has a Target property (can set speed)
+         */
+        const bool _hasTarget;
+
+        /**
+         * @brief The input speed, from the Value dbus property
+         */
+        int64_t _tachInput = 0;
+
+        /**
+         * @brief The current target speed, from the Target dbus property
+         *        (if applicable)
+         */
+        uint64_t _tachTarget = 0;
+
+        /**
+         * @brief The timeout value to use
+         */
+        const size_t _timeout;
+};
+
+}
+}
+}