blob: 26bff6a2c49707afdc81797a37bbd8d9fe501d68 [file] [log] [blame]
Patrick Ventured8012182018-03-08 08:21:38 -08001/**
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
36using tstamp = std::chrono::high_resolution_clock::time_point;
37using namespace std::literals::chrono_literals;
38
Patrick Ventured8012182018-03-08 08:21:38 -080039float PIDZone::getMaxRPMRequest(void) const
40{
41 return _maximumRPMSetPt;
42}
43
44bool PIDZone::getManualMode(void) const
45{
46 return _manualMode;
47}
48
49void PIDZone::setManualMode(bool mode)
50{
51 _manualMode = mode;
52}
53
54bool 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
60int64_t PIDZone::getZoneId(void) const
61{
62 return _zoneId;
63}
64
65void PIDZone::addRPMSetPoint(float setpoint)
66{
67 _RPMSetPoints.push_back(setpoint);
68}
69
70void PIDZone::clearRPMSetPoints(void)
71{
72 _RPMSetPoints.clear();
73}
74
75float PIDZone::getFailSafePercent(void) const
76{
77 return _failSafePercent;
78}
79
80float PIDZone::getMinThermalRpmSetPt(void) const
81{
82 return _minThermalRpmSetPt;
83}
84
85void PIDZone::addFanPID(std::unique_ptr<PIDController> pid)
86{
87 _fans.push_back(std::move(pid));
88}
89
90void PIDZone::addThermalPID(std::unique_ptr<PIDController> pid)
91{
92 _thermals.push_back(std::move(pid));
93}
94
95double PIDZone::getCachedValue(const std::string& name)
96{
97 return _cachedValuesByName.at(name);
98}
99
100void PIDZone::addFanInput(std::string fan)
101{
102 _fanInputs.push_back(fan);
103}
104
105void PIDZone::addThermalInput(std::string therm)
106{
107 _thermalInputs.push_back(therm);
108}
109
110void 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__
156void PIDZone::initializeLog(void)
157{
Patrick Venture5f02ad22018-04-24 10:18:40 -0700158 /* Print header for log file:
159 * epoch_ms,setpt,fan1,fan2,fanN,sensor1,sensor2,sensorN,failsafe
160 */
Patrick Ventured8012182018-03-08 08:21:38 -0800161
162 _log << "epoch_ms,setpt";
163
164 for (auto& f : _fanInputs)
165 {
166 _log << "," << f;
167 }
Patrick Venture5f02ad22018-04-24 10:18:40 -0700168 for (auto& t : _thermalInputs)
169 {
170 _log << "," << t;
171 }
172 _log << ",failsafe";
Patrick Ventured8012182018-03-08 08:21:38 -0800173 _log << std::endl;
174
175 return;
176}
177
178std::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 */
197void 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 {
Patrick Venturefe75b192018-06-08 11:19:43 -0700211 auto& sensor = _mgr.getSensor(f);
Patrick Ventured8012182018-03-08 08:21:38 -0800212 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
Patrick Venture5f02ad22018-04-24 10:18:40 -0700225#ifdef __TUNING_LOGGING__
226 for (auto& t : _thermalInputs)
227 {
228 _log << "," << _cachedValuesByName[t];
229 }
230#endif
231
Patrick Ventured8012182018-03-08 08:21:38 -0800232 return;
233}
234
235void 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 {
Patrick Venturefe75b192018-06-08 11:19:43 -0700243 auto& sensor = _mgr.getSensor(t);
Patrick Ventured8012182018-03-08 08:21:38 -0800244 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
278void 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
294void 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
303void PIDZone::process_fans(void)
304{
305 for (auto& p : _fans)
306 {
307 p->pid_process();
308 }
309}
310
311void PIDZone::process_thermals(void)
312{
313 for (auto& p : _thermals)
314 {
315 p->pid_process();
316 }
317}
318
Patrick Venturefe75b192018-06-08 11:19:43 -0700319const std::unique_ptr<Sensor>& PIDZone::getSensor(std::string name)
Patrick Ventured8012182018-03-08 08:21:38 -0800320{
Patrick Venturefe75b192018-06-08 11:19:43 -0700321 return _mgr.getSensor(name);
Patrick Ventured8012182018-03-08 08:21:38 -0800322}
323
324bool PIDZone::manual(bool value)
325{
326 std::cerr << "manual: " << value << std::endl;
327 setManualMode(value);
328 return ModeObject::manual(value);
329}
330
331bool PIDZone::failSafe() const
332{
333 return getFailSafeMode();
334}