enable health_monitor new implementation

Add health_monitor interface and implementation. Enable the
health_monitor from existing healthMonitor flow.
This change is in relation to following design and D-Bus interface
update -
https://gerrit.openbmc.org/c/openbmc/docs/+/64917
https://gerrit.openbmc.org/c/openbmc/phosphor-dbus-interfaces/+/64914

Tested:
```
busctl tree xyz.openbmc_project.HealthMon
`- /xyz
  `- /xyz/openbmc_project
    `- /xyz/openbmc_project/metric
      `- /xyz/openbmc_project/metric/bmc
        |- /xyz/openbmc_project/metric/bmc/cpu
        | |- /xyz/openbmc_project/metric/bmc/cpu/kernel
        | |- /xyz/openbmc_project/metric/bmc/cpu/total
        | `- /xyz/openbmc_project/metric/bmc/cpu/user
        |- /xyz/openbmc_project/metric/bmc/memory
        | |- /xyz/openbmc_project/metric/bmc/memory/available
        | |- /xyz/openbmc_project/metric/bmc/memory/buffered_and_cached
        | |- /xyz/openbmc_project/metric/bmc/memory/free
        | |- /xyz/openbmc_project/metric/bmc/memory/shared
        | `- /xyz/openbmc_project/metric/bmc/memory/total
        `- /xyz/openbmc_project/metric/bmc/storage
          |- /xyz/openbmc_project/metric/bmc/storage/rw
          `- /xyz/openbmc_project/metric/bmc/storage/tmp

curl -k https://localhost:8443/redfish/v1/Managers/bmc/ManagerDiagnosticData
--user "root:0penBmc"
{
  "@odata.id": "/redfish/v1/Managers/bmc/ManagerDiagnosticData",
  "@odata.type": "#ManagerDiagnosticData.v1_2_0.ManagerDiagnosticData",
  "FreeStorageSpaceKiB": 3,
  "Id": "ManagerDiagnosticData",
  "MemoryStatistics": {
    "AvailableBytes": 354029533,
    "BuffersAndCacheBytes": 79077333,
    "FreeBytes": 291730033,
    "SharedBytes": 11876000,
    "TotalBytes": 425516000
  },
  "Name": "Manager Diagnostic Data",
  "ProcessorStatistics": {
    "KernelPercent": 16.1534,
    "UserPercent": 10.6638
  },
  "ServiceRootUptimeSeconds": 834.753
}

```

