Created metric class

Metric collects updates from sensor. Report displays metric readings
depending on reportingType.

Tested:
  - Added new units tests for Metric class
  - All other unit tests are passing

Change-Id: I19f4831fab163a4f9540cef7bb23e903ae90fddf
Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com>
diff --git a/src/report_factory.cpp b/src/report_factory.cpp
index 5ac32f3..e4689aa 100644
--- a/src/report_factory.cpp
+++ b/src/report_factory.cpp
@@ -1,5 +1,6 @@
 #include "report_factory.hpp"
 
+#include "metric.hpp"
 #include "report.hpp"
 #include "sensor.hpp"
 #include "utils/transform.hpp"
@@ -19,10 +20,84 @@
     interfaces::ReportManager& reportManager,
     interfaces::JsonStorage& reportStorage) const
 {
+    std::optional<std::vector<ReportFactory::SensorTree>> sensorTree;
+
     std::vector<std::shared_ptr<interfaces::Metric>> metrics;
+    metrics.reserve(metricParams.size());
+
+    for (const auto& [sensorPaths, op, id, metadata] : metricParams)
+    {
+        if (!sensorTree && yield && sensorPaths.size() > 0)
+        {
+            sensorTree = getSensorTree(*yield);
+        }
+
+        std::vector<std::shared_ptr<interfaces::Sensor>> sensors =
+            getSensors(sensorTree, sensorPaths);
+
+        metrics.emplace_back(
+            std::make_shared<Metric>(std::move(sensors), op, id, metadata));
+    }
 
     return std::make_unique<Report>(
         bus->get_io_context(), objServer, name, reportingType,
         emitsReadingsSignal, logToMetricReportsCollection, period, metricParams,
         reportManager, reportStorage, std::move(metrics));
 }
+
+std::vector<std::shared_ptr<interfaces::Sensor>> ReportFactory::getSensors(
+    const std::optional<std::vector<ReportFactory::SensorTree>>& tree,
+    const std::vector<sdbusplus::message::object_path>& sensorPaths) const
+{
+    if (tree)
+    {
+        std::vector<std::shared_ptr<interfaces::Sensor>> sensors;
+
+        for (const auto& [sensor, ifacesMap] : *tree)
+        {
+            auto it = std::find(sensorPaths.begin(), sensorPaths.end(), sensor);
+            if (it != sensorPaths.end())
+            {
+                for (const auto& [service, ifaces] : ifacesMap)
+                {
+                    sensors.emplace_back(sensorCache.makeSensor<Sensor>(
+                        service, sensor, bus->get_io_context(), bus));
+                }
+            }
+        }
+
+        return sensors;
+    }
+    else
+    {
+        return utils::transform(
+            sensorPaths,
+            [this](const std::string& sensor)
+                -> std::shared_ptr<interfaces::Sensor> {
+                std::string::size_type pos = sensor.find_first_of(":");
+                auto service = sensor.substr(0, pos);
+                auto path = sensor.substr(pos + 1);
+                return sensorCache.makeSensor<Sensor>(
+                    service, path, bus->get_io_context(), bus);
+            });
+    }
+}
+
+std::vector<ReportFactory::SensorTree>
+    ReportFactory::getSensorTree(boost::asio::yield_context& yield) const
+{
+    std::array<const char*, 1> interfaces = {
+        "xyz.openbmc_project.Sensor.Value"};
+    boost::system::error_code ec;
+
+    auto result = bus->yield_method_call<std::vector<SensorTree>>(
+        yield, ec, "xyz.openbmc_project.ObjectMapper",
+        "/xyz/openbmc_project/object_mapper",
+        "xyz.openbmc_project.ObjectMapper", "GetSubTree",
+        "/xyz/openbmc_project/sensors", 2, interfaces);
+    if (ec)
+    {
+        throw std::runtime_error("failed");
+    }
+    return result;
+}