stepwise: Add ceiling type

Add a stepwise ceiling type, this is used as a
upper clipping curve to limit the max output based
on a temperature sensor. This is commonly used for
quiet fan mode where CPU throttling is allowed to
preserve a max fan noise.

Change-Id: I181d5913c92e5498a34e6d3f67cf99b67471479c
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/dbus/dbusconfiguration.cpp b/dbus/dbusconfiguration.cpp
index 7cb09fb..b449490 100644
--- a/dbus/dbusconfiguration.cpp
+++ b/dbus/dbusconfiguration.cpp
@@ -507,6 +507,9 @@
                 info.stepwiseInfo.ts = 1.0; // currently unused
                 info.stepwiseInfo.positiveHysteresis = 0.0;
                 info.stepwiseInfo.negativeHysteresis = 0.0;
+                std::string subtype = std::get<std::string>(base.at("Class"));
+
+                info.stepwiseInfo.isCeiling = (subtype == "Ceiling");
                 auto findPosHyst = base.find("PositiveHysteresis");
                 auto findNegHyst = base.find("NegativeHysteresis");
                 if (findPosHyst != base.end())
diff --git a/pid/ec/stepwise.hpp b/pid/ec/stepwise.hpp
index bc7c204..3ce847a 100644
--- a/pid/ec/stepwise.hpp
+++ b/pid/ec/stepwise.hpp
@@ -31,6 +31,7 @@
     double output[maxStepwisePoints];
     double positiveHysteresis;
     double negativeHysteresis;
+    bool isCeiling;
 };
 
 double stepwise(const ec::StepwiseInfo& info, double value);
diff --git a/pid/pidthread.cpp b/pid/pidthread.cpp
index 44c5e86..08fb513 100644
--- a/pid/pidthread.cpp
+++ b/pid/pidthread.cpp
@@ -31,6 +31,7 @@
     zone->updateSensors();
     // Zero out the RPM set point goals.
     zone->clearRPMSetPoints();
+    zone->clearRPMCeilings();
     // Run the margin PIDs.
     zone->processThermals();
     // Get the maximum RPM setpoint.
diff --git a/pid/stepwisecontroller.cpp b/pid/stepwisecontroller.cpp
index fee25ab..2408502 100644
--- a/pid/stepwisecontroller.cpp
+++ b/pid/stepwisecontroller.cpp
@@ -95,9 +95,13 @@
 
 void StepwiseController::outputProc(double value)
 {
-    // values are 10 for 10%
-    value *= 100;
-    _owner->addRPMSetPoint(value);
-
+    if (get_stepwise_info().isCeiling)
+    {
+        _owner->addRPMCeiling(value);
+    }
+    else
+    {
+        _owner->addRPMSetPoint(value);
+    }
     return;
 }
diff --git a/pid/zone.cpp b/pid/zone.cpp
index fc0b40b..065e6d7 100644
--- a/pid/zone.cpp
+++ b/pid/zone.cpp
@@ -65,6 +65,16 @@
     _RPMSetPoints.push_back(setpoint);
 }
 
+void PIDZone::addRPMCeiling(double ceiling)
+{
+    _RPMCeilings.push_back(ceiling);
+}
+
+void PIDZone::clearRPMCeilings(void)
+{
+    _RPMCeilings.clear();
+}
+
 void PIDZone::clearRPMSetPoints(void)
 {
     _RPMSetPoints.clear();
@@ -116,6 +126,12 @@
         max = *result;
     }
 
