Refactor virtual sensor calculation logic

Refactor the calculation method to calculate.cpp and use std::map
instead of std::array, and other algorithms will be added to
calculate.cpp in the future.

Signed-off-by: George Liu <liuxiwei@ieisystem.com>
Change-Id: I6dcf5119140a5acdf334ebb280dd0b458da1e3ea
diff --git a/calculate.cpp b/calculate.cpp
new file mode 100644
index 0000000..f433751
--- /dev/null
+++ b/calculate.cpp
@@ -0,0 +1,48 @@
+#include "calculate.hpp"
+
+#include <limits>
+
+namespace phosphor::virtual_sensor
+{
+
+double calculateModifiedMedianValue(std::vector<double>& values)
+{
+    size_t size = values.size();
+    std::sort(values.begin(), values.end());
+    switch (size)
+    {
+        case 2:
+            /* Choose biggest value */
+            return values.at(1);
+        case 0:
+            return std::numeric_limits<double>::quiet_NaN();
+        default:
+            /* Choose median value */
+            if (size % 2 == 0)
+            {
+                // Average of the two middle values
+                return (values.at(size / 2) + values.at(size / 2 - 1)) / 2;
+            }
+            else
+            {
+                return values.at((size - 1) / 2);
+            }
+    }
+}
+
+double calculateMaximumValue(std::vector<double>& values)
+{
+    auto maxIt = std::max_element(values.begin(), values.end());
+    if (maxIt == values.end())
+    {
+        return std::numeric_limits<double>::quiet_NaN();
+    }
+    return *maxIt;
+}
+
+std::map<Interface, CalculationFunc> calculationIfaces{
+    {"xyz.openbmc_project.Configuration.Maximum", calculateMaximumValue},
+    {"xyz.openbmc_project.Configuration.ModifiedMedian",
+     calculateModifiedMedianValue}};
+
+} // namespace phosphor::virtual_sensor
diff --git a/calculate.hpp b/calculate.hpp
new file mode 100644
index 0000000..fdf200f
--- /dev/null
+++ b/calculate.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace phosphor::virtual_sensor
+{
+
+using Interface = std::string;
+using CalculationFunc = std::function<double(std::vector<double>&)>;
+extern std::map<Interface, CalculationFunc> calculationIfaces;
+
+} // namespace phosphor::virtual_sensor
diff --git a/meson.build b/meson.build
index bec557a..20d186b 100644
--- a/meson.build
+++ b/meson.build
@@ -24,6 +24,7 @@
 executable(
     'virtual-sensor',
     [
+        'calculate.cpp',
         'virtualSensor.cpp',
         'dbusSensor.cpp',
         'dbusUtils.cpp',
diff --git a/virtualSensor.cpp b/virtualSensor.cpp
index de8ddc0..1356325 100644
--- a/virtualSensor.cpp
+++ b/virtualSensor.cpp
@@ -1,14 +1,13 @@
 #include "virtualSensor.hpp"
 
+#include "calculate.hpp"
+
 #include <phosphor-logging/lg2.hpp>
 
 #include <fstream>
 
 static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/";
 static constexpr auto vsThresholdsIfaceSuffix = ".Thresholds";
-static constexpr std::array<const char*, 2> calculationIfaces = {
-    "xyz.openbmc_project.Configuration.ModifiedMedian",
-    "xyz.openbmc_project.Configuration.Maximum"};
 static constexpr auto defaultHysteresis = 0;
 
 PHOSPHOR_LOG2_USING_WITH_FLAGS;
@@ -94,17 +93,6 @@
     return defaultValue;
 }
 
-bool isCalculationType(const std::string& interface)
-{
-    auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
-                         interface);
-    if (itr != calculationIfaces.end())
-    {
-        return true;
-    }
-    return false;
-}
-
 const std::string getThresholdType(const std::string& direction,
                                    const std::string& severity)
 {
@@ -212,7 +200,7 @@
         }
     }
     /* Get expression string */
-    if (!isCalculationType(interface))
+    if (!calculationIfaces.contains(interface))
     {
         throw std::invalid_argument("Invalid expression in interface");
     }
@@ -424,21 +412,27 @@
 double VirtualSensor::calculateValue(const std::string& calculation,
                                      const VirtualSensor::ParamMap& paramMap)
 {
-    auto itr = std::find(calculationIfaces.begin(), calculationIfaces.end(),
-                         calculation);
-    if (itr == calculationIfaces.end())
+    auto iter = calculationIfaces.find(calculation);
+    if (iter == calculationIfaces.end())
     {
         return std::numeric_limits<double>::quiet_NaN();
     }
-    else if (calculation == "xyz.openbmc_project.Configuration.ModifiedMedian")
+
+    std::vector<double> values;
+    for (auto& param : paramMap)
     {
-        return calculateModifiedMedianValue(paramMap);
+        auto& name = param.first;
+        if (auto var = symbols.get_variable(name))
+        {
+            if (!sensorInRange(var->ref()))
+            {
+                continue;
+            }
+            values.push_back(var->ref());
+        }
     }
-    else if (calculation == "xyz.openbmc_project.Configuration.Maximum")
-    {
-        return calculateMaximumValue(paramMap);
-    }
-    return std::numeric_limits<double>::quiet_NaN();
+
+    return iter->second(values);
 }
 
 bool VirtualSensor::sensorInRange(double value)
