psu-sensor: Add devmgmt and powercallback to PSU

Some PSU sensors are dependent on system power state.
Without change the device initialization would fail and
dbus-sensors and entity-manager wouldn't come back to
properly instantiate.

Same intention as this hwmontempsensor change: https://github.com/openbmc/dbus-sensors/commit/a1456c4abafc697e7caa6b8e95ac9ddb35c4e7d1

Tested: Same sensors are getting created.  Sensors are
getting created/destroyed on host power events based
on their PowerState.

Signed-off-by: Matt Simmering <matthew.simmering@intel.com>
Change-Id: I3ee719cf65df225f964148d3994eec4d758d72a1
diff --git a/src/DeviceMgmt.hpp b/src/DeviceMgmt.hpp
index 170cd37..5e469de 100644
--- a/src/DeviceMgmt.hpp
+++ b/src/DeviceMgmt.hpp
@@ -63,6 +63,16 @@
         sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap,
         const std::function<void(sdbusplus::message_t&)>& handler);
 
+// Helper find function because some sensors use underscores in their names
+// while others use spaces. Ignore this discrepancy by changing all spaces to
+// underscores.
+inline size_t sensorNameFind(const std::string& fullName,
+                             const std::string& partialName)
+{
+    return boost::replace_all_copy(fullName, " ", "_")
+        .find(boost::replace_all_copy(partialName, " ", "_"));
+}
+
 // returns a {path: <I2CDevice, is_new>} map.  is_new indicates if the I2CDevice
 // is newly instantiated by this call (true) or was already there (false).
 template <class T>
@@ -92,16 +102,31 @@
             {
                 continue;
             }
-            std::string sensorName =
-                std::get<std::string>(findSensorName->second);
 
-            auto findSensor = sensors.find(sensorName);
-            if (findSensor != sensors.end() && findSensor->second != nullptr &&
-                findSensor->second->isActive())
+            const auto* sensorName =
+                std::get_if<std::string>(&findSensorName->second);
+            if (sensorName == nullptr)
+            {
+                std::cerr << "Unable to find sensor name " << name
+                          << " on path " << path.str << "\n";
+                continue;
+            }
+
+            std::shared_ptr<T> findSensor(nullptr);
+            for (const auto& sensor : sensors)
+            {
+                if (sensorNameFind(sensor.first, *sensorName) !=
+                    std::string::npos)
+                {
+                    findSensor = sensor.second;
+                    break;
+                }
+            }
+            if (findSensor != nullptr && findSensor->isActive())
             {
                 devices.emplace(
                     path.str,
-                    std::make_pair(findSensor->second->getI2CDevice(), false));
+                    std::make_pair(findSensor->getI2CDevice(), false));
                 continue;
             }
 
diff --git a/src/PSUSensor.cpp b/src/PSUSensor.cpp
index 763a926..ecf546e 100644
--- a/src/PSUSensor.cpp
+++ b/src/PSUSensor.cpp
@@ -44,10 +44,11 @@
                      const PowerState& powerState,
                      const std::string& sensorUnits, unsigned int factor,
                      double max, double min, double offset,
-                     const std::string& label, size_t tSize, double pollRate) :
+                     const std::string& label, size_t tSize, double pollRate,
+                     const std::shared_ptr<I2CDevice>& i2cDevice) :
     Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration,
            objectType, false, false, max, min, conn, powerState),
-    objServer(objectServer),
+    i2cDevice(i2cDevice), objServer(objectServer),
     inputDev(io, path, boost::asio::random_access_file::read_only),
     waitTimer(io), path(path), sensorFactor(factor), sensorOffset(offset),
     thresholdTimer(io)
