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 **/