regulators: Create DBusSensors class

Create the DBusSensors class.  This is a concrete implementation of the
Sensors abstract base class.  This class manages all the voltage
regulator sensors in the system.

Also add a lastUpdateTime data member to the DBusSensor class.  This
data member is set whenever the sensor is updated.  This enables the
DBusSensors class to detect sensors that were not updated during the
current monitoring cycle.

Sensors that were not updated during the current monitoring cycle are
deleted.  These sensors were likely produced by a hardware device that
was removed or replaced with a different version.

Tested:
* Ran through entire monitoring cycle multiple times
* Tested that lastUpdateTime is set correctly when a sensor is modified
  * Sensor value updated
  * Sensor disabled
  * Sensor put in error state
* Tested where new sensor was created
* Tested where existing sensor was updated
* Tested where all sensors disabled
* Tested where all sensors for a rail put in error state
* Tested where sensors removed due to not being updated this cycle
* Tested where D-Bus exception occurs when trying to create a sensor
* See complete test plan at
  https://gist.github.com/smccarney/69efb813c0005571aee687f67e489278

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: Ib1fc399f100188cc048ac3ab5892117b74f844e9
diff --git a/phosphor-regulators/src/dbus_sensors.cpp b/phosphor-regulators/src/dbus_sensors.cpp
new file mode 100644
index 0000000..eee7bc2
--- /dev/null
+++ b/phosphor-regulators/src/dbus_sensors.cpp
@@ -0,0 +1,122 @@
+/**
+ * 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 "dbus_sensors.hpp"
+
+#include <utility>
+
+namespace phosphor::power::regulators
+{
+
+void DBusSensors::enable(Services& /*services*/)
+{
+    // Currently nothing to do here.  The next monitoring cycle will set the
+    // values of all sensors, and that will set them to the enabled state.
+}
+
+void DBusSensors::endCycle(Services& services)
+{
+    // Delete any sensors that were not updated during this monitoring cycle.
+    // This can happen if the hardware device producing the sensors was removed
+    // or replaced with a different version.
+    auto it = sensors.begin();
+    while (it != sensors.end())
+    {
+        // Increment iterator before potentially deleting the sensor.
+        // map::erase() invalidates iterators/references to the erased element.
+        auto& [sensorName, sensor] = *it;
+        ++it;
+
+        // Check if last update time for sensor is before cycle start time
+        if (sensor->getLastUpdateTime() < cycleStartTime)
+        {
+            services.getJournal().logDebug("Deleted sensor " + sensorName);
+            sensors.erase(sensorName);
+        }
+    }
+}
+
+void DBusSensors::endRail(bool errorOccurred, Services& /*services*/)
+{
+    // If an error occurred, set all sensors for current rail to the error state
+    if (errorOccurred)
+    {
+        for (auto& [sensorName, sensor] : sensors)
+        {
+            if (sensor->getRail() == rail)
+            {
+                sensor->setToErrorState();
+            }
+        }
+    }
+
+    // Clear current rail information
+    rail.clear();
+    deviceInventoryPath.clear();
+    chassisInventoryPath.clear();
+}
+
+void DBusSensors::disable(Services& /*services*/)
+{
+    // Disable all sensors
+    for (auto& [sensorName, sensor] : sensors)
+    {
+        sensor->disable();
+    }
+}
+
+void DBusSensors::setValue(SensorType type, double value, Services& services)
+{
+    // Build unique sensor name based on rail and sensor type
+    std::string sensorName{rail + '_' + sensors::toString(type)};
+
+    // Check to see if the sensor already exists
+    auto it = sensors.find(sensorName);
+    if (it != sensors.end())
+    {
+        // Sensor exists; update value
+        it->second->setValue(value);
+    }
+    else
+    {
+        // Sensor doesn't exist; create it and add it to the map
+        auto sensor = std::make_unique<DBusSensor>(bus, sensorName, type, value,
+                                                   rail, deviceInventoryPath,
+                                                   chassisInventoryPath);
+        sensors.emplace(sensorName, std::move(sensor));
+        services.getJournal().logDebug("Created sensor " + sensorName);
+    }
+}
+
+void DBusSensors::startCycle(Services& /*services*/)
+{
+    // Store the time when this monitoring cycle started.  This is used to
+    // detect sensors that were not updated during this cycle.
+    cycleStartTime = std::chrono::system_clock::now();
+}
+
+void DBusSensors::startRail(const std::string& rail,
+                            const std::string& deviceInventoryPath,
+                            const std::string& chassisInventoryPath,
+                            Services& /*services*/)
+{
+    // Store current rail information; used later by setValue() and endRail()
+    this->rail = rail;
+    this->deviceInventoryPath = deviceInventoryPath;
+    this->chassisInventoryPath = chassisInventoryPath;
+}
+
+} // namespace phosphor::power::regulators