psutils: Move functions from updater to utils

Move common, utility functions from updater.*pp to utils.*pp.  This will
enable those functions to be used by other command line options in the
psutils tool.

Modify --get-version and --get-model to use the new utility functions.

Also update --get-version to provide a single getVersion() function that
handles the existence of the psu.json file as a low-level implementation
detail.

Tested:
* Verified all automated tests run successfully
* Verified --get-version still works
  * With psu.json file
  * Without psu.json file
* Verified --get-model still works
  * With psu.json file
  * Without psu.json file
* Verified --update still gets correct device path, device name, and I2C
  bus/address from functions that moved to utils.*pp
* The complete test plan is available at
  https://gist.github.com/smccarney/c049e24655d32e22cab9d521d145774a

Change-Id: I51ceca10957dc9a924d0d7516dc29632a6ed82d3
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/tools/power-utils/utils.cpp b/tools/power-utils/utils.cpp
index 0afc4a8..e70004d 100644
--- a/tools/power-utils/utils.cpp
+++ b/tools/power-utils/utils.cpp
@@ -13,6 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include "config.h"
+
 #include "utils.hpp"
 
 #include "utility.hpp"
@@ -20,14 +22,21 @@
 #include <phosphor-logging/log.hpp>
 #include <xyz/openbmc_project/Common/Device/error.hpp>
 
+#include <cassert>
 #include <exception>
+#include <filesystem>
+#include <format>
+#include <iomanip>
+#include <ios>
 #include <iostream>
 #include <regex>
+#include <sstream>
 #include <stdexcept>
 
 using namespace phosphor::logging;
 using namespace phosphor::power::util;
 using namespace sdbusplus::xyz::openbmc_project::Common::Device::Error;
+namespace fs = std::filesystem;
 
 namespace utils
 {
@@ -165,4 +174,73 @@
     return false;
 }
 
+std::string getDeviceName(std::string devPath)
+{
+    if (devPath.back() == '/')
+    {
+        devPath.pop_back();
+    }
+    return fs::path(devPath).stem().string();
+}
+
+std::string getDevicePath(sdbusplus::bus_t& bus,
+                          const std::string& psuInventoryPath)
+{
+    try
+    {
+        if (usePsuJsonFile())
+        {
+            auto data = loadJSONFromFile(PSU_JSON_PATH);
+            if (data == nullptr)
+            {
+                return {};
+            }
+            auto devicePath = data["psuDevices"][psuInventoryPath];
+            if (devicePath.empty())
+            {
+                log<level::WARNING>("Unable to find psu devices or path");
+            }
+            return devicePath;
+        }
+        else
+        {
+            const auto [i2cbus, i2caddr] = getPsuI2c(bus, psuInventoryPath);
+            const auto DevicePath = "/sys/bus/i2c/devices/";
+            std::ostringstream ss;
+            ss << std::hex << std::setw(4) << std::setfill('0') << i2caddr;
+            std::string addrStr = ss.str();
+            std::string busStr = std::to_string(i2cbus);
+            std::string devPath = DevicePath + busStr + "-" + addrStr;
+            return devPath;
+        }
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>(
+            std::format("Error in getDevicePath: {}", e.what()).c_str());
+        return {};
+    }
+    catch (...)
+    {
+        log<level::ERR>("Unknown error occurred in getDevicePath");
+        return {};
+    }
+}
+
+std::pair<uint8_t, uint8_t> parseDeviceName(const std::string& devName)
+{
+    // Get I2C bus and device address, e.g. 3-0068
+    // is parsed to bus 3, device address 0x68
+    auto pos = devName.find('-');
+    assert(pos != std::string::npos);
+    uint8_t busId = std::stoi(devName.substr(0, pos));
+    uint8_t devAddr = std::stoi(devName.substr(pos + 1), nullptr, 16);
+    return {busId, devAddr};
+}
+
+bool usePsuJsonFile()
+{
+    return checkFileExists(PSU_JSON_PATH);
+}
+
 } // namespace utils