Added discrete threshold trigger support

Implemented discrete threshold logic
Discrete trigger with empty threshold array is handled as 'onChange'
Added unit tests coverage for discrete trigger

Supported scenarios:
-discrete threshold with value and dwell time
-discrete threshold with value, without dwell time
-discrete trigger without threshold ('onChange')

Tests:
-Unit tests passed

Change-Id: Id60a48f4113bd955d97e154888c00d1b6e5490af
Signed-off-by: Szymon Dompke <szymon.dompke@intel.com>
diff --git a/src/trigger_actions.cpp b/src/trigger_actions.cpp
index 26fa703..8f2cf82 100644
--- a/src/trigger_actions.cpp
+++ b/src/trigger_actions.cpp
@@ -7,6 +7,25 @@
 namespace action
 {
 
+namespace
+{
+std::string timestampToString(uint64_t timestamp)
+{
+    std::time_t t = static_cast<time_t>(timestamp);
+    std::array<char, sizeof("YYYY-MM-DDThh:mm:ssZ")> buf = {};
+    size_t size =
+        std::strftime(buf.data(), buf.size(), "%FT%TZ", std::gmtime(&t));
+    if (size == 0)
+    {
+        throw std::runtime_error("Failed to parse timestamp to string");
+    }
+    return std::string(buf.data(), size);
+}
+} // namespace
+
+namespace numeric
+{
+
 static const char* getDirection(double value, double threshold)
 {
     if (value < threshold)
@@ -24,13 +43,13 @@
 {
     switch (type)
     {
-        case numeric::Type::upperCritical:
+        case ::numeric::Type::upperCritical:
             return "UpperCritical";
-        case numeric::Type::lowerCritical:
+        case ::numeric::Type::lowerCritical:
             return "LowerCritical";
-        case numeric::Type::upperWarning:
+        case ::numeric::Type::upperWarning:
             return "UpperWarning";
-        case numeric::Type::lowerWarning:
+        case ::numeric::Type::lowerWarning:
             return "LowerWarning";
     }
     throw std::runtime_error("Invalid type");
@@ -39,19 +58,10 @@
 void LogToJournal::commit(const std::string& sensorName, uint64_t timestamp,
                           double value)
 {
-    std::time_t t = static_cast<time_t>(timestamp);
-    std::array<char, sizeof("YYYY-MM-DDThh:mm:ssZ")> buf = {};
-    size_t size =
-        std::strftime(buf.data(), buf.size(), "%FT%TZ", std::gmtime(&t));
-    if (size == 0)
-    {
-        throw std::runtime_error("Failed to parse timestamp to string");
-    }
-
     std::string msg = std::string(getType()) +
                       " numeric threshold condition is met on sensor " +
                       sensorName + ", recorded value " + std::to_string(value) +
-                      ", timestamp " + std::string(buf.data(), size) +
+                      ", timestamp " + timestampToString(timestamp) +
                       ", direction " +
                       std::string(getDirection(value, threshold));
 
@@ -62,13 +72,13 @@
 {
     switch (type)
     {
-        case numeric::Type::upperCritical:
+        case ::numeric::Type::upperCritical:
             return "OpenBMC.0.1.0.NumericThresholdUpperCritical";
-        case numeric::Type::lowerCritical:
+        case ::numeric::Type::lowerCritical:
             return "OpenBMC.0.1.0.NumericThresholdLowerCritical";
-        case numeric::Type::upperWarning:
+        case ::numeric::Type::upperWarning:
             return "OpenBMC.0.1.0.NumericThresholdUpperWarning";
-        case numeric::Type::lowerWarning:
+        case ::numeric::Type::lowerWarning:
             return "OpenBMC.0.1.0.NumericThresholdLowerWarning";
     }
     throw std::runtime_error("Invalid type");
@@ -85,6 +95,84 @@
                                  getDirection(value, threshold)));
 }
 
+} // namespace numeric
+
+namespace discrete
+{
+const char* LogToJournal::getSeverity() const
+{
+    switch (severity)
+    {
+        case ::discrete::Severity::ok:
+            return "OK";
+        case ::discrete::Severity::warning:
+            return "Warning";
+        case ::discrete::Severity::critical:
+            return "Critical";
+    }
+    throw std::runtime_error("Invalid severity");
+}
+
+void LogToJournal::commit(const std::string& sensorName, uint64_t timestamp,
+                          double value)
+{
+    std::string msg = std::string(getSeverity()) +
+                      " discrete threshold condition is met on sensor " +
+                      sensorName + ", recorded value " + std::to_string(value) +
+                      ", timestamp " + timestampToString(timestamp);
+
+    phosphor::logging::log<phosphor::logging::level::INFO>(msg.c_str());
+}
+
+const char* LogToRedfish::getMessageId() const
+{
+    switch (severity)
+    {
+        case ::discrete::Severity::ok:
+            return "OpenBMC.0.1.0.DiscreteThresholdOk";
+        case ::discrete::Severity::warning:
+            return "OpenBMC.0.1.0.DiscreteThresholdWarning";
+        case ::discrete::Severity::critical:
+            return "OpenBMC.0.1.0.DiscreteThresholdCritical";
+    }
+    throw std::runtime_error("Invalid severity");
+}
+
+void LogToRedfish::commit(const std::string& sensorName, uint64_t timestamp,
+                          double value)
+{
+    phosphor::logging::log<phosphor::logging::level::INFO>(
+        "Discrete treshold condition is met",
+        phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", getMessageId()),
+        phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s,%f,%llu",
+                                 sensorName.c_str(), value, timestamp));
+}
+
+namespace onChange
+{
+void LogToJournal::commit(const std::string& sensorName, uint64_t timestamp,
+                          double value)
+{
+    std::string msg = "Value changed on sensor " + sensorName +
+                      ", recorded value " + std::to_string(value) +
+                      ", timestamp " + timestampToString(timestamp);
+
+    phosphor::logging::log<phosphor::logging::level::INFO>(msg.c_str());
+}
+
+void LogToRedfish::commit(const std::string& sensorName, uint64_t timestamp,
+                          double value)
+{
+    const char* messageId = "OpenBMC.0.1.0.DiscreteThresholdOnChange";
+    phosphor::logging::log<phosphor::logging::level::INFO>(
+        "Uncondtional discrete threshold triggered",
+        phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", messageId),
+        phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s,%f,%llu",
+                                 sensorName.c_str(), value, timestamp));
+}
+} // namespace onChange
+} // namespace discrete
+
 void UpdateReport::commit(const std::string&, uint64_t, double)
 {
     for (const auto& name : reportNames)