regulators: Add Sensors service

Define the abstract base class Sensors.  This class represents a service
that maintains a list of voltage regulator sensors.

This service makes the voltage regulator sensors available to other BMC
applications.  For example, the Redfish support obtains sensor data from
this service.

Each voltage rail in the system may provide multiple types of sensor
data, such as temperature, output voltage, and output current.  A sensor
tracks one of these data types for a voltage rail.

Also define a concrete subclass MockSensors used for test cases.

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: I0b1c28161bad899c6dd75f87b7ee8e891c8781af
diff --git a/phosphor-regulators/src/sensors.hpp b/phosphor-regulators/src/sensors.hpp
new file mode 100644
index 0000000..d23a2b7
--- /dev/null
+++ b/phosphor-regulators/src/sensors.hpp
@@ -0,0 +1,249 @@
+/**
+ * Copyright © 2021 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.
+ */
+#pragma once
+
+#include "services.hpp"
+
+#include <string>
+
+namespace phosphor::power::regulators
+{
+
+/**
+ * Voltage regulator sensor type.
+ */
+enum class SensorType
+{
+    /**
+     * Output current.
+     */
+    iout,
+
+    /**
+     * Highest output current.
+     */
+    iout_peak,
+
+    /**
+     * Lowest output current.
+     */
+    iout_valley,
+
+    /**
+     * Output power.
+     */
+    pout,
+
+    /**
+     * Temperature.
+     */
+    temperature,
+
+    /**
+     * Highest temperature.
+     */
+    temperature_peak,
+
+    /**
+     * Output voltage.
+     */
+    vout,
+
+    /**
+     * Highest output voltage.
+     */
+    vout_peak,
+
+    /**
+     * Lowest output voltage.
+     */
+    vout_valley
+};
+
+/**
+ * @namespace sensors
+ *
+ * Contains utility functions related to voltage regulator sensors.
+ */
+namespace sensors
+{
+
+/**
+ * Returns the name of the specified SensorType.
+ *
+ * The returned string will exactly match the enumerator name, such as
+ * "temperature_peak".
+ *
+ * @param type sensor type
+ * @return sensor type name
+ */
+std::string toString(SensorType type)
+{
+    std::string name{};
+    switch (type)
+    {
+        case SensorType::iout:
+            name = "iout";
+            break;
+        case SensorType::iout_peak:
+            name = "iout_peak";
+            break;
+        case SensorType::iout_valley:
+            name = "iout_valley";
+            break;
+        case SensorType::pout:
+            name = "pout";
+            break;
+        case SensorType::temperature:
+            name = "temperature";
+            break;
+        case SensorType::temperature_peak:
+            name = "temperature_peak";
+            break;
+        case SensorType::vout:
+            name = "vout";
+            break;
+        case SensorType::vout_peak:
+            name = "vout_peak";
+            break;
+        case SensorType::vout_valley:
+            name = "vout_valley";
+            break;
+    }
+    return name;
+}
+
+} // namespace sensors
+
+/**
+ * @class Sensors
+ *
+ * Abstract base class for a service that maintains a list of voltage regulator
+ * sensors.
+ *
+ * This service makes the voltage regulator sensors available to other BMC
+ * applications.  For example, the Redfish support obtains sensor data from this
+ * service.
+ *
+ * Each voltage rail in the system may provide multiple types of sensor data,
+ * such as temperature, output voltage, and output current (see SensorType).  A
+ * sensor tracks one of these data types for a voltage rail.
+ *
+ * Voltage regulator sensors are typically read frequently based on a timer.
+ * Reading all the sensors once is called a monitoring cycle.  The application
+ * will loop through all voltage rails, reading all supported sensor types for
+ * each rail.  During a monitoring cycle, the following sensor service methods
+ * should be called in the specified order:
+ * - startCycle() // At the start of a sensor monitoring cycle
+ * - startRail()  // Before reading all the sensors for one rail
+ * - setValue()   // To set the value of one sensor for the current rail
+ * - endRail()    // After reading all the sensors for one rail
+ * - endCycle()   // At the end of a sensor monitoring cycle
+ *
+ * This service can be enabled or disabled.  It is typically enabled when the
+ * system is powered on and voltage regulators begin producing output.  It is
+ * typically disabled when the system is powered off.  It can also be
+ * temporarily disabled if other BMC applications need to communicate with the
+ * voltage regulator devices.  When the service is disabled, the sensors still
+ * exist but are in an inactive state since their values are not being updated.
+ */
+class Sensors
+{
+  public:
+    // Specify which compiler-generated methods we want
+    Sensors() = default;
+    Sensors(const Sensors&) = delete;
+    Sensors(Sensors&&) = delete;
+    Sensors& operator=(const Sensors&) = delete;
+    Sensors& operator=(Sensors&&) = delete;
+    virtual ~Sensors() = default;
+
+    /**
+     * Enable the sensors service.
+     *
+     * While the service is enabled, the sensors that it provides will be in an
+     * active state.  This indicates that their value is being updated
+     * periodically.
+     *
+     * @param services system services
+     */
+    virtual void enable(Services& services) = 0;
+
+    /**
+     * Notify the sensors service that the current sensor monitoring cycle has
+     * ended.
+     *
+     * @param services system services
+     */
+    virtual void endCycle(Services& services) = 0;
+
+    /**
+     * Notify the sensors service that sensor monitoring has ended for the
+     * current voltage rail.
+     *
+     * @param errorOccurred specifies whether an error occurred while trying to
+     *                      read all the sensors for the current rail
+     * @param services system services
+     */
+    virtual void endRail(bool errorOccurred, Services& services) = 0;
+
+    /**
+     * Disable the sensors service.
+     *
+     * While the service is disabled, the sensors that it provides will be in an
+     * inactive state.  This indicates that their value is not being updated.
+     *
+     * @param services system services
+     */
+    virtual void disable(Services& services) = 0;
+
+    /**
+     * Sets the value of one sensor for the current voltage rail.
+     *
+     * @param type sensor type
+     * @param value sensor value
+     * @param services system services
+     */
+    virtual void setValue(SensorType type, double value,
+                          Services& services) = 0;
+
+    /**
+     * Notify the sensors service that a sensor monitoring cycle is starting.
+     *
+     * @param services system services
+     */
+    virtual void startCycle(Services& services) = 0;
+
+    /**
+     * Notify the sensors service that sensor monitoring is starting for the
+     * specified voltage rail.
+     *
+     * Calls to setValue() will update sensors for this rail.
+     *
+     * @param rail unique rail ID
+     * @param deviceInventoryPath D-Bus inventory path of the voltage regulator
+     *                            device that produces the rail
+     * @param chassisInventoryPath D-Bus inventory path of the chassis that
+     *                             contains the voltage regulator device
+     * @param services system services
+     */
+    virtual void startRail(const std::string& rail,
+                           const std::string& deviceInventoryPath,
+                           const std::string& chassisInventoryPath,
+                           Services& services) = 0;
+};
+
+} // namespace phosphor::power::regulators
diff --git a/phosphor-regulators/test/meson.build b/phosphor-regulators/test/meson.build
index 0fd55de..3f16812 100644
--- a/phosphor-regulators/test/meson.build
+++ b/phosphor-regulators/test/meson.build
@@ -20,6 +20,7 @@
     'rail_tests.cpp',
     'rule_tests.cpp',
     'sensor_monitoring_tests.cpp',
