Add stepwise parser

The json parser cannot read stepwise setting from config.
This change adds support to stepwise type of pid.

Change-Id: I650c5bd6a0040bf25630e33b3bd36abf388f0cd8
Signed-off-by: Hank Liou <Hank.Liou@quantatw.com>
diff --git a/pid/buildjson.cpp b/pid/buildjson.cpp
index ad51d9b..5e90c47 100644
--- a/pid/buildjson.cpp
+++ b/pid/buildjson.cpp
@@ -37,36 +37,79 @@
      * accordingly.
      */
     auto p = j.at("pid");
-    p.at("samplePeriod").get_to(c.pidInfo.ts);
-    p.at("proportionalCoeff").get_to(c.pidInfo.proportionalCoeff);
-    p.at("integralCoeff").get_to(c.pidInfo.integralCoeff);
-    p.at("feedFwdOffsetCoeff").get_to(c.pidInfo.feedFwdOffset);
-    p.at("feedFwdGainCoeff").get_to(c.pidInfo.feedFwdGain);
-    p.at("integralLimit_min").get_to(c.pidInfo.integralLimit.min);
-    p.at("integralLimit_max").get_to(c.pidInfo.integralLimit.max);
-    p.at("outLim_min").get_to(c.pidInfo.outLim.min);
-    p.at("outLim_max").get_to(c.pidInfo.outLim.max);
-    p.at("slewNeg").get_to(c.pidInfo.slewNeg);
-    p.at("slewPos").get_to(c.pidInfo.slewPos);
 
     auto positiveHysteresis = p.find("positiveHysteresis");
-    if (positiveHysteresis == p.end())
+    auto negativeHysteresis = p.find("negativeHysteresis");
+    auto positiveHysteresisValue = 0.0;
+    auto negativeHysteresisValue = 0.0;
+    if (positiveHysteresis != p.end())
     {
-        c.pidInfo.positiveHysteresis = 0.0;
+        p.at("positiveHysteresis").get_to(positiveHysteresisValue);
     }
-    else
+    if (negativeHysteresis != p.end())
     {
-        p.at("positiveHysteresis").get_to(c.pidInfo.positiveHysteresis);
+        p.at("negativeHysteresis").get_to(negativeHysteresisValue);
     }
 
-    auto negativeHysteresis = p.find("negativeHysteresis");
-    if (negativeHysteresis == p.end())
+    if (c.type != "stepwise")
     {
-        c.pidInfo.negativeHysteresis = 0.0;
+        p.at("samplePeriod").get_to(c.pidInfo.ts);
+        p.at("proportionalCoeff").get_to(c.pidInfo.proportionalCoeff);
+        p.at("integralCoeff").get_to(c.pidInfo.integralCoeff);
+        p.at("feedFwdOffsetCoeff").get_to(c.pidInfo.feedFwdOffset);
+        p.at("feedFwdGainCoeff").get_to(c.pidInfo.feedFwdGain);
+        p.at("integralLimit_min").get_to(c.pidInfo.integralLimit.min);
+        p.at("integralLimit_max").get_to(c.pidInfo.integralLimit.max);
+        p.at("outLim_min").get_to(c.pidInfo.outLim.min);
+        p.at("outLim_max").get_to(c.pidInfo.outLim.max);
+        p.at("slewNeg").get_to(c.pidInfo.slewNeg);
+        p.at("slewPos").get_to(c.pidInfo.slewPos);
+
+        c.pidInfo.positiveHysteresis = positiveHysteresisValue;
+        c.pidInfo.negativeHysteresis = negativeHysteresisValue;
     }
     else
     {
-        p.at("negativeHysteresis").get_to(c.pidInfo.negativeHysteresis);
+        p.at("samplePeriod").get_to(c.stepwiseInfo.ts);
+        p.at("isCeiling").get_to(c.stepwiseInfo.isCeiling);
+
+        for (size_t i = 0; i < ec::maxStepwisePoints; i++)
+        {
+            c.stepwiseInfo.reading[i] =
+                std::numeric_limits<double>::quiet_NaN();
+            c.stepwiseInfo.output[i] = std::numeric_limits<double>::quiet_NaN();
+        }
+
+        auto reading = p.find("reading");
+        if (reading != p.end())
+        {
+            auto r = p.at("reading");
+            for (size_t i = 0; i < ec::maxStepwisePoints; i++)
+            {
+                auto n = r.find(std::to_string(i));
+                if (n != r.end())
+                {
+                    r.at(std::to_string(i)).get_to(c.stepwiseInfo.reading[i]);
+                }
+            }
+        }
+
+        auto output = p.find("output");
+        if (output != p.end())
+        {
+            auto o = p.at("output");
+            for (size_t i = 0; i < ec::maxStepwisePoints; i++)
+            {
+                auto n = o.find(std::to_string(i));
+                if (n != o.end())
+                {
+                    o.at(std::to_string(i)).get_to(c.stepwiseInfo.output[i]);
+                }
+            }
+        }
+
+        c.stepwiseInfo.positiveHysteresis = positiveHysteresisValue;
+        c.stepwiseInfo.negativeHysteresis = negativeHysteresisValue;
     }
 }
 } // namespace conf