+    if (_RPMCeilings.size() > 0)
+    {
+        result = std::min_element(_RPMCeilings.begin(), _RPMCeilings.end());
+        max = std::min(max, *result);
+    }
+
     /*
      * If the maximum RPM setpoint output is below the minimum RPM
      * setpoint, set it to the minimum.
diff --git a/pid/zone.hpp b/pid/zone.hpp
index 223f3cb..077d272 100644
--- a/pid/zone.hpp
+++ b/pid/zone.hpp
@@ -28,6 +28,7 @@
 
     virtual double getCachedValue(const std::string& name) = 0;
     virtual void addRPMSetPoint(double setpoint) = 0;
+    virtual void addRPMCeiling(double ceiling) = 0;
     virtual double getMaxRPMRequest() const = 0;
     virtual bool getFailSafeMode() const = 0;
     virtual double getFailSafePercent() const = 0;
@@ -64,7 +65,9 @@
     bool getFailSafeMode(void) const override;
     int64_t getZoneID(void) const;
     void addRPMSetPoint(double setpoint) override;
+    void addRPMCeiling(double ceiling) override;
     void clearRPMSetPoints(void);
+    void clearRPMCeilings(void);
     double getFailSafePercent(void) const override;
     double getMinThermalRPMSetpoint(void) const;
 
@@ -107,6 +110,7 @@
     std::set<std::string> _failSafeSensors;
 
     std::vector<double> _RPMSetPoints;
+    std::vector<double> _RPMCeilings;
     std::vector<std::string> _fanInputs;
     std::vector<std::string> _thermalInputs;
     std::map<std::string, double> _cachedValuesByName;
diff --git a/test/pid_stepwisecontroller_unittest.cpp b/test/pid_stepwisecontroller_unittest.cpp
index 5b1e655..5cf825d 100644
--- a/test/pid_stepwisecontroller_unittest.cpp
+++ b/test/pid_stepwisecontroller_unittest.cpp
@@ -12,8 +12,6 @@
 using ::testing::Return;
 using ::testing::StrEq;
 
-constexpr size_t scale = 100; // values are 10 for 10%
-
 TEST(StepwiseControllerTest, HysteresisTestPositive)
 {
     // Verifies positive hysteresis works as expected
@@ -29,6 +27,7 @@
     initial.reading[2] = std::numeric_limits<double>::quiet_NaN();
     initial.output[0] = 40.0;
     initial.output[1] = 60.0;
+    initial.isCeiling = false;
 
     std::unique_ptr<Controller> p =
         StepwiseController::createStepwiseController(&z, "foo", inputs,
@@ -40,8 +39,8 @@
         .WillOnce(Return(31.0))  // return 40
         .WillOnce(Return(32.0)); // return 60
 
-    EXPECT_CALL(z, addRPMSetPoint(40.0 * scale)).Times(2);
-    EXPECT_CALL(z, addRPMSetPoint(60.0 * scale)).Times(1);
+    EXPECT_CALL(z, addRPMSetPoint(40.0)).Times(2);
+    EXPECT_CALL(z, addRPMSetPoint(60.0)).Times(1);
 
     for (int ii = 0; ii < 3; ii++)
     {
@@ -64,6 +63,7 @@
     initial.reading[2] = std::numeric_limits<double>::quiet_NaN();
     initial.output[0] = 40.0;
     initial.output[1] = 60.0;
+    initial.isCeiling = false;
 
     std::unique_ptr<Controller> p =
         StepwiseController::createStepwiseController(&z, "foo", inputs,
@@ -75,8 +75,8 @@
         .WillOnce(Return(27.0))  // return 60
         .WillOnce(Return(26.0)); // return 40
 
-    EXPECT_CALL(z, addRPMSetPoint(40.0 * scale)).Times(1);
-    EXPECT_CALL(z, addRPMSetPoint(60.0 * scale)).Times(2);
+    EXPECT_CALL(z, addRPMSetPoint(40.0)).Times(1);
+    EXPECT_CALL(z, addRPMSetPoint(60.0)).Times(2);
 
     for (int ii = 0; ii < 3; ii++)
     {
diff --git a/test/zone_mock.hpp b/test/zone_mock.hpp
index 35a6b32..4a9c3f8 100644
--- a/test/zone_mock.hpp
+++ b/test/zone_mock.hpp
@@ -13,6 +13,7 @@
 
     MOCK_METHOD1(getCachedValue, double(const std::string&));
     MOCK_METHOD1(addRPMSetPoint, void(double));
+    MOCK_METHOD1(addRPMCeiling, void(double));
     MOCK_CONST_METHOD0(getMaxRPMRequest, double());
     MOCK_CONST_METHOD0(getFailSafeMode, bool());
     MOCK_CONST_METHOD0(getFailSafePercent, double());