blob: 928aef5b8ffd25a8c99ba64c97a15938924d437b [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 Venturec32e3fc2019-02-28 10:01:11 -0800142 if (tuningLoggingEnabled)
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 */
Patrick Venturec32e3fc2019-02-28 10:01:11 -0800220 if (tuningLoggingEnabled)
221 {
222 tstamp now = std::chrono::high_resolution_clock::now();
223 _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;
234
235 /*
236 * TODO(venture): We should check when these were last read.
237 * However, these are the fans, so if I'm not getting updated values
238 * for them... what should I do?
239 */
Patrick Venturec32e3fc2019-02-28 10:01:11 -0800240 if (tuningLoggingEnabled)
241 {
242 _log << "," << r.value;
243 }
Patrick Ventured8012182018-03-08 08:21:38 -0800244 }
245
Patrick Venturec32e3fc2019-02-28 10:01:11 -0800246 if (tuningLoggingEnabled)
Patrick Venture5f02ad22018-04-24 10:18:40 -0700247 {
Patrick Venturec32e3fc2019-02-28 10:01:11 -0800248 for (const auto& t : _thermalInputs)
249 {
250 _log << "," << _cachedValuesByName[t];
251 }
Patrick Venture5f02ad22018-04-24 10:18:40 -0700252 }
Patrick Venture5f02ad22018-04-24 10:18:40 -0700253
Patrick Ventured8012182018-03-08 08:21:38 -0800254 return;
255}
256
257void PIDZone::updateSensors(void)
258{
259 using namespace std::chrono;
260 /* margin and temp are stored as temp */
261 tstamp now = high_resolution_clock::now();
262
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700263 for (const auto& t : _thermalInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800264 {
Patrick Venturea58197c2018-06-11 15:29:45 -0700265 auto sensor = _mgr.getSensor(t);
Patrick Ventured8012182018-03-08 08:21:38 -0800266 ReadReturn r = sensor->read();
Patrick Venture563a3562018-10-30 09:31:26 -0700267 int64_t timeout = sensor->getTimeout();
Patrick Ventured8012182018-03-08 08:21:38 -0800268
269 _cachedValuesByName[t] = r.value;
270 tstamp then = r.updated;
271
James Feist473d68d2019-02-25 09:19:13 -0800272 auto duration = duration_cast<std::chrono::seconds>(now - then).count();
273 auto period = std::chrono::seconds(timeout).count();
274
James Feist36b7d8e2018-10-05 15:39:01 -0700275 if (sensor->getFailed())
276 {
277 _failSafeSensors.insert(t);
278 }
James Feist473d68d2019-02-25 09:19:13 -0800279 else if (timeout != 0 && duration >= period)
Patrick Ventured8012182018-03-08 08:21:38 -0800280 {
James Feist473d68d2019-02-25 09:19:13 -0800281 // std::cerr << "Entering fail safe mode.\n";
282 _failSafeSensors.insert(t);
283 }
284 else
285 {
286 // Check if it's in there: remove it.
287 auto kt = _failSafeSensors.find(t);
288 if (kt != _failSafeSensors.end())
Patrick Ventured8012182018-03-08 08:21:38 -0800289 {
James Feist473d68d2019-02-25 09:19:13 -0800290 _failSafeSensors.erase(kt);
Patrick Ventured8012182018-03-08 08:21:38 -0800291 }
292 }
293 }
294
295 return;
296}
297
298void PIDZone::initializeCache(void)
299{
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700300 for (const auto& f : _fanInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800301 {
302 _cachedValuesByName[f] = 0;
303 }
304
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700305 for (const auto& t : _thermalInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800306 {
307 _cachedValuesByName[t] = 0;
308
309 // Start all sensors in fail-safe mode.
310 _failSafeSensors.insert(t);
311 }
312}
313
314void PIDZone::dumpCache(void)
315{
316 std::cerr << "Cache values now: \n";
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700317 for (const auto& k : _cachedValuesByName)
Patrick Ventured8012182018-03-08 08:21:38 -0800318 {
319 std::cerr << k.first << ": " << k.second << "\n";
320 }
321}
322
Patrick Venture563a3562018-10-30 09:31:26 -0700323void PIDZone::processFans(void)
Patrick Ventured8012182018-03-08 08:21:38 -0800324{
325 for (auto& p : _fans)
326 {
James Feist22c257a2018-08-31 14:07:12 -0700327 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800328 }
329}
330
Patrick Venture563a3562018-10-30 09:31:26 -0700331void PIDZone::processThermals(void)
Patrick Ventured8012182018-03-08 08:21:38 -0800332{
333 for (auto& p : _thermals)
334 {
James Feist22c257a2018-08-31 14:07:12 -0700335 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800336 }
337}
338
Patrick Venture2d8e7852018-10-30 19:14:07 -0700339Sensor* PIDZone::getSensor(const std::string& name)
Patrick Ventured8012182018-03-08 08:21:38 -0800340{
Patrick Venturefe75b192018-06-08 11:19:43 -0700341 return _mgr.getSensor(name);
Patrick Ventured8012182018-03-08 08:21:38 -0800342}
343
344bool PIDZone::manual(bool value)
345{
346 std::cerr << "manual: " << value << std::endl;
347 setManualMode(value);
348 return ModeObject::manual(value);
349}
350
351bool PIDZone::failSafe() const
352{
353 return getFailSafeMode();
354}