pseq: Create class for finding devices

Create class for finding power sequencer devices in the system.

Tested:
* See test plan at
  https://gist.github.com/smccarney/115a90a26a04e28c226d4e86eaf84cbf

Change-Id: I024eb6ccf82ea30c08c558194c19afcba8dcf5a2
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/phosphor-power-sequencer/src/device_finder.cpp b/phosphor-power-sequencer/src/device_finder.cpp
new file mode 100644
index 0000000..65d3651
--- /dev/null
+++ b/phosphor-power-sequencer/src/device_finder.cpp
@@ -0,0 +1,92 @@
+/**
+ * Copyright © 2024 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 "device_finder.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+
+#include <exception>
+#include <format>
+#include <stdexcept>
+#include <utility>
+#include <variant>
+#include <vector>
+
+namespace phosphor::power::sequencer
+{
+
+const std::string deviceInterfacesService = "xyz.openbmc_project.EntityManager";
+
+const std::vector<std::string> deviceInterfaces{
+    "xyz.openbmc_project.Configuration.UCD90160",
+    "xyz.openbmc_project.Configuration.UCD90320"};
+
+const std::string typeProperty = "Type";
+const std::string nameProperty = "Name";
+const std::string busProperty = "Bus";
+const std::string addressProperty = "Address";
+
+DeviceFinder::DeviceFinder(sdbusplus::bus_t& bus, Callback callback) :
+    callback{std::move(callback)},
+    interfacesFinder{
+        bus, deviceInterfacesService, deviceInterfaces,
+        std::bind_front(&DeviceFinder::interfaceFoundCallback, this)}
+{}
+
+void DeviceFinder::interfaceFoundCallback(
+    [[maybe_unused]] const std::string& path, const std::string& interface,
+    const DbusPropertyMap& properties)
+{
+    try
+    {
+        DeviceProperties device{};
+
+        auto value = getPropertyValue(properties, typeProperty);
+        device.type = std::get<std::string>(value);
+
+        value = getPropertyValue(properties, nameProperty);
+        device.name = std::get<std::string>(value);
+
+        value = getPropertyValue(properties, busProperty);
+        device.bus = static_cast<uint8_t>(std::get<uint64_t>(value));
+
+        value = getPropertyValue(properties, addressProperty);
+        device.address = static_cast<uint16_t>(std::get<uint64_t>(value));
+
+        callback(device);
+    }
+    catch (const std::exception& e)
+    {
+        lg2::error(
+            "Unable to obtain properties of interface {INTERFACE}: {ERROR}",
+            "INTERFACE", interface, "ERROR", e);
+    }
+}
+
+const DbusVariant&
+    DeviceFinder::getPropertyValue(const DbusPropertyMap& properties,
+                                   const std::string& propertyName)
+{
+    auto it = properties.find(propertyName);
+    if (it == properties.end())
+    {
+        throw std::runtime_error{
+            std::format("{} property not found", propertyName)};
+    }
+    return it->second;
+}
+
+} // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/device_finder.hpp b/phosphor-power-sequencer/src/device_finder.hpp
new file mode 100644
index 0000000..f09d164
--- /dev/null
+++ b/phosphor-power-sequencer/src/device_finder.hpp
@@ -0,0 +1,119 @@
+/**
+ * Copyright © 2024 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 "dbus_interfaces_finder.hpp"
+#include "utility.hpp"
+
+#include <sdbusplus/bus.hpp>
+
+#include <cstdint>
+#include <functional>
+#include <string>
+
+namespace phosphor::power::sequencer
+{
+
+using DbusVariant = phosphor::power::util::DbusVariant;
+using DbusPropertyMap = phosphor::power::util::DbusPropertyMap;
+using DBusInterfacesFinder = phosphor::power::util::DBusInterfacesFinder;
+
+/**
+ * Power sequencer device properties.
+ */
+struct DeviceProperties
+{
+    std::string type;
+    std::string name;
+    uint8_t bus;
+    uint16_t address;
+};
+
+/**
+ * @class DeviceFinder
+ *
+ * Class that finds power sequencer devices in the system.
+ *
+ * When a device is found, the callback function specified in the constructor is
+ * called.  This function will be called multiple times if multiple devices are
+ * found.
+ */
+class DeviceFinder
+{
+  public:
+    // Specify which compiler-generated methods we want
+    DeviceFinder() = delete;
+    DeviceFinder(const DeviceFinder&) = delete;
+    DeviceFinder(DeviceFinder&&) = delete;
+    DeviceFinder& operator=(const DeviceFinder&) = delete;
+    DeviceFinder& operator=(DeviceFinder&&) = delete;
+    ~DeviceFinder() = default;
+
+    /**
+     * Callback function that is called when a power sequencer device is found.
+     *
+     * @param device Device that was found
+     */
+    using Callback = std::function<void(const DeviceProperties& device)>;
+
+    /**
+     * Constructor.
+     *
+     * @param bus D-Bus bus object
+     * @param callback Callback function that is called each time a power
+     *                 sequencer device is found
+     */
+    explicit DeviceFinder(sdbusplus::bus_t& bus, Callback callback);
+
+    /**
+     * Callback function that is called when a D-Bus interface is found that
+     * contains power sequencer device properties.
+     *
+     * @param path D-Bus object path that implements the interface
+     * @param interface D-Bus interface that was found
+     * @param properties Properties of the D-Bus interface
+     */
+    void interfaceFoundCallback(const std::string& path,
+                                const std::string& interface,
+                                const DbusPropertyMap& properties);
+
+  private:
+    /**
+     * Returns the value of the D-Bus property with the specified name.
+     *
+     * Throws an exception if the property was not found.
+     *
+     * @param properties D-Bus interface properties
+     * @param propertyName D-Bus property name
+     * @return Property value
+     */
+    const DbusVariant& getPropertyValue(const DbusPropertyMap& properties,
+                                        const std::string& propertyName);
+
+    /**
+     * Callback function that is called each time a power sequencer device is
+     * found.
+     */
+    Callback callback;
+
+    /**
+     * Class used to find D-Bus interfaces that contain power sequencer device
+     * properties.
+     */
+    DBusInterfacesFinder interfacesFinder;
+};
+
+} // namespace phosphor::power::sequencer
diff --git a/phosphor-power-sequencer/src/meson.build b/phosphor-power-sequencer/src/meson.build
index eb641b9..f853c7a 100644
--- a/phosphor-power-sequencer/src/meson.build
+++ b/phosphor-power-sequencer/src/meson.build
@@ -6,6 +6,7 @@
 phosphor_power_sequencer_library = static_library(
     'phosphor-power-sequencer',
     'config_file_parser.cpp',
+    'device_finder.cpp',
     'pmbus_driver_device.cpp',
     'rail.cpp',
     'services.cpp',
@@ -16,7 +17,9 @@
     implicit_include_directories: false,
     dependencies: [
         nlohmann_json_dep,
-        phosphor_logging
+        phosphor_dbus_interfaces,
+        phosphor_logging,
+        sdbusplus,
     ],
     include_directories: [
         phosphor_power_sequencer_include_directories