blob: 065e6d7a894d33352f988d152074cc56eb12f61d [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"
26
Patrick Ventured8012182018-03-08 08:21:38 -080027#include <algorithm>
28#include <chrono>
29#include <cstring>
30#include <fstream>
31#include <iostream>
Patrick Ventured8012182018-03-08 08:21:38 -080032#include <memory>
33
Patrick Ventured8012182018-03-08 08:21:38 -080034using tstamp = std::chrono::high_resolution_clock::time_point;
35using namespace std::literals::chrono_literals;
36
Patrick Venture5f59c0f2018-11-11 12:55:14 -080037double PIDZone::getMaxRPMRequest(void) const
Patrick Ventured8012182018-03-08 08:21:38 -080038{
39 return _maximumRPMSetPt;
40}
41
42bool PIDZone::getManualMode(void) const
43{
44 return _manualMode;
45}
46
47void PIDZone::setManualMode(bool mode)
48{
49 _manualMode = mode;
50}
51
52bool PIDZone::getFailSafeMode(void) const
53{
54 // If any keys are present at least one sensor is in fail safe mode.
55 return !_failSafeSensors.empty();
56}
57
Patrick Venture0bbeaf82018-10-30 18:50:31 -070058int64_t PIDZone::getZoneID(void) const
Patrick Ventured8012182018-03-08 08:21:38 -080059{
60 return _zoneId;
61}
62
Patrick Venture5f59c0f2018-11-11 12:55:14 -080063void PIDZone::addRPMSetPoint(double setpoint)
Patrick Ventured8012182018-03-08 08:21:38 -080064{
65 _RPMSetPoints.push_back(setpoint);
66}
67
James Feist608304d2019-02-25 10:01:42 -080068void PIDZone::addRPMCeiling(double ceiling)
69{
70 _RPMCeilings.push_back(ceiling);
71}
72
73void PIDZone::clearRPMCeilings(void)
74{
75 _RPMCeilings.clear();
76}
77
Patrick Ventured8012182018-03-08 08:21:38 -080078void PIDZone::clearRPMSetPoints(void)
79{
80 _RPMSetPoints.clear();
81}
82
Patrick Venture5f59c0f2018-11-11 12:55:14 -080083double PIDZone::getFailSafePercent(void) const
Patrick Ventured8012182018-03-08 08:21:38 -080084{
85 return _failSafePercent;
86}
87
Patrick Venture5f59c0f2018-11-11 12:55:14 -080088double PIDZone::getMinThermalRPMSetpoint(void) const
Patrick Ventured8012182018-03-08 08:21:38 -080089{
90 return _minThermalRpmSetPt;
91}
92
James Feist22c257a2018-08-31 14:07:12 -070093void PIDZone::addFanPID(std::unique_ptr<Controller> pid)
Patrick Ventured8012182018-03-08 08:21:38 -080094{
95 _fans.push_back(std::move(pid));
96}
97
James Feist22c257a2018-08-31 14:07:12 -070098void PIDZone::addThermalPID(std::unique_ptr<Controller> pid)
Patrick Ventured8012182018-03-08 08:21:38 -080099{
100 _thermals.push_back(std::move(pid));
101}
102
103double PIDZone::getCachedValue(const std::string& name)
104{
105 return _cachedValuesByName.at(name);
106}
107
Patrick Venturec399f6f2018-10-30 19:24:47 -0700108void PIDZone::addFanInput(const std::string& fan)
Patrick Ventured8012182018-03-08 08:21:38 -0800109{
110 _fanInputs.push_back(fan);
111}
112
Patrick Venturec399f6f2018-10-30 19:24:47 -0700113void PIDZone::addThermalInput(const std::string& therm)
Patrick Ventured8012182018-03-08 08:21:38 -0800114{
115 _thermalInputs.push_back(therm);
116}
117
118void PIDZone::determineMaxRPMRequest(void)
119{
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800120 double max = 0;
121 std::vector<double>::iterator result;
Patrick Ventured8012182018-03-08 08:21:38 -0800122
123 if (_RPMSetPoints.size() > 0)
124 {
125 result = std::max_element(_RPMSetPoints.begin(), _RPMSetPoints.end());
126 max = *result;
127 }
128
James Feist608304d2019-02-25 10:01:42 -0800129 if (_RPMCeilings.size() > 0)
130 {
131 result = std::min_element(_RPMCeilings.begin(), _RPMCeilings.end());
132 max = std::min(max, *result);
133 }
134
Patrick Ventured8012182018-03-08 08:21:38 -0800135 /*
Patrick Venture7280e272019-02-11 10:45:32 -0800136 * If the maximum RPM setpoint output is below the minimum RPM
137 * setpoint, set it to the minimum.
Patrick Ventured8012182018-03-08 08:21:38 -0800138 */
Patrick Venturee6a7a2e2018-10-30 19:30:02 -0700139 max = std::max(getMinThermalRPMSetpoint(), max);
Patrick Ventured8012182018-03-08 08:21:38 -0800140
141#ifdef __TUNING_LOGGING__
142 /*
Patrick Venture7280e272019-02-11 10:45:32 -0800143 * We received no setpoints from thermal sensors.
Patrick Ventured8012182018-03-08 08:21:38 -0800144 * This is a case experienced during tuning where they only specify
145 * fan sensors and one large fan PID for all the fans.
146 */
Patrick Venture7280e272019-02-11 10:45:32 -0800147 static constexpr auto setpointpath = "/etc/thermal.d/setpoint";
Patrick Ventured8012182018-03-08 08:21:38 -0800148 try
149 {
Patrick Ventured8012182018-03-08 08:21:38 -0800150 std::ifstream ifs;
151 ifs.open(setpointpath);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700152 if (ifs.good())
153 {
Patrick Venturedf766f22018-10-13 09:30:58 -0700154 int value;
Patrick Ventured8012182018-03-08 08:21:38 -0800155 ifs >> value;
Patrick Venturedf766f22018-10-13 09:30:58 -0700156
Patrick Venture7280e272019-02-11 10:45:32 -0800157 /* expecting RPM setpoint, not pwm% */
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800158 max = static_cast<double>(value);
Patrick Ventured8012182018-03-08 08:21:38 -0800159 }
160 }
161 catch (const std::exception& e)
162 {
163 /* This exception is uninteresting. */
164 std::cerr << "Unable to read from '" << setpointpath << "'\n";
165 }
166#endif
167
168 _maximumRPMSetPt = max;
169 return;
170}
171
172#ifdef __TUNING_LOGGING__
173void PIDZone::initializeLog(void)
174{
Patrick Venture5f02ad22018-04-24 10:18:40 -0700175 /* Print header for log file:
176 * epoch_ms,setpt,fan1,fan2,fanN,sensor1,sensor2,sensorN,failsafe
177 */
Patrick Ventured8012182018-03-08 08:21:38 -0800178
179 _log << "epoch_ms,setpt";
180
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700181 for (const auto& f : _fanInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800182 {
183 _log << "," << f;
184 }
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700185 for (const auto& t : _thermalInputs)
Patrick Venture5f02ad22018-04-24 10:18:40 -0700186 {
187 _log << "," << t;
188 }
189 _log << ",failsafe";
Patrick Ventured8012182018-03-08 08:21:38 -0800190 _log << std::endl;
191
192 return;
193}
194
195std::ofstream& PIDZone::getLogHandle(void)
196{
197 return _log;
198}
199#endif
200
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 */
220#ifdef __TUNING_LOGGING__
221 tstamp now = std::chrono::high_resolution_clock::now();
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700222 _log << std::chrono::duration_cast<std::chrono::milliseconds>(
223 now.time_since_epoch())
224 .count();
Patrick Ventured8012182018-03-08 08:21:38 -0800225 _log << "," << _maximumRPMSetPt;
226#endif
227
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700228 for (const auto& f : _fanInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800229 {
Patrick Venturea58197c2018-06-11 15:29:45 -0700230 auto sensor = _mgr.getSensor(f);
Patrick Ventured8012182018-03-08 08:21:38 -0800231 ReadReturn r = sensor->read();
232 _cachedValuesByName[f] = r.value;
233
234 /*
235 * TODO(venture): We should check when these were last read.
236 * However, these are the fans, so if I'm not getting updated values
237 * for them... what should I do?
238 */
239#ifdef __TUNING_LOGGING__
240 _log << "," << r.value;
241#endif
242 }
243
Patrick Venture5f02ad22018-04-24 10:18:40 -0700244#ifdef __TUNING_LOGGING__
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700245 for (const auto& t : _thermalInputs)
Patrick Venture5f02ad22018-04-24 10:18:40 -0700246 {
247 _log << "," << _cachedValuesByName[t];
248 }
249#endif
250
Patrick Ventured8012182018-03-08 08:21:38 -0800251 return;
252}
253
254void PIDZone::updateSensors(void)
255{
256 using namespace std::chrono;
257 /* margin and temp are stored as temp */
258 tstamp now = high_resolution_clock::now();
259
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700260 for (const auto& t : _thermalInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800261 {
Patrick Venturea58197c2018-06-11 15:29:45 -0700262 auto sensor = _mgr.getSensor(t);
Patrick Ventured8012182018-03-08 08:21:38 -0800263 ReadReturn r = sensor->read();
Patrick Venture563a3562018-10-30 09:31:26 -0700264 int64_t timeout = sensor->getTimeout();
Patrick Ventured8012182018-03-08 08:21:38 -0800265
266 _cachedValuesByName[t] = r.value;
267 tstamp then = r.updated;
268
James Feist473d68d2019-02-25 09:19:13 -0800269 auto duration = duration_cast<std::chrono::seconds>(now - then).count();
270 auto period = std::chrono::seconds(timeout).count();
271
James Feist36b7d8e2018-10-05 15:39:01 -0700272 if (sensor->getFailed())
273 {
274 _failSafeSensors.insert(t);
275 }
James Feist473d68d2019-02-25 09:19:13 -0800276 else if (timeout != 0 && duration >= period)
Patrick Ventured8012182018-03-08 08:21:38 -0800277 {
James Feist473d68d2019-02-25 09:19:13 -0800278 // std::cerr << "Entering fail safe mode.\n";
279 _failSafeSensors.insert(t);
280 }
281 else
282 {
283 // Check if it's in there: remove it.
284 auto kt = _failSafeSensors.find(t);
285 if (kt != _failSafeSensors.end())
Patrick Ventured8012182018-03-08 08:21:38 -0800286 {
James Feist473d68d2019-02-25 09:19:13 -0800287 _failSafeSensors.erase(kt);
Patrick Ventured8012182018-03-08 08:21:38 -0800288 }
289 }
290 }
291
292 return;
293}
294
295void PIDZone::initializeCache(void)
296{
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700297 for (const auto& f : _fanInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800298 {
299 _cachedValuesByName[f] = 0;
300 }
301
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700302 for (const auto& t : _thermalInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800303 {
304 _cachedValuesByName[t] = 0;
305
306 // Start all sensors in fail-safe mode.
307 _failSafeSensors.insert(t);
308 }
309}
310
311void PIDZone::dumpCache(void)
312{
313 std::cerr << "Cache values now: \n";
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700314 for (const auto& k : _cachedValuesByName)
Patrick Ventured8012182018-03-08 08:21:38 -0800315 {
316 std::cerr << k.first << ": " << k.second << "\n";
317 }
318}
319
Patrick Venture563a3562018-10-30 09:31:26 -0700320void PIDZone::processFans(void)
Patrick Ventured8012182018-03-08 08:21:38 -0800321{
322 for (auto& p : _fans)
323 {
James Feist22c257a2018-08-31 14:07:12 -0700324 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800325 }
326}
327
Patrick Venture563a3562018-10-30 09:31:26 -0700328void PIDZone::processThermals(void)
Patrick Ventured8012182018-03-08 08:21:38 -0800329{
330 for (auto& p : _thermals)
331 {
James Feist22c257a2018-08-31 14:07:12 -0700332 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800333 }
334}
335
Patrick Venture2d8e7852018-10-30 19:14:07 -0700336Sensor* PIDZone::getSensor(const std::string& name)
Patrick Ventured8012182018-03-08 08:21:38 -0800337{
Patrick Venturefe75b192018-06-08 11:19:43 -0700338 return _mgr.getSensor(name);
Patrick Ventured8012182018-03-08 08:21:38 -0800339}
340
341bool PIDZone::manual(bool value)
342{
343 std::cerr << "manual: " << value << std::endl;
344 setManualMode(value);
345 return ModeObject::manual(value);
346}
347
348bool PIDZone::failSafe() const
349{
350 return getFailSafeMode();
351}