Fix interval calculation bug and D-Bus init

The new timing parameters were not settable from the D-Bus code path,
only from the old static JSON code path. Also, the divison would not
occur, causing the variable to remain at 1000 by default, not 10,
causing the thermal intervals to run 100 times slower than intended!

I fixed the algorithm used to calculate when the thermal intervals
should be inserted amongst the fan intervals. Now, the division is
not necessary, and any value should work, so long as the thermal
interval is greater than or equal to the fan interval.

I also fixed a subtle bug regarding the timer scheduling. It was
reinitializing the timer expiration time from "now" each interval,
instead of cleanly incrementing from the original expiration. This
caused the timer to run slower than intended, as the execution time
of each interval would not be subtracted out from the remaining time
that needs to be waited for, as it should have been.

Tested: Default values, for timing parameters, now work as intended

Signed-off-by: Josh Lehan <krellan@google.com>
Change-Id: I759387b97af3ce93a76459faf5e9e7be3474016e
diff --git a/pid/pidloop.cpp b/pid/pidloop.cpp
index aba2b6e..0ccf35c 100644
--- a/pid/pidloop.cpp
+++ b/pid/pidloop.cpp
@@ -53,6 +53,8 @@
     if (*isCanceling)
         return;
 
+    std::chrono::steady_clock::time_point nextTime;
+
     if (first)
     {
         if (loggingEnabled)
@@ -62,11 +64,22 @@
 
         zone->initializeCache();
         processThermals(zone);
+
+        nextTime = std::chrono::steady_clock::now();
+    }
+    else
+    {
+        nextTime = timer->expiry();
     }
 
-    timer->expires_after(
-        std::chrono::milliseconds(zone->getCycleIntervalTime()));
-    timer->async_wait([zone, timer, cycleCnt, isCanceling](
+    uint64_t msPerFanCycle = zone->getCycleIntervalTime();
+
+    // Push forward the original expiration time of timer, instead of just
+    // resetting it with expires_after() from now, to make sure the interval
+    // is of the expected duration, and not stretched out by CPU time taken.
+    nextTime += std::chrono::milliseconds(msPerFanCycle);
+    timer->expires_at(nextTime);
+    timer->async_wait([zone, timer, cycleCnt, isCanceling, msPerFanCycle](
                           const boost::system::error_code& ec) mutable {
         if (ec == boost::asio::error::operation_aborted)
         {
@@ -110,9 +123,15 @@
         // Get the latest fan speeds.
         zone->updateFanTelemetry();
 
-        if (zone->getUpdateThermalsCycle() <= cycleCnt)
+        uint64_t msPerThermalCycle = zone->getUpdateThermalsCycle();
+
+        // Process thermal cycles at a rate that is less often than fan
+        // cycles. If thermal time is not an exact multiple of fan time,
+        // there will be some remainder left over, to keep the timing
+        // correct, as the intervals are staggered into one another.
+        if (cycleCnt >= msPerThermalCycle)
         {
-            cycleCnt = 0;
+            cycleCnt -= msPerThermalCycle;
 
             processThermals(zone);
         }
@@ -127,7 +146,9 @@
             zone->writeLog(out.str());
         }
 
-        cycleCnt += 1;
+        // Count how many milliseconds have elapsed, so we can know when
+        // to perform thermal cycles, in proper ratio with fan cycles.
+        cycleCnt += msPerFanCycle;
 
         pidControlLoop(zone, timer, isCanceling, false, cycleCnt);
     });