@@ -98,8 +99,8 @@
 
 PSUSensor::~PSUSensor()
 {
-    waitTimer.cancel();
-    inputDev.close();
+    deactivate();
+
     objServer.remove_interface(sensorInterface);
     for (const auto& iface : thresholdInterfaces)
     {
@@ -108,6 +109,31 @@
     objServer.remove_interface(association);
 }
 
+bool PSUSensor::isActive()
+{
+    return inputDev.is_open();
+}
+
+void PSUSensor::activate(const std::string& newPath,
+                         const std::shared_ptr<I2CDevice>& newI2CDevice)
+{
+    path = newPath;
+    i2cDevice = newI2CDevice;
+    inputDev.open(path, boost::asio::random_access_file::read_only);
+    markAvailable(true);
+    setupRead();
+}
+
+void PSUSensor::deactivate()
+{
+    markAvailable(false);
+    // close the input dev to cancel async operations
+    inputDev.close();
+    waitTimer.cancel();
+    i2cDevice = nullptr;
+    path = "";
+}
+
 void PSUSensor::setupRead(void)
 {
     if (!readingStateGood())
diff --git a/src/PSUSensor.hpp b/src/PSUSensor.hpp
index 49e181e..69f365b 100644
--- a/src/PSUSensor.hpp
+++ b/src/PSUSensor.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "DeviceMgmt.hpp"
 #include "PwmSensor.hpp"
 #include "Thresholds.hpp"
 #include "sensor.hpp"
@@ -23,15 +24,26 @@
               const std::string& sensorConfiguration,
               const PowerState& powerState, const std::string& sensorUnits,
               unsigned int factor, double max, double min, double offset,
-              const std::string& label, size_t tSize, double pollRate);
+              const std::string& label, size_t tSize, double pollRate,
+              const std::shared_ptr<I2CDevice>& i2cDevice);
     ~PSUSensor() override;
     void setupRead(void);
+    void activate(const std::string& newPath,
+                  const std::shared_ptr<I2CDevice>& newI2CDevice);
+    void deactivate(void);
+    bool isActive(void);
+
+    std::shared_ptr<I2CDevice> getI2CDevice() const
+    {
+        return i2cDevice;
+    }
 
   private:
     // Note, this buffer is a shared_ptr because during a read, its lifetime
     // might have to outlive the PSUSensor class if the object gets destroyed
     // while in the middle of a read operation
     std::shared_ptr<std::array<char, 128>> buffer;
+    std::shared_ptr<I2CDevice> i2cDevice;
     sdbusplus::asio::object_server& objServer;
     boost::asio::random_access_file inputDev;
     boost::asio::steady_timer waitTimer;
diff --git a/src/PSUSensorMain.cpp b/src/PSUSensorMain.cpp
index d4a7895..510dde4 100644
--- a/src/PSUSensorMain.cpp
+++ b/src/PSUSensorMain.cpp
@@ -284,11 +284,14 @@
     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
     const ManagedObjectType& sensorConfigs,
     const std::shared_ptr<boost::container::flat_set<std::string>>&
-        sensorsChanged)
+        sensorsChanged,
+    bool activateOnly)
 {
     int numCreated = 0;
     bool firstScan = sensorsChanged == nullptr;
 
+    auto devices = instantiateDevices(sensorConfigs, sensors, sensorTypes);
+
     std::vector<fs::path> pmbusPaths;
     if (!findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths))
     {
@@ -434,6 +437,18 @@
             continue;
         }
 
+        auto findI2CDev = devices.find(*interfacePath);
+
+        std::shared_ptr<I2CDevice> i2cDev;
+        if (findI2CDev != devices.end())
+        {
+            if (activateOnly && !findI2CDev->second.second)
+            {
+                continue;
+            }
+            i2cDev = findI2CDev->second.first;
+        }
+
         auto findPSUName = baseConfig->find("Name");
         if (findPSUName == baseConfig->end())
         {
@@ -887,19 +902,33 @@
                           << "\"\n";
             }
             // destruct existing one first if already created