@@ -466,9 +460,7 @@
             throw std::invalid_argument("ParamName not found in symbols");
         }
     }
-    auto itr =
-        std::find(calculationIfaces.begin(), calculationIfaces.end(), exprStr);
-    auto val = (itr == calculationIfaces.end())
+    auto val = (!calculationIfaces.contains(exprStr))
                    ? expression.value()
                    : calculateValue(exprStr, paramMap);
 
@@ -484,72 +476,6 @@
     checkThresholds(val, hardShutdownIface);
 }
 
-double VirtualSensor::calculateModifiedMedianValue(
-    const VirtualSensor::ParamMap& paramMap)
-{
-    std::vector<double> values;
-
-    for (auto& param : paramMap)
-    {
-        auto& name = param.first;
-        if (auto var = symbols.get_variable(name))
-        {
-            if (!sensorInRange(var->ref()))
-            {
-                continue;
-            }
-            values.push_back(var->ref());
-        }
-    }
-
-    size_t size = values.size();
-    std::sort(values.begin(), values.end());
-    switch (size)
-    {
-        case 2:
-            /* Choose biggest value */
-            return values.at(1);
-        case 0:
-            return std::numeric_limits<double>::quiet_NaN();
-        default:
-            /* Choose median value */
-            if (size % 2 == 0)
-            {
-                // Average of the two middle values
-                return (values.at(size / 2) + values.at(size / 2 - 1)) / 2;
-            }
-            else
-            {
-                return values.at((size - 1) / 2);
-            }
-    }
-}
-
-double VirtualSensor::calculateMaximumValue(
-    const VirtualSensor::ParamMap& paramMap)
-{
-    std::vector<double> values;
-
-    for (auto& param : paramMap)
-    {
-        auto& name = param.first;
-        if (auto var = symbols.get_variable(name))
-        {
-            if (!sensorInRange(var->ref()))
-            {
-                continue;
-            }
-            values.push_back(var->ref());
-        }
-    }
-    auto maxIt = std::max_element(values.begin(), values.end());
-    if (maxIt == values.end())
-    {
-        return std::numeric_limits<double>::quiet_NaN();
-    }
-    return *maxIt;
-}
-
 void VirtualSensor::createThresholds(const Json& threshold,
                                      const std::string& objPath)
 {
@@ -705,18 +631,18 @@
 
 void VirtualSensors::propertiesChanged(sdbusplus::message_t& msg)
 {
-    std::string path;
+    std::string interface;
     PropertyMap properties;
 
-    msg.read(path, properties);
+    msg.read(interface, properties);
 
     /* We get multiple callbacks for one sensor. 'Type' is a required field and
      * is a unique label so use to to only proceed once per sensor */
     if (properties.contains("Type"))
     {
-        if (isCalculationType(path))
+        if (calculationIfaces.contains(interface))
         {
-            createVirtualSensorsFromDBus(path);
+            createVirtualSensorsFromDBus(interface);
         }
     }
 }
@@ -804,7 +730,7 @@
         this->propertiesChanged(message);
     };
 
-    for (const char* iface : calculationIfaces)
+    for (const auto& [iface, _] : calculationIfaces)
     {
         auto match = std::make_unique<sdbusplus::bus::match_t>(
             bus,
@@ -938,15 +864,15 @@
                 if (desc.contains("Type"))
                 {
                     auto type = desc.value("Type", "");
-                    auto path = "xyz.openbmc_project.Configuration." + type;
+                    auto intf = "xyz.openbmc_project.Configuration." + type;
 
-                    if (!isCalculationType(path))
+                    if (!calculationIfaces.contains(intf))
                     {
                         error("Invalid calculation type {TYPE} supplied.",
                               "TYPE", type);
                         continue;
                     }
-                    createVirtualSensorsFromDBus(path);
+                    createVirtualSensorsFromDBus(intf);
                 }
                 continue;
             }
diff --git a/virtualSensor.hpp b/virtualSensor.hpp
index 314b374..aed1b3e 100644
--- a/virtualSensor.hpp
+++ b/virtualSensor.hpp
@@ -194,11 +194,6 @@
     /** @brief Returns which calculation function or expression to use */
     double calculateValue(const std::string& sensortype,
                           const VirtualSensor::ParamMap& paramMap);
-    /** @brief Calculate median value from sensors */
-    double
-        calculateModifiedMedianValue(const VirtualSensor::ParamMap& paramMap);
-    /** @brief Calculate maximum value from sensors */
-    double calculateMaximumValue(const VirtualSensor::ParamMap& paramMap);
     /** @brief create threshold objects from json config */
     void createThresholds(const Json& threshold, const std::string& objPath);
     /** @brief parse config from entity manager **/