Change-Id: I1e9fac226b438f0556cb20321f1f2b6d51af7dbc
Signed-off-by: Jagpal Singh Gill <paligill@gmail.com>
diff --git a/healthMonitor.cpp b/healthMonitor.cpp
deleted file mode 100644
index 2b4e0c2..0000000
--- a/healthMonitor.cpp
+++ /dev/null
@@ -1,824 +0,0 @@
-#include "config.h"
-
-#include "healthMonitor.hpp"
-
-#include <unistd.h>
-
-#include <boost/asio/steady_timer.hpp>
-#include <sdbusplus/asio/connection.hpp>
-#include <sdbusplus/asio/object_server.hpp>
-#include <sdbusplus/asio/sd_event.hpp>
-#include <sdbusplus/bus/match.hpp>
-#include <sdbusplus/server/manager.hpp>
-#include <sdeventplus/event.hpp>
-
-#include <fstream>
-#include <iostream>
-#include <memory>
-#include <numeric>
-#include <sstream>
-
-extern "C"
-{
-#include <sys/statvfs.h>
-#include <sys/sysinfo.h>
-}
-
-PHOSPHOR_LOG2_USING;
-
-static constexpr bool DEBUG = false;
-static constexpr uint8_t defaultHighThreshold = 100;
-
-// Limit sensor recreation interval to 10s
-bool needUpdate;
-static constexpr int TIMER_INTERVAL = 10;
-std::shared_ptr<boost::asio::steady_timer> sensorRecreateTimer;
-std::shared_ptr<phosphor::health::HealthMon> healthMon;
-
-namespace phosphor
-{
-namespace health
-{
-
-// Example values for iface:
-// BMC_CONFIGURATION
-// BMC_INVENTORY_ITEM
-std::vector<std::string> findPathsWithType(sdbusplus::bus_t& bus,
-                                           const std::string& iface)
-{
-    PHOSPHOR_LOG2_USING;
-    std::vector<std::string> ret;
-
-    // Find all BMCs (DBus objects implementing the
-    // Inventory.Item.Bmc interface that may be created by
-    // configuring the Inventory Manager)
-    sdbusplus::message_t msg = bus.new_method_call(
-        "xyz.openbmc_project.ObjectMapper",
-        "/xyz/openbmc_project/object_mapper",
-        "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths");
-
-    // "/": No limit for paths for all the paths that may be touched
-    // in this daemon
-
-    // 0: Limit the depth to 0 to match both objects created by
-    // EntityManager and by InventoryManager
-
-    // {iface}: The endpoint of the Association Definition must have
-    // the Inventory.Item.Bmc interface
-    msg.append("/", 0, std::vector<std::string>{iface});
-
-    try
-    {
-        bus.call(msg, 0).read(ret);
-
-        if (!ret.empty())
-        {
-            debug("{IFACE} found", "IFACE", iface);
-        }
-        else
-        {
-            debug("{IFACE} not found", "IFACE", iface);
-        }
-    }
-    catch (std::exception& e)
-    {
-        error("Exception occurred while calling {PATH}: {ERROR}", "PATH",
-              InventoryPath, "ERROR", e);
-    }
-    return ret;
-}
-
-enum CPUStatesTime
-{
-    USER_IDX = 0,
-    NICE_IDX,
-    SYSTEM_IDX,
-    IDLE_IDX,
-    IOWAIT_IDX,
-    IRQ_IDX,
-    SOFTIRQ_IDX,
-    STEAL_IDX,
-    GUEST_USER_IDX,
-    GUEST_NICE_IDX,
-    NUM_CPU_STATES_TIME
-};
-
-// # cat /proc/stat|grep 'cpu '
-// cpu  5750423 14827 1572788 9259794 1317 0 28879 0 0 0
-static_assert(NUM_CPU_STATES_TIME == 10);
-
-enum CPUUtilizationType
-{
-    USER = 0,
-    KERNEL,
-    TOTAL
-};
-
-double readCPUUtilization(enum CPUUtilizationType type)
-{
-    auto proc_stat = "/proc/stat";
-    std::ifstream fileStat(proc_stat);
-    if (!fileStat.is_open())
-    {
-        error("cpu file not available: {PATH}", "PATH", proc_stat);
-        return -1;
-    }
-
-    std::string firstLine, labelName;
-    std::size_t timeData[NUM_CPU_STATES_TIME];
-
-    std::getline(fileStat, firstLine);
-    std::stringstream ss(firstLine);
-    ss >> labelName;
-
-    if (DEBUG)
-        debug("CPU stats first Line is: {LINE}", "LINE", firstLine);
-
-    if (labelName.compare("cpu"))
-    {
-        error("CPU data not available");
-        return -1;
-    }
-
-    int i;
-    for (i = 0; i < NUM_CPU_STATES_TIME; i++)
-    {
-        if (!(ss >> timeData[i]))
-            break;
-    }
-
-    if (i != NUM_CPU_STATES_TIME)
-    {
-        error("CPU data not correct");
-        return -1;
-    }
-
-    static std::unordered_map<enum CPUUtilizationType, uint64_t> preActiveTime,
-        preTotalTime;
-
-    // These are actually Jiffies. On the BMC, 1 jiffy usually corresponds to
-    // 0.01 second.
-    uint64_t activeTime = 0, activeTimeDiff = 0, totalTime = 0,
-             totalTimeDiff = 0;
-    double activePercValue = 0;
-
-    if (type == TOTAL)
-    {
-        activeTime = timeData[USER_IDX] + timeData[NICE_IDX] +
-                     timeData[SYSTEM_IDX] + timeData[IRQ_IDX] +
-                     timeData[SOFTIRQ_IDX] + timeData[STEAL_IDX] +
-                     timeData[GUEST_USER_IDX] + timeData[GUEST_NICE_IDX];
-    }
-    else if (type == KERNEL)
-    {
-        activeTime = timeData[SYSTEM_IDX];
-    }
-    else if (type == USER)
-    {
-        activeTime = timeData[USER_IDX];
-    }
-
-    totalTime = std::accumulate(std::begin(timeData), std::end(timeData), 0);
-
-    activeTimeDiff = activeTime - preActiveTime[type];
-    totalTimeDiff = totalTime - preTotalTime[type];
-
-    /* Store current idle and active time for next calculation */
-    preActiveTime[type] = activeTime;
-    preTotalTime[type] = totalTime;
-
-    activePercValue = (100.0 * activeTimeDiff) / totalTimeDiff;
-
-    if (DEBUG)
-        debug("CPU Utilization is {VALUE}", "VALUE", activePercValue);
-
-    return activePercValue;
-}
-
-auto readCPUUtilizationTotal([[maybe_unused]] const std::string& path)
-{
-    return readCPUUtilization(CPUUtilizationType::TOTAL);
-}
-
-auto readCPUUtilizationKernel([[maybe_unused]] const std::string& path)
-{
-    return readCPUUtilization(CPUUtilizationType::KERNEL);
-}
-
-auto readCPUUtilizationUser([[maybe_unused]] const std::string& path)
-{
-    return readCPUUtilization(CPUUtilizationType::USER);
-}
-
-double readMemoryUtilization([[maybe_unused]] const std::string& path)
-{
-    /* Unused var: path */
-    std::ignore = path;
-    std::ifstream meminfo("/proc/meminfo");
-    std::string line;
-    double memTotal = -1;
-    double memAvail = -1;
-
-    while (std::getline(meminfo, line))
-    {
-        std::string name;
-        double value;
-        std::istringstream iss(line);
-
-        if (!(iss >> name >> value))
-        {
-            continue;
-        }
-
-        if (name.starts_with("MemTotal"))
-        {
-            memTotal = value;
-        }
-        else if (name.starts_with("MemAvailable"))
-        {
-            memAvail = value;
-        }
-    }
-
-    if (memTotal <= 0 || memAvail <= 0)
-    {
-        return std::numeric_limits<double>::quiet_NaN();
-    }
-
-    if (DEBUG)
-    {
-        debug("MemTotal: {VALUE}", "VALUE", memTotal);
-        debug("MemAvailable: {VALUE}", "VALUE", memAvail);
-    }
-
-    return (memTotal - memAvail) / memTotal * 100;
-}
-
-double readStorageUtilization([[maybe_unused]] const std::string& path)
-{
-    struct statvfs buffer
-    {};
-    int ret = statvfs(path.c_str(), &buffer);
-    double total = 0;
-    double available = 0;
-    double used = 0;
-    double usedPercentage = 0;
-
-    if (ret != 0)
-    {
-        auto e = errno;
-        error("Error from statvfs: {ERROR}; {PATH}", "ERROR", strerror(e),
-              "PATH", path);
-        return 0;
-    }
-
-    total = buffer.f_blocks * (buffer.f_frsize / 1024);
-    available = buffer.f_bfree * (buffer.f_frsize / 1024);
-    used = total - available;
-    usedPercentage = (used / total) * 100;
-
-    if (DEBUG)
-    {
-        debug("Storage Total: {VALUE}", "VALUE", total);
-        debug("Available: {VALUE}", "VALUE", available);
-        debug("Used: {VALUE}", "VALUE", used);
-        debug("Storage Utilization: {VALUE}", "VALUE", usedPercentage);
-    }
-
-    return usedPercentage;
-}
-
-double readInodeUtilization([[maybe_unused]] const std::string& path)
-{
-    struct statvfs buffer
-    {};
-    int ret = statvfs(path.c_str(), &buffer);
-    double totalInodes = 0;
-    double availableInodes = 0;
-    double used = 0;
-    double usedPercentage = 0;
-
-    if (ret != 0)
-    {
-        auto e = errno;
-        error("Error from statvfs on {PATH}: {ERROR}", "PATH", path, "ERROR",
-              strerror(e));
-        return 0;
-    }
-
-    totalInodes = buffer.f_files;
-    availableInodes = buffer.f_ffree;
-    used = totalInodes - availableInodes;
-    usedPercentage = (used / totalInodes) * 100;
-
-    if (DEBUG)
-    {
-        debug("Total Inodes: {VALUE}", "VALUE", totalInodes);
-        debug("Available Inodes: {VALUE}", "VALUE", availableInodes);
-        debug("Used: {VALUE}", "VALUE", used);
-        debug("Inodes utilization is: {VALUE}", "VALUE", usedPercentage);
-    }
-
-    return usedPercentage;
-}
-
-constexpr auto storage = "Storage";
-constexpr auto inode = "Inode";
-
-/** Map of read function for each health sensors supported
- *
- * The following health sensors are read in the ManagerDiagnosticData
- * Redfish resource:
- *  - CPU_Kernel populates ProcessorStatistics.KernelPercent
- *  - CPU_User populates ProcessorStatistics.UserPercent
- */
-const std::map<std::string, std::function<double(const std::string& path)>>
-    readSensors = {{"CPU", readCPUUtilizationTotal},
-                   {"CPU_Kernel", readCPUUtilizationKernel},
-                   {"CPU_User", readCPUUtilizationUser},
-                   {"Memory", readMemoryUtilization},
-                   {storage, readStorageUtilization},
-                   {inode, readInodeUtilization}};
-
-void HealthSensor::setSensorThreshold(double criticalHigh, double warningHigh)
-{
-    CriticalInterface::criticalHigh(criticalHigh);
-    CriticalInterface::criticalLow(std::numeric_limits<double>::quiet_NaN());
-
-    WarningInterface::warningHigh(warningHigh);
-    WarningInterface::warningLow(std::numeric_limits<double>::quiet_NaN());
-}
-
-void HealthSensor::setSensorValueToDbus(const double value)
-{
-    ValueIface::value(value);
-}
-
-void HealthSensor::initHealthSensor(
-    const std::vector<std::string>& bmcInventoryPaths)
-{
-    info("{SENSOR} Health Sensor initialized", "SENSOR", sensorConfig.name);
-
-    /* Look for sensor read functions and Read Sensor values */
-    auto it = readSensors.find(sensorConfig.name);
-
-    if (sensorConfig.name.rfind(storage, 0) == 0)
-    {
-        it = readSensors.find(storage);
-    }
-    else if (sensorConfig.name.rfind(inode, 0) == 0)
-    {
-        it = readSensors.find(inode);
-    }
-    else if (it == readSensors.end())
-    {
-        error("Sensor read function not available");
-        return;
-    }
-
-    double value = it->second(sensorConfig.path);
-
-    if (value < 0)
-    {
-        error("Reading Sensor Utilization failed: {SENSOR}", "SENSOR",
-              sensorConfig.name);
-        return;
-    }
-
-    /* Initialize unit value (Percent) for utilization sensor */
-    ValueIface::unit(ValueIface::Unit::Percent);
-
-    ValueIface::maxValue(100);
-    ValueIface::minValue(0);
-    ValueIface::value(std::numeric_limits<double>::quiet_NaN());
-
-    // Associate the sensor to chassis
-    // This connects the DBus object to a Chassis.
-
-    std::vector<AssociationTuple> associationTuples;
-    for (const auto& chassisId : bmcInventoryPaths)
-    {
-        // This utilization sensor "is monitoring" the BMC with path chassisId.
-        // The chassisId is "monitored_by" this utilization sensor.
-        associationTuples.push_back({"monitors", "monitored_by", chassisId});
-    }
-    AssociationDefinitionInterface::associations(associationTuples);
-
-    /* Start the timer for reading sensor data at regular interval */
-    readTimer.restart(std::chrono::milliseconds(sensorConfig.freq * 1000));
-}
-
-void HealthSensor::checkSensorThreshold(const double value)
-{
-    if (std::isfinite(sensorConfig.criticalHigh) &&
-        (value > sensorConfig.criticalHigh))
-    {
-        if (!CriticalInterface::criticalAlarmHigh())
-        {
-            CriticalInterface::criticalAlarmHigh(true);
-            if (sensorConfig.criticalLog)
-            {
-                error(
-                    "ASSERT: sensor {SENSOR} is above the upper threshold critical high",
-                    "SENSOR", sensorConfig.name);
-                startUnit(sensorConfig.criticalTgt);
-            }
-        }
-        return;
-    }
-
-    if (CriticalInterface::criticalAlarmHigh())
-    {
-        CriticalInterface::criticalAlarmHigh(false);
-        if (sensorConfig.criticalLog)
-            info(
-                "DEASSERT: sensor {SENSOR} is under the upper threshold critical high",
-                "SENSOR", sensorConfig.name);
-    }
-
-    if (std::isfinite(sensorConfig.warningHigh) &&
-        (value > sensorConfig.warningHigh))
-    {
-        if (!WarningInterface::warningAlarmHigh())
-        {
-            WarningInterface::warningAlarmHigh(true);
-            if (sensorConfig.warningLog)
-            {
-                error(
-                    "ASSERT: sensor {SENSOR} is above the upper threshold warning high",
-                    "SENSOR", sensorConfig.name);
-                startUnit(sensorConfig.warningTgt);
-            }
-        }
-        return;
-    }
-
-    if (WarningInterface::warningAlarmHigh())
-    {
-        WarningInterface::warningAlarmHigh(false);
-        if (sensorConfig.warningLog)
-            info(
-                "DEASSERT: sensor {SENSOR} is under the upper threshold warning high",
-                "SENSOR", sensorConfig.name);
-    }
-}
-
-void HealthSensor::readHealthSensor()
-{
-    /* Read current sensor value */
-    double value;
-
-    if (sensorConfig.name.rfind(storage, 0) == 0)
-    {
-        value = readSensors.find(storage)->second(sensorConfig.path);
-    }
-    else if (sensorConfig.name.rfind(inode, 0) == 0)
-    {
-        value = readSensors.find(inode)->second(sensorConfig.path);
-    }
-    else
-    {
-        value = readSensors.find(sensorConfig.name)->second(sensorConfig.path);
-    }
-
-    if (value < 0)
-    {
-        error("Reading Sensor Utilization failed: {SENSOR}", "SENSOR",
-              sensorConfig.name);
-        return;
-    }
-
-    /* Remove first item from the queue */
-    if (valQueue.size() >= sensorConfig.windowSize)
-    {
-        valQueue.pop_front();
-    }
-    /* Add new item at the back */
-    valQueue.push_back(value);
-    /* Wait until the queue is filled with enough reference*/
-    if (valQueue.size() < sensorConfig.windowSize)
-    {
-        return;
-    }
-
-    /* Calculate average values for the given window size */
-    double avgValue = 0;
-    avgValue = accumulate(valQueue.begin(), valQueue.end(), avgValue);
-    avgValue = avgValue / sensorConfig.windowSize;
-
-    /* Set this new value to dbus */
-    setSensorValueToDbus(avgValue);
-
-    /* Check the sensor threshold  and log required message */
-    checkSensorThreshold(avgValue);
-}
-
-void HealthSensor::startUnit(const std::string& sysdUnit)
-{
-    if (sysdUnit.empty())
-    {
-        return;
-    }
-
-    sdbusplus::message_t msg = bus.new_method_call(
-        "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
-        "org.freedesktop.systemd1.Manager", "StartUnit");
-    msg.append(sysdUnit, "replace");
-    bus.call_noreply(msg);
-}
-
-void HealthMon::recreateSensors()
-{
-    PHOSPHOR_LOG2_USING;
-    healthSensors.clear();
-
-    // Find BMC inventory paths and create health sensors
-    std::vector<std::string> bmcInventoryPaths =
-        findPathsWithType(bus, BMC_INVENTORY_ITEM);
-    createHealthSensors(bmcInventoryPaths);
-}
-
-void printConfig(HealthConfig& cfg)
-{
-    debug("Name: {VALUE}", "VALUE", cfg.name);
-    debug("Freq: {VALUE}", "VALUE", cfg.freq);
-    debug("Window Size: {VALUE}", "VALUE", cfg.windowSize);
-    debug("Critical value: {VALUE}", "VALUE", cfg.criticalHigh);
-    debug("warning value: {VALUE}", "VALUE", cfg.warningHigh);
-    debug("Critical log: {VALUE}", "VALUE", cfg.criticalLog);
-    debug("Warning log: {VALUE}", "VALUE", cfg.warningLog);
-    debug("Critical Target: {VALUE}", "VALUE", cfg.criticalTgt);
-    debug("Warning Target: {VALUE}", "VALUE", cfg.warningTgt);
-    debug("Path: {VALUE}", "VALUE", cfg.path);
-}
-
-/* Create dbus utilization sensor object for each configured sensors */
-void HealthMon::createHealthSensors(
-    const std::vector<std::string>& bmcInventoryPaths)
-{
-    for (auto& cfg : sensorConfigs)
-    {
-        std::string objPath = std::string(HEALTH_SENSOR_PATH) + cfg.name;
-        auto healthSensor = std::make_shared<HealthSensor>(
-            bus, objPath.c_str(), cfg, bmcInventoryPaths);
-        healthSensors.emplace(cfg.name, healthSensor);
-
-        info("{SENSOR} Health Sensor created", "SENSOR", cfg.name);
-
-        /* Set configured values of crtical and warning high to dbus */
-        healthSensor->setSensorThreshold(cfg.criticalHigh, cfg.warningHigh);
-    }
-}
-
-/** @brief Parsing Health config JSON file  */
-Json HealthMon::parseConfigFile(std::string configFile)
-{
-    std::ifstream jsonFile(configFile);
-    if (!jsonFile.is_open())
-    {
-        error("config JSON file not found: {PATH}", "PATH", configFile);
-    }
-
-    auto data = Json::parse(jsonFile, nullptr, false);
-    if (data.is_discarded())
-    {
-        error("config readings JSON parser failure: {PATH}", "PATH",
-              configFile);
-    }
-
-    return data;
-}
-
-void HealthMon::getConfigData(Json& data, HealthConfig& cfg)
-{
-    static const Json empty{};
-
-    /* Default frerquency of sensor polling is 1 second */
-    cfg.freq = data.value("Frequency", 1);
-
-    /* Default window size sensor queue is 1 */
-    cfg.windowSize = data.value("Window_size", 1);
-
-    auto threshold = data.value("Threshold", empty);
-    if (!threshold.empty())
-    {
-        auto criticalData = threshold.value("Critical", empty);
-        if (!criticalData.empty())
-        {
-            cfg.criticalHigh = criticalData.value("Value",
-                                                  defaultHighThreshold);
-            cfg.criticalLog = criticalData.value("Log", true);
-            cfg.criticalTgt = criticalData.value("Target", "");
-        }
-        auto warningData = threshold.value("Warning", empty);
-        if (!warningData.empty())
-        {
-            cfg.warningHigh = warningData.value("Value", defaultHighThreshold);
-            cfg.warningLog = warningData.value("Log", false);
-            cfg.warningTgt = warningData.value("Target", "");
-        }
-    }
-    cfg.path = data.value("Path", "");
-}
-
-std::vector<HealthConfig> HealthMon::getHealthConfig()
-{
-    std::vector<HealthConfig> cfgs;
-    auto data = parseConfigFile(HEALTH_CONFIG_FILE);
-
-    // print values
-    if (DEBUG)
-        debug("Config json data: {VALUE}", "VALUE", data.dump(2));
-
-    /* Get data items from config json data*/
-    for (auto& j : data.items())
-    {
-        auto key = j.key();
-        /* key need match default value in map readSensors or match the key
-         * start with "Storage" or "Inode" */
-        bool isStorageOrInode = (key.rfind(storage, 0) == 0 ||
-                                 key.rfind(inode, 0) == 0);
-        if (readSensors.find(key) != readSensors.end() || isStorageOrInode)
-        {
-            HealthConfig cfg = HealthConfig();
-            cfg.name = j.key();
-            getConfigData(j.value(), cfg);
-            if (isStorageOrInode)
-            {
-                struct statvfs buffer
-                {};
-                int ret = statvfs(cfg.path.c_str(), &buffer);
-                if (ret != 0)
-                {
-                    auto e = errno;
-                    error("Error from statvfs: {ERROR}; ({NAME}, {PATH})",
-                          "ERROR", strerror(e), "NAME", cfg.name, "PATH",
-                          cfg.path);
-                    continue;
-                }
-            }
-            cfgs.push_back(cfg);
-            if (DEBUG)
-                printConfig(cfg);
-        }
-        else
-        {
-            error("{SENSOR} Health Sensor not supported", "SENSOR", key);
-        }
-    }
-    return cfgs;
-}
-
-// Two caveats here.
-// 1. The BMC Inventory will only show up by the nearest ObjectMapper polling
-// interval.
-// 2. InterfacesAdded events will are not emitted like they are with E-M.
-void HealthMon::createBmcInventoryIfNotCreated()
-{
-    if (bmcInventory == nullptr)
-    {
-        info("createBmcInventory");
-        bmcInventory = std::make_shared<phosphor::health::BmcInventory>(
-            bus, "/xyz/openbmc_project/inventory/bmc");
-    }
-}
-
-bool HealthMon::bmcInventoryCreated()
-{
-    return bmcInventory != nullptr;
-}
-
-} // namespace health
-} // namespace phosphor
-
-void sensorRecreateTimerCallback(
-    std::shared_ptr<boost::asio::steady_timer> timer, sdbusplus::bus_t& bus)
-{
-    timer->expires_after(std::chrono::seconds(TIMER_INTERVAL));
-    timer->async_wait([timer, &bus](const boost::system::error_code& ec) {
-        if (ec == boost::asio::error::operation_aborted)
-        {
-            info("sensorRecreateTimer aborted");
-            return;
-        }
-
-        // When Entity-manager is already running
-        if (!needUpdate)
-        {
-            if ((!healthMon->bmcInventoryCreated()) &&
-                (!phosphor::health::findPathsWithType(bus, BMC_CONFIGURATION)
-                      .empty()))
-            {
-                healthMon->createBmcInventoryIfNotCreated();
-                needUpdate = true;
-            }
-        }
-        else
-        {
-            // If this daemon maintains its own DBus object, we must make sure
-            // the object is registered to ObjectMapper
-            if (phosphor::health::findPathsWithType(bus, BMC_INVENTORY_ITEM)
-                    .empty())
-            {
-                info(
-                    "BMC inventory item not registered to Object Mapper yet, waiting for next iteration");
-            }
-            else
-            {
-                info(
-                    "BMC inventory item registered to Object Mapper, creating sensors now");
-                healthMon->recreateSensors();
-                needUpdate = false;
-            }
-        }
-        sensorRecreateTimerCallback(timer, bus);
-    });
-}
-
-/**
- * @brief Main
- */
-int main()
-{
-    // The io_context is needed for the timer
-    boost::asio::io_context io;
-
-    // DBus connection
-    auto conn = std::make_shared<sdbusplus::asio::connection>(io);
-
-    conn->request_name(HEALTH_BUS_NAME);
-
-    // Get a default event loop
-    auto event = sdeventplus::Event::get_default();
-
-    // Create an health monitor object
-    healthMon = std::make_shared<phosphor::health::HealthMon>(*conn);
-
-    // Add object manager through object_server
-    sdbusplus::asio::object_server objectServer(conn);
-
-    sdbusplus::asio::sd_event_wrapper sdEvents(io);
-
-    sensorRecreateTimer = std::make_shared<boost::asio::steady_timer>(io);
-
-    // If the SystemInventory does not exist: wait for the InterfaceAdded signal
-    auto interfacesAddedSignalHandler =
-        std::make_unique<sdbusplus::bus::match_t>(
-            static_cast<sdbusplus::bus_t&>(*conn),
-            sdbusplus::bus::match::rules::interfacesAdded(),
-            [conn](sdbusplus::message_t& msg) {
-        using Association = std::tuple<std::string, std::string, std::string>;
-        using InterfacesAdded = std::vector<std::pair<
-            std::string,
-            std::vector<std::pair<std::string,
-                                  std::variant<std::vector<Association>>>>>>;
-
-        sdbusplus::message::object_path o;
-        InterfacesAdded interfacesAdded;
-
-        try
-        {
-            msg.read(o);
-            msg.read(interfacesAdded);
-        }
-        catch (const std::exception& e)
-        {
-            error(
-                "Exception occurred while processing interfacesAdded: {ERROR}",
-                "ERROR", e);
-            return;
-        }
-
-        // Ignore any signal coming from health-monitor itself.
-        if (msg.get_sender() == conn->get_unique_name())
-        {
-            return;
-        }
-
-        // Check if the BMC Inventory is in the interfaces created.
-        bool hasBmcConfiguration = false;
-        for (const auto& x : interfacesAdded)
-        {
-            if (x.first == BMC_CONFIGURATION)
-            {
-                hasBmcConfiguration = true;
-            }
-        }
-
-        if (hasBmcConfiguration)
-        {
-            info(
-                "BMC configuration detected, will create a corresponding Inventory item");
-            healthMon->createBmcInventoryIfNotCreated();
-            needUpdate = true;
-        }
-    });
-
-    // Start the timer
-    boost::asio::post(io, [conn]() {
-        sensorRecreateTimerCallback(sensorRecreateTimer, *conn);
-    });
-    io.run();
-
-    return 0;
-}
diff --git a/healthMonitor.hpp b/healthMonitor.hpp
deleted file mode 100644
index c6e77dd..0000000
--- a/healthMonitor.hpp
+++ /dev/null
@@ -1,202 +0,0 @@
-#include <nlohmann/json.hpp>
-#include <phosphor-logging/lg2.hpp>
-#include <sdbusplus/bus.hpp>
-#include <sdbusplus/message.hpp>
-#include <sdbusplus/server/manager.hpp>
-#include <sdeventplus/clock.hpp>
-#include <sdeventplus/event.hpp>
-#include <sdeventplus/utility/timer.hpp>
-#include <xyz/openbmc_project/Association/Definitions/server.hpp>
-#include <xyz/openbmc_project/Inventory/Item/Bmc/server.hpp>
-#include <xyz/openbmc_project/Sensor/Threshold/Critical/server.hpp>
-#include <xyz/openbmc_project/Sensor/Threshold/Warning/server.hpp>
-#include <xyz/openbmc_project/Sensor/Value/server.hpp>
-
-constexpr char BMC_INVENTORY_ITEM[] = "xyz.openbmc_project.Inventory.Item.Bmc";
-constexpr char BMC_CONFIGURATION[] = "xyz.openbmc_project.Configuration.Bmc";
-
-#include <deque>
-#include <limits>
-#include <map>
-#include <string>
-
-namespace phosphor
-{
-namespace health
-{
-
-const char* InventoryPath = "/xyz/openbmc_project/inventory";
-
-// Used for identifying the BMC inventory creation signal
-const char* BMCActivationPath = "/xyz/openbmc_project/inventory/bmc/activation";
-
-bool FindSystemInventoryInObjectMapper(sdbusplus::bus_t& bus)
-{
-    sdbusplus::message_t msg =
-        bus.new_method_call("xyz.openbmc_project.ObjectMapper",
-                            "/xyz/openbmc_project/object_mapper",
-                            "xyz.openbmc_project.ObjectMapper", "GetObject");
-    msg.append(InventoryPath);
-    msg.append(std::vector<std::string>{});
-
-    try
-    {
-        sdbusplus::message_t reply = bus.call(msg, 0);
-        return true;
-    }
-    catch (const std::exception& e)
-    {}
-    return false;
-}
-
-using Json = nlohmann::json;
-using ValueIface = sdbusplus::xyz::openbmc_project::Sensor::server::Value;
-
-using CriticalInterface =
-    sdbusplus::xyz::openbmc_project::Sensor::Threshold::server::Critical;
-
-using WarningInterface =
-    sdbusplus::xyz::openbmc_project::Sensor::Threshold::server::Warning;
-
-using AssociationDefinitionInterface =
-    sdbusplus::xyz::openbmc_project::Association::server::Definitions;
-
-using healthIfaces =
-    sdbusplus::server::object_t<ValueIface, CriticalInterface, WarningInterface,
-                                AssociationDefinitionInterface>;
-
-using BmcInterface = sdbusplus::server::object_t<
-    sdbusplus::xyz::openbmc_project::Inventory::Item::server::Bmc>;
-
-using AssociationTuple = std::tuple<std::string, std::string, std::string>;
-
-struct HealthConfig
-{
-    std::string name;
-    uint16_t freq;
-    uint16_t windowSize;
-    double criticalHigh = std::numeric_limits<double>::quiet_NaN();
-    double warningHigh = std::numeric_limits<double>::quiet_NaN();
-    bool criticalLog;
-    bool warningLog;
-    std::string criticalTgt;
-    std::string warningTgt;
-    std::string path;
-};
-
-class HealthSensor : public healthIfaces
-{
-  public:
-    HealthSensor() = delete;
-    HealthSensor(const HealthSensor&) = delete;
-    HealthSensor& operator=(const HealthSensor&) = delete;
-    HealthSensor(HealthSensor&&) = delete;
-    HealthSensor& operator=(HealthSensor&&) = delete;
-    virtual ~HealthSensor() = default;
-
-    /** @brief Constructs HealthSensor
-     *
-     * @param[in] bus     - Handle to system dbus
-     * @param[in] objPath - The Dbus path of health sensor
-     */
-    HealthSensor(sdbusplus::bus_t& bus, const char* objPath,
-                 HealthConfig& sensorConfig,
-                 const std::vector<std::string>& bmcIds) :
-        healthIfaces(bus, objPath),
-        bus(bus), sensorConfig(sensorConfig),
-        timerEvent(sdeventplus::Event::get_default()),
-        readTimer(timerEvent, std::bind(&HealthSensor::readHealthSensor, this))
-    {
-        initHealthSensor(bmcIds);
-    }
-
-    /** @brief list of sensor data values */
-    std::deque<double> valQueue;
-    /** @brief Initialize sensor, set default value and association */
-    void initHealthSensor(const std::vector<std::string>& bmcIds);
-    /** @brief Set sensor value utilization to health sensor D-bus  */
-    void setSensorValueToDbus(const double value);
-    /** @brief Set Sensor Threshold to D-bus at beginning */
-    void setSensorThreshold(double criticalHigh, double warningHigh);
-    /** @brief Check Sensor threshold and update alarm and log */
-    void checkSensorThreshold(const double value);
-
-  private:
-    /** @brief sdbusplus bus client connection. */
-    sdbusplus::bus_t& bus;
-    /** @brief Sensor config from config file */
-    HealthConfig& sensorConfig;
-    /** @brief the Event Loop structure */
-    sdeventplus::Event timerEvent;
-    /** @brief Sensor Read Timer */
-    sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> readTimer;
-    /** @brief Read sensor at regular intrval */
-    void readHealthSensor();
-    /** @brief Start configured threshold systemd unit */
-    void startUnit(const std::string& sysdUnit);
-};
-
-class BmcInventory : public BmcInterface
-{
-  public:
-    BmcInventory() = delete;
-    BmcInventory(sdbusplus::bus_t& bus, const char* objPath) :
-        BmcInterface(bus, objPath)
-    {}
-};
-
-class HealthMon
-{
-  public:
-    HealthMon() = delete;
-    HealthMon(const HealthMon&) = delete;
-    HealthMon& operator=(const HealthMon&) = delete;
-    HealthMon(HealthMon&&) = delete;
-    HealthMon& operator=(HealthMon&&) = delete;
-    virtual ~HealthMon() = default;
-
-    /** @brief Recreates sensor objects and their association if possible
-     */
-    void recreateSensors();
-
-    /** @brief Constructs HealthMon
-     *
-     * @param[in] bus     - Handle to system dbus
-     */
-    HealthMon(sdbusplus::bus_t& bus) :
-        bus(bus), sensorsObjectManager(bus, "/xyz/openbmc_project/sensors")
-    {
-        // Read JSON file
-        sensorConfigs = getHealthConfig();
-        recreateSensors();
-    }
-
-    /** @brief Parse Health config JSON file  */
-    Json parseConfigFile(std::string configFile);
-
-    /** @brief Read config for each health sensor component */
-    void getConfigData(Json& data, HealthConfig& cfg);
-
-    /** @brief Map of the object HealthSensor */
-    std::unordered_map<std::string, std::shared_ptr<HealthSensor>>
-        healthSensors;
-
-    /** @brief Create sensors for health monitoring */
-    void createHealthSensors(const std::vector<std::string>& bmcIds);
-
-    /** @brief Create the BMC Inventory object */
-    void createBmcInventoryIfNotCreated();
-
-    std::shared_ptr<BmcInventory> bmcInventory;
-
-    bool bmcInventoryCreated();
-
-  private:
-    sdbusplus::bus_t& bus;
-    std::vector<HealthConfig> sensorConfigs;
-    std::vector<HealthConfig> getHealthConfig();
-    sdbusplus::server::manager_t sensorsObjectManager;
-};
-
-} // namespace health
-} // namespace phosphor
diff --git a/health_monitor.cpp b/health_monitor.cpp
new file mode 100644
index 0000000..b839ae7
--- /dev/null
+++ b/health_monitor.cpp
@@ -0,0 +1,66 @@
+#include "health_monitor.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+#include <sdbusplus/async.hpp>
+
+PHOSPHOR_LOG2_USING;
+
+namespace phosphor::health::monitor
+{
+
+using namespace phosphor::health::utils;
+
+void HealthMonitor::create()
+{
+    info("Creating Health Monitor with config size {SIZE}", "SIZE",
+         configs.size());
+    constexpr auto BMCInventoryItem = "xyz.openbmc_project.Inventory.Item.Bmc";
+    auto bmcPaths = findPaths(bus, BMCInventoryItem);
+
+    for (auto& [type, collectionConfig] : configs)
+    {
+        info("Creating Health Metric Collection for {TYPE}", "TYPE",
+             std::to_underlying(type));
+        collections[type] =
+            std::make_unique<CollectionIntf::HealthMetricCollection>(
+                bus, type, collectionConfig, bmcPaths);
+    }
+}
+
+void HealthMonitor::run()
+{
+    info("Running Health Monitor");
+    for (auto& [type, collection] : collections)
+    {
+        debug("Reading Health Metric Collection for {TYPE}", "TYPE",
+              std::to_underlying(type));
+        collection->read();
+    }
+}
+
+} // namespace phosphor::health::monitor
+
+using namespace phosphor::health::monitor;
+
+int main()
+{
+    constexpr auto path = MetricIntf::ValueIntf::Value::namespace_path::value;
+    sdbusplus::async::context ctx;
+    sdbusplus::server::manager_t manager{ctx, path};
+    constexpr auto healthMonitorServiceName = "xyz.openbmc_project.HealthMon";
+
+    info("Creating health monitor");
+    HealthMonitor healthMonitor{ctx.get_bus()};
+    ctx.request_name(healthMonitorServiceName);
+
+    ctx.spawn([&]() -> sdbusplus::async::task<> {
+        while (!ctx.stop_requested())
+        {
+            healthMonitor.run();
+            co_await sdbusplus::async::sleep_for(ctx, std::chrono::seconds(5));
+        }
+    }());
+
+    ctx.run();
+    return 0;
+}
diff --git a/health_monitor.hpp b/health_monitor.hpp
new file mode 100644
index 0000000..31d16ba
--- /dev/null
+++ b/health_monitor.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "health_metric_collection.hpp"
+
+#include <unordered_map>
+
+namespace phosphor::health::monitor
+{
+namespace ConfigIntf = phosphor::health::metric::config;
+namespace MetricIntf = phosphor::health::metric;
+namespace CollectionIntf = phosphor::health::metric::collection;
+class HealthMonitor
+{
+  public:
+    HealthMonitor() = delete;
+
+    HealthMonitor(sdbusplus::bus_t& bus) :
+        bus(bus), configs(ConfigIntf::getHealthMetricConfigs())
+    {
+        create();
+    }
+
+    /** @brief Run the health monitor */
+    void run();
+
+  private:
+    using map_t = std::unordered_map<
+        MetricIntf::Type,
+        std::unique_ptr<CollectionIntf::HealthMetricCollection>>;
+    /** @brief Create a new health monitor object */
+    void create();
+    /** @brief D-Bus bus connection */
+    sdbusplus::bus_t& bus;
+    /** @brief Health metric configs */
+    ConfigIntf::HealthMetric::map_t configs;
+    map_t collections;
+};
+
+} // namespace phosphor::health::monitor
diff --git a/health_utils.cpp b/health_utils.cpp
index 163fc8c..a60db8b 100644
--- a/health_utils.cpp
+++ b/health_utils.cpp
@@ -20,4 +20,29 @@
     bus.call_noreply(msg);
 }
 
