1ce6a3f36SJames Feist /**
2ce6a3f36SJames Feist * Copyright 2017 Google Inc.
3ce6a3f36SJames Feist *
4ce6a3f36SJames Feist * Licensed under the Apache License, Version 2.0 (the "License");
5ce6a3f36SJames Feist * you may not use this file except in compliance with the License.
6ce6a3f36SJames Feist * You may obtain a copy of the License at
7ce6a3f36SJames Feist *
8ce6a3f36SJames Feist * http://www.apache.org/licenses/LICENSE-2.0
9ce6a3f36SJames Feist *
10ce6a3f36SJames Feist * Unless required by applicable law or agreed to in writing, software
11ce6a3f36SJames Feist * distributed under the License is distributed on an "AS IS" BASIS,
12ce6a3f36SJames Feist * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ce6a3f36SJames Feist * See the License for the specific language governing permissions and
14ce6a3f36SJames Feist * limitations under the License.
15ce6a3f36SJames Feist */
16ce6a3f36SJames Feist
17ce6a3f36SJames Feist #include "pidloop.hpp"
18ce6a3f36SJames Feist
19ce6a3f36SJames Feist #include "pid/pidcontroller.hpp"
20ce6a3f36SJames Feist #include "pid/tuning.hpp"
217a98c19aSPatrick Venture #include "pid/zone_interface.hpp"
22ce6a3f36SJames Feist #include "sensors/sensor.hpp"
23ce6a3f36SJames Feist
24ce6a3f36SJames Feist #include <boost/asio/steady_timer.hpp>
25a83a3eccSPatrick Venture
26ce6a3f36SJames Feist #include <chrono>
27ce6a3f36SJames Feist #include <map>
28ce6a3f36SJames Feist #include <memory>
297a98c19aSPatrick Venture #include <sstream>
30ce6a3f36SJames Feist #include <thread>
31ce6a3f36SJames Feist #include <vector>
32ce6a3f36SJames Feist
33a076487aSPatrick Venture namespace pid_control
34a076487aSPatrick Venture {
35a076487aSPatrick Venture
processThermals(std::shared_ptr<ZoneInterface> zone)365301aae3SJohnathan Mantey static void processThermals(std::shared_ptr<ZoneInterface> zone)
37ce6a3f36SJames Feist {
38ce6a3f36SJames Feist // Get the latest margins.
39ce6a3f36SJames Feist zone->updateSensors();
409bbf333dSPatrick Venture // Zero out the set point goals.
419bbf333dSPatrick Venture zone->clearSetPoints();
42ce6a3f36SJames Feist zone->clearRPMCeilings();
43ce6a3f36SJames Feist // Run the margin PIDs.
44ce6a3f36SJames Feist zone->processThermals();
45ce6a3f36SJames Feist // Get the maximum RPM setpoint.
46f7a2dd5cSPatrick Venture zone->determineMaxSetPointRequest();
47ce6a3f36SJames Feist }
48ce6a3f36SJames Feist
pidControlLoop(std::shared_ptr<ZoneInterface> zone,std::shared_ptr<boost::asio::steady_timer> timer,const bool * isCanceling,bool first,uint64_t cycleCnt)495301aae3SJohnathan Mantey void pidControlLoop(std::shared_ptr<ZoneInterface> zone,
505301aae3SJohnathan Mantey std::shared_ptr<boost::asio::steady_timer> timer,
510e8fc398SBonnie Lo const bool* isCanceling, bool first, uint64_t cycleCnt)
52ce6a3f36SJames Feist {
53b6a0b89eSHao Jiang if (*isCanceling)
54b6a0b89eSHao Jiang return;
55b6a0b89eSHao Jiang
56*9f9a06aaSJosh Lehan std::chrono::steady_clock::time_point nextTime;
57*9f9a06aaSJosh Lehan
58ce6a3f36SJames Feist if (first)
59ce6a3f36SJames Feist {
60de79ee05SPatrick Venture if (loggingEnabled)
61ce6a3f36SJames Feist {
62ce6a3f36SJames Feist zone->initializeLog();
63ce6a3f36SJames Feist }
64ce6a3f36SJames Feist
65ce6a3f36SJames Feist zone->initializeCache();
66ce6a3f36SJames Feist processThermals(zone);
67*9f9a06aaSJosh Lehan
68*9f9a06aaSJosh Lehan nextTime = std::chrono::steady_clock::now();
69*9f9a06aaSJosh Lehan }
70*9f9a06aaSJosh Lehan else
71*9f9a06aaSJosh Lehan {
72*9f9a06aaSJosh Lehan nextTime = timer->expiry();
73ce6a3f36SJames Feist }
74ce6a3f36SJames Feist
75*9f9a06aaSJosh Lehan uint64_t msPerFanCycle = zone->getCycleIntervalTime();
76*9f9a06aaSJosh Lehan
77*9f9a06aaSJosh Lehan // Push forward the original expiration time of timer, instead of just
78*9f9a06aaSJosh Lehan // resetting it with expires_after() from now, to make sure the interval
79*9f9a06aaSJosh Lehan // is of the expected duration, and not stretched out by CPU time taken.
80*9f9a06aaSJosh Lehan nextTime += std::chrono::milliseconds(msPerFanCycle);
81*9f9a06aaSJosh Lehan timer->expires_at(nextTime);
82*9f9a06aaSJosh Lehan timer->async_wait([zone, timer, cycleCnt, isCanceling, msPerFanCycle](
83b6a0b89eSHao Jiang const boost::system::error_code& ec) mutable {
841fe08952SJames Feist if (ec == boost::asio::error::operation_aborted)
851fe08952SJames Feist {
861fe08952SJames Feist return; // timer being canceled, stop loop
871fe08952SJames Feist }
881fe08952SJames Feist
89ce6a3f36SJames Feist /*
90ce6a3f36SJames Feist * This should sleep on the conditional wait for the listen thread
91ce6a3f36SJames Feist * to tell us it's in sync. But then we also need a timeout option
92ce6a3f36SJames Feist * in case phosphor-hwmon is down, we can go into some weird failure
93ce6a3f36SJames Feist * more.
94ce6a3f36SJames Feist *
95ce6a3f36SJames Feist * Another approach would be to start all sensors in worst-case
96ce6a3f36SJames Feist * values, and fail-safe mode and then clear out of fail-safe mode
97ce6a3f36SJames Feist * once we start getting values. Which I think it is a solid
98ce6a3f36SJames Feist * approach.
99ce6a3f36SJames Feist *
100ce6a3f36SJames Feist * For now this runs before it necessarily has any sensor values.
101ce6a3f36SJames Feist * For the host sensors they start out in fail-safe mode. For the
102ce6a3f36SJames Feist * fans, they start out as 0 as input and then are adjusted once
103ce6a3f36SJames Feist * they have values.
104ce6a3f36SJames Feist *
105ce6a3f36SJames Feist * If a fan has failed, it's value will be whatever we're told or
106ce6a3f36SJames Feist * however we retrieve it. This program disregards fan values of 0,
107ce6a3f36SJames Feist * so any code providing a fan speed can set to 0 on failure and
108ce6a3f36SJames Feist * that fan value will be effectively ignored. The PID algorithm
109ce6a3f36SJames Feist * will be unhappy but nothing bad will happen.
110ce6a3f36SJames Feist *
111ce6a3f36SJames Feist * TODO(venture): If the fan value is 0 should that loop just be
112ce6a3f36SJames Feist * skipped? Right now, a 0 value is ignored in
113ce6a3f36SJames Feist * FanController::inputProc()
114ce6a3f36SJames Feist */
115ce6a3f36SJames Feist
116ce6a3f36SJames Feist // Check if we should just go back to sleep.
117ce6a3f36SJames Feist if (zone->getManualMode())
118ce6a3f36SJames Feist {
1190e8fc398SBonnie Lo pidControlLoop(zone, timer, isCanceling, false, cycleCnt);
120ce6a3f36SJames Feist return;
121ce6a3f36SJames Feist }
122ce6a3f36SJames Feist
123ce6a3f36SJames Feist // Get the latest fan speeds.
124ce6a3f36SJames Feist zone->updateFanTelemetry();
125ce6a3f36SJames Feist
126*9f9a06aaSJosh Lehan uint64_t msPerThermalCycle = zone->getUpdateThermalsCycle();
127*9f9a06aaSJosh Lehan
128*9f9a06aaSJosh Lehan // Process thermal cycles at a rate that is less often than fan
129*9f9a06aaSJosh Lehan // cycles. If thermal time is not an exact multiple of fan time,
130*9f9a06aaSJosh Lehan // there will be some remainder left over, to keep the timing
131*9f9a06aaSJosh Lehan // correct, as the intervals are staggered into one another.
132*9f9a06aaSJosh Lehan if (cycleCnt >= msPerThermalCycle)
133ce6a3f36SJames Feist {
134*9f9a06aaSJosh Lehan cycleCnt -= msPerThermalCycle;
135ce6a3f36SJames Feist
136ce6a3f36SJames Feist processThermals(zone);
137ce6a3f36SJames Feist }
138ce6a3f36SJames Feist
139ce6a3f36SJames Feist // Run the fan PIDs every iteration.
140ce6a3f36SJames Feist zone->processFans();
141ce6a3f36SJames Feist
142de79ee05SPatrick Venture if (loggingEnabled)
143ce6a3f36SJames Feist {
1447a98c19aSPatrick Venture std::ostringstream out;
1457a98c19aSPatrick Venture out << "," << zone->getFailSafeMode() << std::endl;
1467a98c19aSPatrick Venture zone->writeLog(out.str());
147ce6a3f36SJames Feist }
148ce6a3f36SJames Feist
149*9f9a06aaSJosh Lehan // Count how many milliseconds have elapsed, so we can know when
150*9f9a06aaSJosh Lehan // to perform thermal cycles, in proper ratio with fan cycles.
151*9f9a06aaSJosh Lehan cycleCnt += msPerFanCycle;
152ce6a3f36SJames Feist
1530e8fc398SBonnie Lo pidControlLoop(zone, timer, isCanceling, false, cycleCnt);
154ce6a3f36SJames Feist });
155ce6a3f36SJames Feist }
156a076487aSPatrick Venture
157a076487aSPatrick Venture } // namespace pid_control
158