add json config and implementation for hysteresis

Add the json config for health metric hysteresis and implementation
logic for it. Update the health metric config test to validate the
hysteresis value.

Change-Id: I60c0daa601abee9ac1916581d23257cc04128dd0
Signed-off-by: Jagpal Singh Gill <paligill@gmail.com>
diff --git a/health_metric.cpp b/health_metric.cpp
index 2f23394..2887766 100644
--- a/health_metric.cpp
+++ b/health_metric.cpp
@@ -13,8 +13,6 @@
 
 using association_t = std::tuple<std::string, std::string, std::string>;
 
-static constexpr double hysteresis = 1.0;
-
 auto HealthMetric::getPath(MType type, std::string name, SubType subType)
     -> std::string
 {
@@ -216,7 +214,7 @@
     }
     auto changed = std::abs((value.current - lastNotifiedValue) /
                             lastNotifiedValue * 100.0);
-    if (changed >= hysteresis)
+    if (changed >= config.hysteresis)
     {
         lastNotifiedValue = value.current;
         return true;
diff --git a/health_metric_config.cpp b/health_metric_config.cpp
index 88f0972..1c91033 100644
--- a/health_metric_config.cpp
+++ b/health_metric_config.cpp
@@ -75,6 +75,7 @@
 {
     self.windowSize = j.value("Window_size",
                               HealthMetric::defaults::windowSize);
+    self.hysteresis = j.value("Hysteresis", HealthMetric::defaults::hysteresis);
     // Path is only valid for storage
     self.path = j.value("Path", "");
 
@@ -139,9 +140,10 @@
         for (auto& config : configList)
         {
             debug(
-                "TYPE={TYPE}, NAME={NAME} SUBTYPE={SUBTYPE} PATH={PATH}, WSIZE={WSIZE}",
+                "TYPE={TYPE}, NAME={NAME} SUBTYPE={SUBTYPE} PATH={PATH}, WSIZE={WSIZE}, HYSTERESIS={HYSTERESIS}",
                 "TYPE", type, "NAME", config.name, "SUBTYPE", config.subType,
-                "PATH", config.path, "WSIZE", config.windowSize);
+                "PATH", config.path, "WSIZE", config.windowSize, "HYSTERESIS",
+                config.hysteresis);
 
             for (auto& [key, threshold] : config.thresholds)
             {
@@ -194,6 +196,7 @@
 json defaultHealthMetricConfig = R"({
     "CPU": {
         "Window_size": 120,
+        "Hysteresis": 1.0,
         "Threshold": {
             "Critical_Upper": {
                 "Value": 90.0,
@@ -208,16 +211,20 @@
         }
     },
     "CPU_User": {
-        "Window_size": 120
+        "Window_size": 120,
+        "Hysteresis": 1.0
     },
     "CPU_Kernel": {
-        "Window_size": 120
+        "Window_size": 120,
+        "Hysteresis": 1.0
     },
     "Memory": {
-        "Window_size": 120
+        "Window_size": 120,
+        "Hysteresis": 1.0
     },
     "Memory_Available": {
         "Window_size": 120,
+        "Hysteresis": 1.0,
         "Threshold": {
             "Critical_Lower": {
                 "Value": 15.0,
@@ -227,10 +234,12 @@
         }
     },
     "Memory_Free": {
-        "Window_size": 120
+        "Window_size": 120,
+        "Hysteresis": 1.0
     },
     "Memory_Shared": {
         "Window_size": 120,
+        "Hysteresis": 1.0,
         "Threshold": {
             "Critical_Upper": {
                 "Value": 85.0,
@@ -240,11 +249,13 @@
         }
     },
     "Memory_Buffered_And_Cached": {
-        "Window_size": 120
+        "Window_size": 120,
+        "Hysteresis": 1.0
     },
     "Storage_RW": {
         "Path": "/run/initramfs/rw",
         "Window_size": 120,
+        "Hysteresis": 1.0,
         "Threshold": {
             "Critical_Lower": {
                 "Value": 15.0,
@@ -256,6 +267,7 @@
     "Storage_TMP": {
         "Path": "/tmp",
         "Window_size": 120,
+        "Hysteresis": 1.0,
         "Threshold": {
             "Critical_Lower": {
                 "Value": 15.0,
diff --git a/health_metric_config.hpp b/health_metric_config.hpp
index 5943878..89fecdc 100644
--- a/health_metric_config.hpp
+++ b/health_metric_config.hpp
@@ -73,6 +73,8 @@
     SubType subType = SubType::NA;
     /** @brief The window size for the metric. */
     size_t windowSize = defaults::windowSize;
+    /** @brief The hysteresis for the metric */
+    double hysteresis = defaults::hysteresis;
     /** @brief The threshold configs for the metric. */
     Threshold::map_t thresholds{};
     /** @brief The path for filesystem metric */
@@ -84,6 +86,7 @@
     {
         static constexpr auto windowSize = 1;
         static constexpr auto path = "";
+        static constexpr auto hysteresis = 1.0;
     };
 };
 
diff --git a/test/test_health_metric_config.cpp b/test/test_health_metric_config.cpp
index 142a814..5ee6f16 100644
--- a/test/test_health_metric_config.cpp
+++ b/test/test_health_metric_config.cpp
@@ -64,6 +64,7 @@
             EXPECT_NE(config.name, std::string(""));
             EXPECT_TRUE(isValidSubType(type, config.subType));
             EXPECT_GE(config.windowSize, HealthMetric::defaults::windowSize);
+            EXPECT_GE(config.hysteresis, HealthMetric::defaults::hysteresis);
             if (config.thresholds.size())
             {
                 count_with_thresholds++;