blob: e206e17976310fa4209dbf2b937268a182cb70ba [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. */
Patrick Ventured8012182018-03-08 08:21:38 -080018#include "zone.hpp"
19
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070020#include "conf.hpp"
21#include "pid/controller.hpp"
22#include "pid/ec/pid.hpp"
23#include "pid/fancontroller.hpp"
James Feist22c257a2018-08-31 14:07:12 -070024#include "pid/stepwisecontroller.hpp"
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070025#include "pid/thermalcontroller.hpp"
Patrick Venturec32e3fc2019-02-28 10:01:11 -080026#include "pid/tuning.hpp"
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070027
Patrick Ventured8012182018-03-08 08:21:38 -080028#include <algorithm>
29#include <chrono>
30#include <cstring>
31#include <fstream>
32#include <iostream>
Patrick Ventured8012182018-03-08 08:21:38 -080033#include <memory>
34
Patrick Ventured8012182018-03-08 08:21:38 -080035using tstamp = std::chrono::high_resolution_clock::time_point;
36using namespace std::literals::chrono_literals;
37
Patrick Venture5f59c0f2018-11-11 12:55:14 -080038double PIDZone::getMaxRPMRequest(void) const
Patrick Ventured8012182018-03-08 08:21:38 -080039{
40 return _maximumRPMSetPt;
41}
42
43bool PIDZone::getManualMode(void) const
44{
45 return _manualMode;
46}
47
48void PIDZone::setManualMode(bool mode)
49{
50 _manualMode = mode;
51}
52
53bool 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
Patrick Venture0bbeaf82018-10-30 18:50:31 -070059int64_t PIDZone::getZoneID(void) const
Patrick Ventured8012182018-03-08 08:21:38 -080060{
61 return _zoneId;
62}
63
Patrick Venture5f59c0f2018-11-11 12:55:14 -080064void PIDZone::addRPMSetPoint(double setpoint)
Patrick Ventured8012182018-03-08 08:21:38 -080065{
66 _RPMSetPoints.push_back(setpoint);
67}
68
James Feist608304d2019-02-25 10:01:42 -080069void PIDZone::addRPMCeiling(double ceiling)
70{
71 _RPMCeilings.push_back(ceiling);
72}
73
74void PIDZone::clearRPMCeilings(void)
75{
76 _RPMCeilings.clear();
77}
78
Patrick Ventured8012182018-03-08 08:21:38 -080079void PIDZone::clearRPMSetPoints(void)
80{
81 _RPMSetPoints.clear();
82}
83
Patrick Venture5f59c0f2018-11-11 12:55:14 -080084double PIDZone::getFailSafePercent(void) const
Patrick Ventured8012182018-03-08 08:21:38 -080085{
86 return _failSafePercent;
87}
88
Patrick Venture5f59c0f2018-11-11 12:55:14 -080089double PIDZone::getMinThermalRPMSetpoint(void) const
Patrick Ventured8012182018-03-08 08:21:38 -080090{
James Feist3484bed2019-02-25 13:28:18 -080091 return _minThermalOutputSetPt;
Patrick Ventured8012182018-03-08 08:21:38 -080092}
93
James Feist22c257a2018-08-31 14:07:12 -070094void PIDZone::addFanPID(std::unique_ptr<Controller> pid)
Patrick Ventured8012182018-03-08 08:21:38 -080095{
96 _fans.push_back(std::move(pid));
97}
98
James Feist22c257a2018-08-31 14:07:12 -070099void PIDZone::addThermalPID(std::unique_ptr<Controller> pid)
Patrick Ventured8012182018-03-08 08:21:38 -0800100{
101 _thermals.push_back(std::move(pid));
102}
103
104double PIDZone::getCachedValue(const std::string& name)
105{
106 return _cachedValuesByName.at(name);
107}
108
Patrick Venturec399f6f2018-10-30 19:24:47 -0700109void PIDZone::addFanInput(const std::string& fan)
Patrick Ventured8012182018-03-08 08:21:38 -0800110{
111 _fanInputs.push_back(fan);
112}
113
Patrick Venturec399f6f2018-10-30 19:24:47 -0700114void PIDZone::addThermalInput(const std::string& therm)
Patrick Ventured8012182018-03-08 08:21:38 -0800115{
116 _thermalInputs.push_back(therm);
117}
118
119void PIDZone::determineMaxRPMRequest(void)
120{
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800121 double max = 0;
122 std::vector<double>::iterator result;
Patrick Ventured8012182018-03-08 08:21:38 -0800123
124 if (_RPMSetPoints.size() > 0)
125 {
126 result = std::max_element(_RPMSetPoints.begin(), _RPMSetPoints.end());
127 max = *result;
128 }
129
James Feist608304d2019-02-25 10:01:42 -0800130 if (_RPMCeilings.size() > 0)
131 {
132 result = std::min_element(_RPMCeilings.begin(), _RPMCeilings.end());
133 max = std::min(max, *result);
134 }
135
Patrick Ventured8012182018-03-08 08:21:38 -0800136 /*
Patrick Venture7280e272019-02-11 10:45:32 -0800137 * If the maximum RPM setpoint output is below the minimum RPM
138 * setpoint, set it to the minimum.
Patrick Ventured8012182018-03-08 08:21:38 -0800139 */
Patrick Venturee6a7a2e2018-10-30 19:30:02 -0700140 max = std::max(getMinThermalRPMSetpoint(), max);
Patrick Ventured8012182018-03-08 08:21:38 -0800141
Patrick Venturede79ee02019-05-08 14:50:00 -0700142 if (tuningEnabled)
Patrick Ventured8012182018-03-08 08:21:38 -0800143 {
Patrick Venturec32e3fc2019-02-28 10:01:11 -0800144 /*
145 * We received no setpoints from thermal sensors.
146 * This is a case experienced during tuning where they only specify
147 * fan sensors and one large fan PID for all the fans.
148 */
149 static constexpr auto setpointpath = "/etc/thermal.d/setpoint";
150 try
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700151 {
Patrick Venturec32e3fc2019-02-28 10:01:11 -0800152 std::ifstream ifs;
153 ifs.open(setpointpath);
154 if (ifs.good())
155 {
156 int value;
157 ifs >> value;
Patrick Venturedf766f22018-10-13 09:30:58 -0700158
Patrick Venturec32e3fc2019-02-28 10:01:11 -0800159 /* expecting RPM setpoint, not pwm% */
160 max = static_cast<double>(value);
161 }
162 }
163 catch (const std::exception& e)
164 {
165 /* This exception is uninteresting. */
166 std::cerr << "Unable to read from '" << setpointpath << "'\n";
Patrick Ventured8012182018-03-08 08:21:38 -0800167 }
168 }
Patrick Ventured8012182018-03-08 08:21:38 -0800169
170 _maximumRPMSetPt = max;
171 return;
172}
173
Patrick Ventured8012182018-03-08 08:21:38 -0800174void PIDZone::initializeLog(void)
175{
Patrick Venture5f02ad22018-04-24 10:18:40 -0700176 /* Print header for log file:
177 * epoch_ms,setpt,fan1,fan2,fanN,sensor1,sensor2,sensorN,failsafe
178 */
Patrick Ventured8012182018-03-08 08:21:38 -0800179
180 _log << "epoch_ms,setpt";
181
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700182 for (const auto& f : _fanInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800183 {
184 _log << "," << f;
185 }
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700186 for (const auto& t : _thermalInputs)
Patrick Venture5f02ad22018-04-24 10:18:40 -0700187 {
188 _log << "," << t;
189 }
190 _log << ",failsafe";
Patrick Ventured8012182018-03-08 08:21:38 -0800191 _log << std::endl;
192
193 return;
194}
195
196std::ofstream& PIDZone::getLogHandle(void)
197{
198 return _log;
199}
Patrick Ventured8012182018-03-08 08:21:38 -0800200
201/*
202 * TODO(venture) This is effectively updating the cache and should check if the
203 * values they're using to update it are new or old, or whatnot. For instance,
204 * if we haven't heard from the host in X time we need to detect this failure.
205 *
206 * I haven't decided if the Sensor should have a lastUpdated method or whether
207 * that should be for the ReadInterface or etc...
208 */
209
210/**
211 * We want the PID loop to run with values cached, so this will get all the
212 * fan tachs for the loop.
213 */
214void PIDZone::updateFanTelemetry(void)
215{
216 /* TODO(venture): Should I just make _log point to /dev/null when logging
217 * is disabled? I think it's a waste to try and log things even if the
218 * data is just being dropped though.
219 */
Will Liangded0ab52019-05-15 17:10:06 +0800220 tstamp now = std::chrono::high_resolution_clock::now();
Patrick Venturede79ee02019-05-08 14:50:00 -0700221 if (loggingEnabled)
Patrick Venturec32e3fc2019-02-28 10:01:11 -0800222 {
Patrick Venturec32e3fc2019-02-28 10:01:11 -0800223 _log << std::chrono::duration_cast<std::chrono::milliseconds>(
224 now.time_since_epoch())
225 .count();
226 _log << "," << _maximumRPMSetPt;
227 }
Patrick Ventured8012182018-03-08 08:21:38 -0800228
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700229 for (const auto& f : _fanInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800230 {
Patrick Venturea58197c2018-06-11 15:29:45 -0700231 auto sensor = _mgr.getSensor(f);
Patrick Ventured8012182018-03-08 08:21:38 -0800232 ReadReturn r = sensor->read();
233 _cachedValuesByName[f] = r.value;
Will Liangded0ab52019-05-15 17:10:06 +0800234 int64_t timeout = sensor->getTimeout();
235 tstamp then = r.updated;
Patrick Ventured8012182018-03-08 08:21:38 -0800236
Will Liangded0ab52019-05-15 17:10:06 +0800237 auto duration =
238 std::chrono::duration_cast<std::chrono::seconds>(now - then)
239 .count();
240 auto period = std::chrono::seconds(timeout).count();
Patrick Ventured8012182018-03-08 08:21:38 -0800241 /*
242 * TODO(venture): We should check when these were last read.
243 * However, these are the fans, so if I'm not getting updated values
244 * for them... what should I do?
245 */
Patrick Venturede79ee02019-05-08 14:50:00 -0700246 if (loggingEnabled)
Patrick Venturec32e3fc2019-02-28 10:01:11 -0800247 {
248 _log << "," << r.value;
249 }
Will Liangded0ab52019-05-15 17:10:06 +0800250
251 // check if fan fail.
252 if (sensor->getFailed())
253 {
254 _failSafeSensors.insert(f);
255 }
256 else if (timeout != 0 && duration >= period)
257 {
258 _failSafeSensors.insert(f);
259 }
260 else
261 {
262 // Check if it's in there: remove it.
263 auto kt = _failSafeSensors.find(f);
264 if (kt != _failSafeSensors.end())
265 {
266 _failSafeSensors.erase(kt);
267 }
268 }
Patrick Ventured8012182018-03-08 08:21:38 -0800269 }
270
Patrick Venturede79ee02019-05-08 14:50:00 -0700271 if (loggingEnabled)
Patrick Venture5f02ad22018-04-24 10:18:40 -0700272 {
Patrick Venturec32e3fc2019-02-28 10:01:11 -0800273 for (const auto& t : _thermalInputs)
274 {
275 _log << "," << _cachedValuesByName[t];
276 }
Patrick Venture5f02ad22018-04-24 10:18:40 -0700277 }
Patrick Venture5f02ad22018-04-24 10:18:40 -0700278
Patrick Ventured8012182018-03-08 08:21:38 -0800279 return;
280}
281
282void PIDZone::updateSensors(void)
283{
284 using namespace std::chrono;
285 /* margin and temp are stored as temp */
286 tstamp now = high_resolution_clock::now();
287
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700288 for (const auto& t : _thermalInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800289 {
Patrick Venturea58197c2018-06-11 15:29:45 -0700290 auto sensor = _mgr.getSensor(t);
Patrick Ventured8012182018-03-08 08:21:38 -0800291 ReadReturn r = sensor->read();
Patrick Venture563a3562018-10-30 09:31:26 -0700292 int64_t timeout = sensor->getTimeout();
Patrick Ventured8012182018-03-08 08:21:38 -0800293
294 _cachedValuesByName[t] = r.value;
295 tstamp then = r.updated;
296
James Feist473d68d2019-02-25 09:19:13 -0800297 auto duration = duration_cast<std::chrono::seconds>(now - then).count();
298 auto period = std::chrono::seconds(timeout).count();
299
James Feist36b7d8e2018-10-05 15:39:01 -0700300 if (sensor->getFailed())
301 {
302 _failSafeSensors.insert(t);
303 }
James Feist473d68d2019-02-25 09:19:13 -0800304 else if (timeout != 0 && duration >= period)
Patrick Ventured8012182018-03-08 08:21:38 -0800305 {
James Feist473d68d2019-02-25 09:19:13 -0800306 // std::cerr << "Entering fail safe mode.\n";
307 _failSafeSensors.insert(t);
308 }
309 else
310 {
311 // Check if it's in there: remove it.
312 auto kt = _failSafeSensors.find(t);
313 if (kt != _failSafeSensors.end())
Patrick Ventured8012182018-03-08 08:21:38 -0800314 {
James Feist473d68d2019-02-25 09:19:13 -0800315 _failSafeSensors.erase(kt);
Patrick Ventured8012182018-03-08 08:21:38 -0800316 }
317 }
318 }
319
320 return;
321}
322
323void PIDZone::initializeCache(void)
324{
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700325 for (const auto& f : _fanInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800326 {
327 _cachedValuesByName[f] = 0;
Will Liangded0ab52019-05-15 17:10:06 +0800328
329 // Start all fans in fail-safe mode.
330 _failSafeSensors.insert(f);
Patrick Ventured8012182018-03-08 08:21:38 -0800331 }
332
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700333 for (const auto& t : _thermalInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800334 {
335 _cachedValuesByName[t] = 0;
336
337 // Start all sensors in fail-safe mode.
338 _failSafeSensors.insert(t);
339 }
340}
341
342void PIDZone::dumpCache(void)
343{
344 std::cerr << "Cache values now: \n";
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700345 for (const auto& k : _cachedValuesByName)
Patrick Ventured8012182018-03-08 08:21:38 -0800346 {
347 std::cerr << k.first << ": " << k.second << "\n";
348 }
349}
350
Patrick Venture563a3562018-10-30 09:31:26 -0700351void PIDZone::processFans(void)
Patrick Ventured8012182018-03-08 08:21:38 -0800352{
353 for (auto& p : _fans)
354 {
James Feist22c257a2018-08-31 14:07:12 -0700355 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800356 }
357}
358
Patrick Venture563a3562018-10-30 09:31:26 -0700359void PIDZone::processThermals(void)
Patrick Ventured8012182018-03-08 08:21:38 -0800360{
361 for (auto& p : _thermals)
362 {
James Feist22c257a2018-08-31 14:07:12 -0700363 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800364 }
365}
366
Patrick Venture2d8e7852018-10-30 19:14:07 -0700367Sensor* PIDZone::getSensor(const std::string& name)
Patrick Ventured8012182018-03-08 08:21:38 -0800368{
Patrick Venturefe75b192018-06-08 11:19:43 -0700369 return _mgr.getSensor(name);
Patrick Ventured8012182018-03-08 08:21:38 -0800370}
371
372bool PIDZone::manual(bool value)
373{
374 std::cerr << "manual: " << value << std::endl;
375 setManualMode(value);
376 return ModeObject::manual(value);
377}
378
379bool PIDZone::failSafe() const
380{
381 return getFailSafeMode();
382}