xref: /openbmc/phosphor-pid-control/pid/zone.cpp (revision 107a25da)
1 /**
2  * Copyright 2017 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /* Configuration. */
18 #include "zone.hpp"
19 
20 #include "conf.hpp"
21 #include "pid/controller.hpp"
22 #include "pid/ec/pid.hpp"
23 #include "pid/fancontroller.hpp"
24 #include "pid/stepwisecontroller.hpp"
25 #include "pid/thermalcontroller.hpp"
26 
27 #include <algorithm>
28 #include <chrono>
29 #include <cstring>
30 #include <fstream>
31 #include <iostream>
32 #include <libconfig.h++>
33 #include <memory>
34 
35 using tstamp = std::chrono::high_resolution_clock::time_point;
36 using namespace std::literals::chrono_literals;
37 
38 float PIDZone::getMaxRPMRequest(void) const
39 {
40     return _maximumRPMSetPt;
41 }
42 
43 bool PIDZone::getManualMode(void) const
44 {
45     return _manualMode;
46 }
47 
48 void PIDZone::setManualMode(bool mode)
49 {
50     _manualMode = mode;
51 }
52 
53 bool PIDZone::getFailSafeMode(void) const
54 {
55     // If any keys are present at least one sensor is in fail safe mode.
56     return !_failSafeSensors.empty();
57 }
58 
59 int64_t PIDZone::getZoneId(void) const
60 {
61     return _zoneId;
62 }
63 
64 void PIDZone::addRPMSetPoint(float setpoint)
65 {
66     _RPMSetPoints.push_back(setpoint);
67 }
68 
69 void PIDZone::clearRPMSetPoints(void)
70 {
71     _RPMSetPoints.clear();
72 }
73 
74 float PIDZone::getFailSafePercent(void) const
75 {
76     return _failSafePercent;
77 }
78 
79 float PIDZone::getMinThermalRpmSetPt(void) const
80 {
81     return _minThermalRpmSetPt;
82 }
83 
84 void PIDZone::addFanPID(std::unique_ptr<Controller> pid)
85 {
86     _fans.push_back(std::move(pid));
87 }
88 
89 void PIDZone::addThermalPID(std::unique_ptr<Controller> pid)
90 {
91     _thermals.push_back(std::move(pid));
92 }
93 
94 double PIDZone::getCachedValue(const std::string& name)
95 {
96     return _cachedValuesByName.at(name);
97 }
98 
99 void PIDZone::addFanInput(std::string fan)
100 {
101     _fanInputs.push_back(fan);
102 }
103 
104 void PIDZone::addThermalInput(std::string therm)
105 {
106     _thermalInputs.push_back(therm);
107 }
108 
109 void PIDZone::determineMaxRPMRequest(void)
110 {
111     float max = 0;
112     std::vector<float>::iterator result;
113 
114     if (_RPMSetPoints.size() > 0)
115     {
116         result = std::max_element(_RPMSetPoints.begin(), _RPMSetPoints.end());
117         max = *result;
118     }
119 
120     /*
121      * If the maximum RPM set-point output is below the minimum RPM
122      * set-point, set it to the minimum.
123      */
124     max = std::max(getMinThermalRpmSetPt(), max);
125 
126 #ifdef __TUNING_LOGGING__
127     /*
128      * We received no set-points from thermal sensors.
129      * This is a case experienced during tuning where they only specify
130      * fan sensors and one large fan PID for all the fans.
131      */
132     static constexpr auto setpointpath = "/etc/thermal.d/set-point";
133     try
134     {
135         std::ifstream ifs;
136         ifs.open(setpointpath);
137         if (ifs.good())
138         {
139             int value;
140             ifs >> value;
141 
142             /* expecting RPM set-point, not pwm% */
143             max = static_cast<float>(value);
144         }
145     }
146     catch (const std::exception& e)
147     {
148         /* This exception is uninteresting. */
149         std::cerr << "Unable to read from '" << setpointpath << "'\n";
150     }
151 #endif
152 
153     _maximumRPMSetPt = max;
154     return;
155 }
156 
157 #ifdef __TUNING_LOGGING__
158 void PIDZone::initializeLog(void)
159 {
160     /* Print header for log file:
161      * epoch_ms,setpt,fan1,fan2,fanN,sensor1,sensor2,sensorN,failsafe
162      */
163 
164     _log << "epoch_ms,setpt";
165 
166     for (auto& f : _fanInputs)
167     {
168         _log << "," << f;
169     }
170     for (auto& t : _thermalInputs)
171     {
172         _log << "," << t;
173     }
174     _log << ",failsafe";
175     _log << std::endl;
176 
177     return;
178 }
179 
180 std::ofstream& PIDZone::getLogHandle(void)
181 {
182     return _log;
183 }
184 #endif
185 
186 /*
187  * TODO(venture) This is effectively updating the cache and should check if the
188  * values they're using to update it are new or old, or whatnot.  For instance,
189  * if we haven't heard from the host in X time we need to detect this failure.
190  *
191  * I haven't decided if the Sensor should have a lastUpdated method or whether
192  * that should be for the ReadInterface or etc...
193  */
194 
195 /**
196  * We want the PID loop to run with values cached, so this will get all the
197  * fan tachs for the loop.
198  */
199 void PIDZone::updateFanTelemetry(void)
200 {
201     /* TODO(venture): Should I just make _log point to /dev/null when logging
202      * is disabled?  I think it's a waste to try and log things even if the
203      * data is just being dropped though.
204      */
205 #ifdef __TUNING_LOGGING__
206     tstamp now = std::chrono::high_resolution_clock::now();
207     _log << std::chrono::duration_cast<std::chrono::milliseconds>(
208                 now.time_since_epoch())
209                 .count();
210     _log << "," << _maximumRPMSetPt;
211 #endif
212 
213     for (auto& f : _fanInputs)
214     {
215         auto sensor = _mgr.getSensor(f);
216         ReadReturn r = sensor->read();
217         _cachedValuesByName[f] = r.value;
218 
219         /*
220          * TODO(venture): We should check when these were last read.
221          * However, these are the fans, so if I'm not getting updated values
222          * for them... what should I do?
223          */
224 #ifdef __TUNING_LOGGING__
225         _log << "," << r.value;
226 #endif
227     }
228 
229 #ifdef __TUNING_LOGGING__
230     for (auto& t : _thermalInputs)
231     {
232         _log << "," << _cachedValuesByName[t];
233     }
234 #endif
235 
236     return;
237 }
238 
239 void PIDZone::updateSensors(void)
240 {
241     using namespace std::chrono;
242     /* margin and temp are stored as temp */
243     tstamp now = high_resolution_clock::now();
244 
245     for (auto& t : _thermalInputs)
246     {
247         auto sensor = _mgr.getSensor(t);
248         ReadReturn r = sensor->read();
249         int64_t timeout = sensor->GetTimeout();
250 
251         _cachedValuesByName[t] = r.value;
252         tstamp then = r.updated;
253 
254         /* Only go into failsafe if the timeout is set for
255          * the sensor.
256          */
257         if (timeout > 0)
258         {
259             auto duration =
260                 duration_cast<std::chrono::seconds>(now - then).count();
261             auto period = std::chrono::seconds(timeout).count();
262             if (duration >= period)
263             {
264                 // std::cerr << "Entering fail safe mode.\n";
265                 _failSafeSensors.insert(t);
266             }
267             else
268             {
269                 // Check if it's in there: remove it.
270                 auto kt = _failSafeSensors.find(t);
271                 if (kt != _failSafeSensors.end())
272                 {
273                     _failSafeSensors.erase(kt);
274                 }
275             }
276         }
277     }
278 
279     return;
280 }
281 
282 void PIDZone::initializeCache(void)
283 {
284     for (auto& f : _fanInputs)
285     {
286         _cachedValuesByName[f] = 0;
287     }
288 
289     for (auto& t : _thermalInputs)
290     {
291         _cachedValuesByName[t] = 0;
292 
293         // Start all sensors in fail-safe mode.
294         _failSafeSensors.insert(t);
295     }
296 }
297 
298 void PIDZone::dumpCache(void)
299 {
300     std::cerr << "Cache values now: \n";
301     for (auto& k : _cachedValuesByName)
302     {
303         std::cerr << k.first << ": " << k.second << "\n";
304     }
305 }
306 
307 void PIDZone::process_fans(void)
308 {
309     for (auto& p : _fans)
310     {
311         p->process();
312     }
313 }
314 
315 void PIDZone::process_thermals(void)
316 {
317     for (auto& p : _thermals)
318     {
319         p->process();
320     }
321 }
322 
323 Sensor* PIDZone::getSensor(std::string name)
324 {
325     return _mgr.getSensor(name);
326 }
327 
328 bool PIDZone::manual(bool value)
329 {
330     std::cerr << "manual: " << value << std::endl;
331     setManualMode(value);
332     return ModeObject::manual(value);
333 }
334 
335 bool PIDZone::failSafe() const
336 {
337     return getFailSafeMode();
338 }
339