diff --git a/test/pid_json_unittest.cpp b/test/pid_json_unittest.cpp
index 9d33789..2bd7d35 100644
--- a/test/pid_json_unittest.cpp
+++ b/test/pid_json_unittest.cpp
@@ -115,3 +115,88 @@
 
     EXPECT_DOUBLE_EQ(zoneConfig[1].minThermalOutput, 3000.0);
 }
+
+TEST(ZoneFromJson, oneZoneOneStepwiseWithHysteresis)
+{
+    // Parse a valid configuration with one zone and one PID and the PID uses
+    // Hysteresis parameters.
+
+    std::map<int64_t, conf::PIDConf> pidConfig;
+    std::map<int64_t, struct conf::ZoneConfig> zoneConfig;
+
+    auto j2 = R"(
+      {
+        "zones" : [{
+          "id": 1,
+          "minThermalOutput": 3000.0,
+          "failsafePercent": 75.0,
+          "pids": [{
+            "name": "temp1",
+            "type": "stepwise",
+            "inputs": ["temp1"],
+            "setpoint": 30.0,
+            "pid": {
+              "samplePeriod": 0.1,
+              "positiveHysteresis": 1.0,
+              "negativeHysteresis": 1.0,
+              "isCeiling": false,
+              "reading": {
+                "0": 45,
+                "1": 46,
+                "2": 47,
+                "3": 48,
+                "4": 49,
+                "5": 50,
+                "6": 51,
+                "7": 52,
+                "8": 53,
+                "9": 54,
+                "10": 55,
+                "11": 56,
+                "12": 57,
+                "13": 58,
+                "14": 59,
+                "15": 60,
+                "16": 61,
+                "17": 62,
+                "18": 63,
+                "19": 64
+              },
+              "output": {
+                "0": 5000,
+                "1": 2400,
+                "2": 2600,
+                "3": 2800,
+                "4": 3000,
+                "5": 3200,
+                "6": 3400,
+                "7": 3600,
+                "8": 3800,
+                "9": 4000,
+                "10": 4200,
+                "11": 4400,
+                "12": 4600,
+                "13": 4800,
+                "14": 5000,
+                "15": 5200,
+                "16": 5400,
+                "17": 5600,
+                "18": 5800,
+                "19": 6000
+              }
+            }
+          }]
+        }]
+      }
+    )"_json;
+
+    std::tie(pidConfig, zoneConfig) = buildPIDsFromJson(j2);
+    EXPECT_EQ(pidConfig.size(), 1);
+    EXPECT_EQ(zoneConfig.size(), 1);
+
+    EXPECT_EQ(pidConfig[1]["temp1"].type, "stepwise");
+    EXPECT_DOUBLE_EQ(pidConfig[1]["temp1"].stepwiseInfo.positiveHysteresis,
+                     1.0);
+
+    EXPECT_DOUBLE_EQ(zoneConfig[1].minThermalOutput, 3000.0);
+}
\ No newline at end of file