Support to accumulate PWM of different controllers for same sensor

Description:
1. Add one property: accumulateSetPoint in zone of fan table that could
   be used to enable accumulation of output PWM of different controllers
   with same sensor.

2. Add one property: checkHysterWithSetpt in pid info of fan table to
   select to compare current input and setpoint to check hysteresis.

3. The purpose of accumulate the stepwise output and PID output for
   one sensor is that the setting of stepwise could use to prevent
   the fan speed from suddenly increasing from a very low speed to a
   very high speed due to reaching the setpoint.

   Use stepwise before setpoint could also keep the PWM steady at
   low ambient temperature.

Design:
1. Search "accumulateSetPoint" field in fan table.
   If the value was true, accumulate the output PWM of different
   controllers with same profile name.

2. Support two method to calculate PID output that could be chosen by
   setting the "checkHysterWithSetpt" to true in pid info of fan table.

   If the flag was set to true, it won't calculate PWM output if the
   input lower than setpoint.

Test Case:
1. Check the output PWM of different controllers with same profile
   name could be accumulated - pass.

2. Set "checkHysterWithSetpt" to true and check PID output would not be
   calculated if the input temperature was lower than setpoint - pass.

Please see more details in gist:
https://gist.github.com/DelphineCCChiu/a6170d3e1a12fc4ee76fad324382fba3

Change-Id: I9f38f250d72545784c6c11be2fde7d45f0b385c4
Signed-off-by: Delphine CC Chiu <Delphine_CC_Chiu@wiwynn.com>
diff --git a/pid/pidcontroller.cpp b/pid/pidcontroller.cpp
index fa8d6f6..b33037f 100644
--- a/pid/pidcontroller.cpp
+++ b/pid/pidcontroller.cpp
@@ -30,6 +30,81 @@
 namespace pid_control
 {
 
+double PIDController::calPIDOutput(double setpt, double input,
+                                   ec::pid_info_t* info)
+{
+    double output;
+    auto name = getID();
+
+    if (info->checkHysterWithSetpt)
+    {
+        // Over the hysteresis bounds, keep counting pid
+        if (input > (setpt + info->positiveHysteresis))
+        {
+            // Calculate new output
+            output = ec::pid(info, input, setpt, &name);
+
+            // this variable isn't actually used in this context, but we're
+            // setting it here incase somebody uses it later it's the correct
+            // value
+            lastInput = input;
+        }
+        // Under the hysteresis bounds, initialize pid
+        else if (input < (setpt - info->negativeHysteresis))
+        {
+            lastInput = setpt;
+            info->integral = 0;
+            output = 0;
+        }
+        // inside the hysteresis bounds, keep last output
+        else
+        {
+            lastInput = input;
+            output = info->lastOutput;
+        }
+
+        info->lastOutput = output;
+    }
+    else
+    {
+        // if no hysteresis, maintain previous behavior
+        if (info->positiveHysteresis == 0 && info->negativeHysteresis == 0)
+        {
+            // Calculate new output
+            output = ec::pid(info, input, setpt, &name);
+
+            // this variable isn't actually used in this context, but we're
+            // setting it here incase somebody uses it later it's the correct
+            // value
+            lastInput = input;
+        }
+        else
+        {
+            // initialize if the value is not set (NAN) or abnormal (+INF or
+            // -INF)
+            if (!(std::isfinite(lastInput)))
+            {
+                lastInput = input;
+            }
+
+            // if reading is outside of hysteresis bounds, use it for reading,
+            // otherwise use last reading without updating it first
+            else if ((input - lastInput) > info->positiveHysteresis)
+            {
+                lastInput = input;
+            }
+            else if ((lastInput - input) > info->negativeHysteresis)
+            {
+                lastInput = input;
+            }
+
+            output = ec::pid(info, lastInput, setpt, &name);
+        }
+    }
+
+    return output;
+}
+
 void PIDController::process(void)
 {
     double input;
@@ -43,39 +118,11 @@
     input = inputProc();
 
     auto info = getPIDInfo();
-    auto name = getID();
 
-    // if no hysteresis, maintain previous behavior
-    if (info->positiveHysteresis == 0 && info->negativeHysteresis == 0)
-    {
-        // Calculate new output
-        output = ec::pid(info, input, setpt, &name);
+    // Calculate output value
+    output = calPIDOutput(setpt, input, info);
 
-        // this variable isn't actually used in this context, but we're setting
-        // it here incase somebody uses it later it's the correct value
-        lastInput = input;
-    }
-    else
-    {
-        // initialize if not set yet
-        if (std::isnan(lastInput))
-        {
-            lastInput = input;
-        }
-
-        // if reading is outside of hysteresis bounds, use it for reading,
-        // otherwise use last reading without updating it first
-        else if ((input - lastInput) > info->positiveHysteresis)
-        {
-            lastInput = input;
-        }
-        else if ((lastInput - input) > info->negativeHysteresis)
-        {
-            lastInput = input;
-        }
-
-        output = ec::pid(info, lastInput, setpt, &name);
-    }
+    info->lastOutput = output;
 
     // Output new value
     outputProc(output);