+    'sensors_tests.cpp',
     'system_tests.cpp',
     'temporary_file_tests.cpp',
     'write_verification_error_tests.cpp',
diff --git a/phosphor-regulators/test/mock_sensors.hpp b/phosphor-regulators/test/mock_sensors.hpp
new file mode 100644
index 0000000..0973d7c
--- /dev/null
+++ b/phosphor-regulators/test/mock_sensors.hpp
@@ -0,0 +1,66 @@
+/**
+ * Copyright © 2021 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.
+ */
+#pragma once
+
+#include "sensors.hpp"
+#include "services.hpp"
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+namespace phosphor::power::regulators
+{
+
+/**
+ * @class MockSensors
+ *
+ * Mock implementation of the Sensors interface.
+ */
+class MockSensors : public Sensors
+{
+  public:
+    // Specify which compiler-generated methods we want
+    MockSensors() = default;
+    MockSensors(const MockSensors&) = delete;
+    MockSensors(MockSensors&&) = delete;
+    MockSensors& operator=(const MockSensors&) = delete;
+    MockSensors& operator=(MockSensors&&) = delete;
+    virtual ~MockSensors() = default;
+
+    MOCK_METHOD(void, enable, (Services & services), (override));
+
+    MOCK_METHOD(void, endCycle, (Services & services), (override));
+
+    MOCK_METHOD(void, endRail, (bool errorOccurred, Services& services),
+                (override));
+
+    MOCK_METHOD(void, disable, (Services & services), (override));
+
+    MOCK_METHOD(void, setValue,
+                (SensorType type, double value, Services& services),
+                (override));
+
+    MOCK_METHOD(void, startCycle, (Services & services), (override));
+
+    MOCK_METHOD(void, startRail,
+                (const std::string& rail,
+                 const std::string& deviceInventoryPath,
+                 const std::string& chassisInventoryPath, Services& services),
+                (override));
+};
+
+} // namespace phosphor::power::regulators
diff --git a/phosphor-regulators/test/sensors_tests.cpp b/phosphor-regulators/test/sensors_tests.cpp
new file mode 100644
index 0000000..764bc07
--- /dev/null
+++ b/phosphor-regulators/test/sensors_tests.cpp
@@ -0,0 +1,34 @@
+/**
+ * Copyright © 2021 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 "sensors.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace phosphor::power::regulators;
+using namespace phosphor::power::regulators::sensors;
+
+TEST(SensorsTests, ToString)
+{
+    EXPECT_EQ(toString(SensorType::iout), "iout");
+    EXPECT_EQ(toString(SensorType::iout_peak), "iout_peak");
+    EXPECT_EQ(toString(SensorType::iout_valley), "iout_valley");
+    EXPECT_EQ(toString(SensorType::pout), "pout");
+    EXPECT_EQ(toString(SensorType::temperature), "temperature");
+    EXPECT_EQ(toString(SensorType::temperature_peak), "temperature_peak");
+    EXPECT_EQ(toString(SensorType::vout), "vout");
+    EXPECT_EQ(toString(SensorType::vout_peak), "vout_peak");
+    EXPECT_EQ(toString(SensorType::vout_valley), "vout_valley");
+}