+auto findPaths(sdbusplus::bus_t& bus, const std::string& iface) -> paths_t
+{
+    sdbusplus::message_t msg = bus.new_method_call(
+        "xyz.openbmc_project.ObjectMapper",
+        "/xyz/openbmc_project/object_mapper",
+        "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths");
+    const char* inventoryPath = "/xyz/openbmc_project/inventory";
+
+    msg.append("/", 0, std::vector<std::string>{iface});
+
+    try
+    {
+        auto paths = bus.call(msg, int32_t(0)).unpack<paths_t>();
+        debug("Found {COUNT} paths for {IFACE}", "COUNT", paths.size(), "IFACE",
+              iface);
+        return paths;
+    }
+    catch (std::exception& e)
+    {
+        error("Exception occurred for GetSubTreePaths for {PATH}: {ERROR}",
+              "PATH", inventoryPath, "ERROR", e);
+    }
+    return {};
+}
+
 } // namespace phosphor::health::utils
diff --git a/health_utils.hpp b/health_utils.hpp
index b89d8e5..ded0159 100644
--- a/health_utils.hpp
+++ b/health_utils.hpp
@@ -12,5 +12,7 @@
 
 /** @brief Start a systemd unit */
 void startUnit(sdbusplus::bus_t& bus, const std::string& sysdUnit);
+/** @brief Find D-Bus paths for given interface */
+auto findPaths(sdbusplus::bus_t& bus, const std::string& iface) -> paths_t;
 
 } // namespace phosphor::health::utils
diff --git a/meson.build b/meson.build
index 09ef147..f56e3f5 100644
--- a/meson.build
+++ b/meson.build
@@ -24,11 +24,11 @@
 executable(
     'health-monitor',
     [
-        'healthMonitor.cpp',
         'health_metric_config.cpp',
         'health_metric.cpp',
         'health_utils.cpp',
         'health_metric_collection.cpp',
+        'health_monitor.cpp',
     ],
     dependencies: [
         base_deps