xref: /openbmc/phosphor-pid-control/pid/zone.cpp (revision 07c3a80e)
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         int value;
136         std::ifstream ifs;
137         ifs.open(setpointpath);
138         if (ifs.good())
139         {
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>(
206                 now.time_since_epoch())
207                 .count();
208     _log << "," << _maximumRPMSetPt;
209 #endif
210 
211     for (auto& f : _fanInputs)
212     {
213         auto sensor = _mgr.getSensor(f);
214         ReadReturn r = sensor->read();
215         _cachedValuesByName[f] = r.value;
216 
217         /*
218          * TODO(venture): We should check when these were last read.
219          * However, these are the fans, so if I'm not getting updated values
220          * for them... what should I do?
221          */
222 #ifdef __TUNING_LOGGING__
223         _log << "," << r.value;
224 #endif
225     }
226 
227 #ifdef __TUNING_LOGGING__
228     for (auto& t : _thermalInputs)
229     {
230         _log << "," << _cachedValuesByName[t];
231     }
232 #endif
233 
234     return;
235 }
236 
237 void PIDZone::updateSensors(void)
238 {
239     using namespace std::chrono;
240     /* margin and temp are stored as temp */
241     tstamp now = high_resolution_clock::now();
242 
243     for (auto& t : _thermalInputs)
244     {
245         auto sensor = _mgr.getSensor(t);
246         ReadReturn r = sensor->read();
247         int64_t timeout = sensor->GetTimeout();
248 
249         _cachedValuesByName[t] = r.value;
250         tstamp then = r.updated;
251 
252         /* Only go into failsafe if the timeout is set for
253          * the sensor.
254          */
255         if (timeout > 0)
256         {
257             auto duration =
258                 duration_cast<std::chrono::seconds>(now - then).count();
259             auto period = std::chrono::seconds(timeout).count();
260             if (duration >= period)
261             {
262                 // std::cerr << "Entering fail safe mode.\n";
263                 _failSafeSensors.insert(t);
264             }
265             else
266             {
267                 // Check if it's in there: remove it.
268                 auto kt = _failSafeSensors.find(t);
269                 if (kt != _failSafeSensors.end())
270                 {
271                     _failSafeSensors.erase(kt);
272                 }
273             }
274         }
275     }
276 
277     return;
278 }
279 
280 void PIDZone::initializeCache(void)
281 {
282     for (auto& f : _fanInputs)
283     {
284         _cachedValuesByName[f] = 0;
285     }
286 
287     for (auto& t : _thermalInputs)
288     {
289         _cachedValuesByName[t] = 0;
290 
291         // Start all sensors in fail-safe mode.
292         _failSafeSensors.insert(t);
293     }
294 }
295 
296 void PIDZone::dumpCache(void)
297 {
298     std::cerr << "Cache values now: \n";
299     for (auto& k : _cachedValuesByName)
300     {
301         std::cerr << k.first << ": " << k.second << "\n";
302     }
303 }
304 
305 void PIDZone::process_fans(void)
306 {
307     for (auto& p : _fans)
308     {
309         p->process();
310     }
311 }
312 
313 void PIDZone::process_thermals(void)
314 {
315     for (auto& p : _thermals)
316     {
317         p->process();
318     }
319 }
320 
321 Sensor* PIDZone::getSensor(std::string name)
322 {
323     return _mgr.getSensor(name);
324 }
325 
326 bool PIDZone::manual(bool value)
327 {
328     std::cerr << "manual: " << value << std::endl;
329     setManualMode(value);
330     return ModeObject::manual(value);
331 }
332 
333 bool PIDZone::failSafe() const
334 {
335     return getFailSafeMode();
336 }
337