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