-            sensors[sensorName] = nullptr;
-            sensors[sensorName] = std::make_shared<PSUSensor>(
-                sensorPathStr, sensorType, objectServer, dbusConnection, io,
-                sensorName, std::move(sensorThresholds), *interfacePath,
-                readState, findSensorUnit->second, factor,
-                psuProperty->maxReading, psuProperty->minReading,
-                psuProperty->sensorOffset, labelHead, thresholdConfSize,
-                pollRate);
-            sensors[sensorName]->setupRead();
-            ++numCreated;
-            if constexpr (debug)
+
+            auto& sensor = sensors[sensorName];
+            if (!activateOnly)
             {
-                std::cerr << "Created " << numCreated << " sensors so far\n";
+                sensor = nullptr;
+            }
+
+            if (sensor != nullptr)
+            {
+                sensor->activate(sensorPathStr, i2cDev);
+            }
+            else
+            {
+                sensors[sensorName] = std::make_shared<PSUSensor>(
+                    sensorPathStr, sensorType, objectServer, dbusConnection, io,
+                    sensorName, std::move(sensorThresholds), *interfacePath,
+                    readState, findSensorUnit->second, factor,
+                    psuProperty->maxReading, psuProperty->minReading,
+                    psuProperty->sensorOffset, labelHead, thresholdConfSize,
+                    pollRate, i2cDev);
+                sensors[sensorName]->setupRead();
+                ++numCreated;
+                if constexpr (debug)
+                {
+                    std::cerr << "Created " << numCreated
+                              << " sensors so far\n";
+                }
             }
         }
 
@@ -922,13 +951,14 @@
     boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
     const std::shared_ptr<boost::container::flat_set<std::string>>&
-        sensorsChanged)
+        sensorsChanged,
+    bool activateOnly)
 {
     auto getter = std::make_shared<GetSensorConfiguration>(
-        dbusConnection, [&io, &objectServer, &dbusConnection, sensorsChanged](
-                            const ManagedObjectType& sensorConfigs) {
+        dbusConnection, [&io, &objectServer, &dbusConnection, sensorsChanged,
+                         activateOnly](const ManagedObjectType& sensorConfigs) {
             createSensorsCallback(io, objectServer, dbusConnection,
-                                  sensorConfigs, sensorsChanged);
+                                  sensorConfigs, sensorsChanged, activateOnly);
         });
     std::vector<std::string> types(sensorTypes.size());
     for (const auto& [type, dt] : sensorTypes)
@@ -1057,6 +1087,29 @@
                          {"fan4", {"fan4_alarm", "fan4_fault"}}}}};
 }
 
+static void powerStateChanged(
+    PowerState type, bool newState,
+    boost::container::flat_map<std::string, std::shared_ptr<PSUSensor>>&
+        sensors,
+    boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
+    std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
+{
+    if (newState)
+    {
+        createSensors(io, objectServer, dbusConnection, nullptr, true);
+    }
+    else
+    {
+        for (auto& [path, sensor] : sensors)
+        {
+            if (sensor != nullptr && sensor->readState == type)
+            {
+                sensor->deactivate();
+            }
+        }
+    }
+}
+
 int main()
 {
     boost::asio::io_context io;
@@ -1071,8 +1124,16 @@
 
     propertyInitialize();
 
-    boost::asio::post(
-        io, [&]() { createSensors(io, objectServer, systemBus, nullptr); });
+    auto powerCallBack =
+        [&io, &objectServer, &systemBus](PowerState type, bool state) {
+        powerStateChanged(type, state, sensors, io, objectServer, systemBus);
+    };
+
+    setupPowerMatchCallback(systemBus, powerCallBack);
+
+    boost::asio::post(io, [&]() {
+        createSensors(io, objectServer, systemBus, nullptr, false);
+    });
     boost::asio::steady_timer filterTimer(io);
     std::function<void(sdbusplus::message_t&)> eventHandler =
         [&](sdbusplus::message_t& message) {
@@ -1092,7 +1153,7 @@
             {
                 std::cerr << "timer error\n";
             }
-            createSensors(io, objectServer, systemBus, sensorsChanged);
+            createSensors(io, objectServer, systemBus, sensorsChanged, false);
         });
     };
 
@@ -1147,7 +1208,7 @@
                 std::cerr << "timer error\n";
                 return;
             }
-            createSensors(io, objectServer, systemBus, nullptr);
+            createSensors(io, objectServer, systemBus, nullptr, false);
         });
     };