blob: fc0b40b2349bc4724b46e92137b33b74d64f49ec [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
68void PIDZone::clearRPMSetPoints(void)
69{
70 _RPMSetPoints.clear();
71}
72
Patrick Venture5f59c0f2018-11-11 12:55:14 -080073double PIDZone::getFailSafePercent(void) const
Patrick Ventured8012182018-03-08 08:21:38 -080074{
75 return _failSafePercent;
76}
77
Patrick Venture5f59c0f2018-11-11 12:55:14 -080078double PIDZone::getMinThermalRPMSetpoint(void) const
Patrick Ventured8012182018-03-08 08:21:38 -080079{
80 return _minThermalRpmSetPt;
81}
82
James Feist22c257a2018-08-31 14:07:12 -070083void PIDZone::addFanPID(std::unique_ptr<Controller> pid)
Patrick Ventured8012182018-03-08 08:21:38 -080084{
85 _fans.push_back(std::move(pid));
86}
87
James Feist22c257a2018-08-31 14:07:12 -070088void PIDZone::addThermalPID(std::unique_ptr<Controller> pid)
Patrick Ventured8012182018-03-08 08:21:38 -080089{
90 _thermals.push_back(std::move(pid));
91}
92
93double PIDZone::getCachedValue(const std::string& name)
94{
95 return _cachedValuesByName.at(name);
96}
97
Patrick Venturec399f6f2018-10-30 19:24:47 -070098void PIDZone::addFanInput(const std::string& fan)
Patrick Ventured8012182018-03-08 08:21:38 -080099{
100 _fanInputs.push_back(fan);
101}
102
Patrick Venturec399f6f2018-10-30 19:24:47 -0700103void PIDZone::addThermalInput(const std::string& therm)
Patrick Ventured8012182018-03-08 08:21:38 -0800104{
105 _thermalInputs.push_back(therm);
106}
107
108void PIDZone::determineMaxRPMRequest(void)
109{
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800110 double max = 0;
111 std::vector<double>::iterator result;
Patrick Ventured8012182018-03-08 08:21:38 -0800112
113 if (_RPMSetPoints.size() > 0)
114 {
115 result = std::max_element(_RPMSetPoints.begin(), _RPMSetPoints.end());
116 max = *result;
117 }
118
119 /*
Patrick Venture7280e272019-02-11 10:45:32 -0800120 * If the maximum RPM setpoint output is below the minimum RPM
121 * setpoint, set it to the minimum.
Patrick Ventured8012182018-03-08 08:21:38 -0800122 */
Patrick Venturee6a7a2e2018-10-30 19:30:02 -0700123 max = std::max(getMinThermalRPMSetpoint(), max);
Patrick Ventured8012182018-03-08 08:21:38 -0800124
125#ifdef __TUNING_LOGGING__
126 /*
Patrick Venture7280e272019-02-11 10:45:32 -0800127 * We received no setpoints from thermal sensors.
Patrick Ventured8012182018-03-08 08:21:38 -0800128 * This is a case experienced during tuning where they only specify
129 * fan sensors and one large fan PID for all the fans.
130 */
Patrick Venture7280e272019-02-11 10:45:32 -0800131 static constexpr auto setpointpath = "/etc/thermal.d/setpoint";
Patrick Ventured8012182018-03-08 08:21:38 -0800132 try
133 {
Patrick Ventured8012182018-03-08 08:21:38 -0800134 std::ifstream ifs;
135 ifs.open(setpointpath);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700136 if (ifs.good())
137 {
Patrick Venturedf766f22018-10-13 09:30:58 -0700138 int value;
Patrick Ventured8012182018-03-08 08:21:38 -0800139 ifs >> value;
Patrick Venturedf766f22018-10-13 09:30:58 -0700140
Patrick Venture7280e272019-02-11 10:45:32 -0800141 /* expecting RPM setpoint, not pwm% */
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800142 max = static_cast<double>(value);
Patrick Ventured8012182018-03-08 08:21:38 -0800143 }
144 }
145 catch (const std::exception& e)
146 {
147 /* This exception is uninteresting. */
148 std::cerr << "Unable to read from '" << setpointpath << "'\n";
149 }
150#endif
151
152 _maximumRPMSetPt = max;
153 return;
154}
155
156#ifdef __TUNING_LOGGING__
157void PIDZone::initializeLog(void)
158{
Patrick Venture5f02ad22018-04-24 10:18:40 -0700159 /* Print header for log file:
160 * epoch_ms,setpt,fan1,fan2,fanN,sensor1,sensor2,sensorN,failsafe
161 */
Patrick Ventured8012182018-03-08 08:21:38 -0800162
163 _log << "epoch_ms,setpt";
164
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700165 for (const auto& f : _fanInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800166 {
167 _log << "," << f;
168 }
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700169 for (const auto& t : _thermalInputs)
Patrick Venture5f02ad22018-04-24 10:18:40 -0700170 {
171 _log << "," << t;
172 }
173 _log << ",failsafe";
Patrick Ventured8012182018-03-08 08:21:38 -0800174 _log << std::endl;
175
176 return;
177}
178
179std::ofstream& PIDZone::getLogHandle(void)
180{
181 return _log;
182}
183#endif
184
185/*
186 * TODO(venture) This is effectively updating the cache and should check if the
187 * values they're using to update it are new or old, or whatnot. For instance,
188 * if we haven't heard from the host in X time we need to detect this failure.
189 *
190 * I haven't decided if the Sensor should have a lastUpdated method or whether
191 * that should be for the ReadInterface or etc...
192 */
193
194/**
195 * We want the PID loop to run with values cached, so this will get all the
196 * fan tachs for the loop.
197 */
198void PIDZone::updateFanTelemetry(void)
199{
200 /* TODO(venture): Should I just make _log point to /dev/null when logging
201 * is disabled? I think it's a waste to try and log things even if the
202 * data is just being dropped though.
203 */
204#ifdef __TUNING_LOGGING__
205 tstamp now = std::chrono::high_resolution_clock::now();
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700206 _log << std::chrono::duration_cast<std::chrono::milliseconds>(
207 now.time_since_epoch())
208 .count();
Patrick Ventured8012182018-03-08 08:21:38 -0800209 _log << "," << _maximumRPMSetPt;
210#endif
211
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700212 for (const auto& f : _fanInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800213 {
Patrick Venturea58197c2018-06-11 15:29:45 -0700214 auto sensor = _mgr.getSensor(f);
Patrick Ventured8012182018-03-08 08:21:38 -0800215 ReadReturn r = sensor->read();
216 _cachedValuesByName[f] = r.value;
217
218 /*
219 * TODO(venture): We should check when these were last read.
220 * However, these are the fans, so if I'm not getting updated values
221 * for them... what should I do?
222 */
223#ifdef __TUNING_LOGGING__
224 _log << "," << r.value;
225#endif
226 }
227
Patrick Venture5f02ad22018-04-24 10:18:40 -0700228#ifdef __TUNING_LOGGING__
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700229 for (const auto& t : _thermalInputs)
Patrick Venture5f02ad22018-04-24 10:18:40 -0700230 {
231 _log << "," << _cachedValuesByName[t];
232 }
233#endif
234
Patrick Ventured8012182018-03-08 08:21:38 -0800235 return;
236}
237
238void PIDZone::updateSensors(void)
239{
240 using namespace std::chrono;
241 /* margin and temp are stored as temp */
242 tstamp now = high_resolution_clock::now();
243
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700244 for (const auto& t : _thermalInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800245 {
Patrick Venturea58197c2018-06-11 15:29:45 -0700246 auto sensor = _mgr.getSensor(t);
Patrick Ventured8012182018-03-08 08:21:38 -0800247 ReadReturn r = sensor->read();
Patrick Venture563a3562018-10-30 09:31:26 -0700248 int64_t timeout = sensor->getTimeout();
Patrick Ventured8012182018-03-08 08:21:38 -0800249
250 _cachedValuesByName[t] = r.value;
251 tstamp then = r.updated;
252
James Feist473d68d2019-02-25 09:19:13 -0800253 auto duration = duration_cast<std::chrono::seconds>(now - then).count();
254 auto period = std::chrono::seconds(timeout).count();
255
James Feist36b7d8e2018-10-05 15:39:01 -0700256 if (sensor->getFailed())
257 {
258 _failSafeSensors.insert(t);
259 }
James Feist473d68d2019-02-25 09:19:13 -0800260 else if (timeout != 0 && duration >= period)
Patrick Ventured8012182018-03-08 08:21:38 -0800261 {
James Feist473d68d2019-02-25 09:19:13 -0800262 // 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())
Patrick Ventured8012182018-03-08 08:21:38 -0800270 {
James Feist473d68d2019-02-25 09:19:13 -0800271 _failSafeSensors.erase(kt);
Patrick Ventured8012182018-03-08 08:21:38 -0800272 }
273 }
274 }
275
276 return;
277}
278
279void PIDZone::initializeCache(void)
280{
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700281 for (const auto& f : _fanInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800282 {
283 _cachedValuesByName[f] = 0;
284 }
285
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700286 for (const auto& t : _thermalInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800287 {
288 _cachedValuesByName[t] = 0;
289
290 // Start all sensors in fail-safe mode.
291 _failSafeSensors.insert(t);
292 }
293}
294
295void PIDZone::dumpCache(void)
296{
297 std::cerr << "Cache values now: \n";
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700298 for (const auto& k : _cachedValuesByName)
Patrick Ventured8012182018-03-08 08:21:38 -0800299 {
300 std::cerr << k.first << ": " << k.second << "\n";
301 }
302}
303
Patrick Venture563a3562018-10-30 09:31:26 -0700304void PIDZone::processFans(void)
Patrick Ventured8012182018-03-08 08:21:38 -0800305{
306 for (auto& p : _fans)
307 {
James Feist22c257a2018-08-31 14:07:12 -0700308 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800309 }
310}
311
Patrick Venture563a3562018-10-30 09:31:26 -0700312void PIDZone::processThermals(void)
Patrick Ventured8012182018-03-08 08:21:38 -0800313{
314 for (auto& p : _thermals)
315 {
James Feist22c257a2018-08-31 14:07:12 -0700316 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800317 }
318}
319
Patrick Venture2d8e7852018-10-30 19:14:07 -0700320Sensor* PIDZone::getSensor(const std::string& name)
Patrick Ventured8012182018-03-08 08:21:38 -0800321{
Patrick Venturefe75b192018-06-08 11:19:43 -0700322 return _mgr.getSensor(name);
Patrick Ventured8012182018-03-08 08:21:38 -0800323}
324
325bool PIDZone::manual(bool value)
326{
327 std::cerr << "manual: " << value << std::endl;
328 setManualMode(value);
329 return ModeObject::manual(value);
330}
331
332bool PIDZone::failSafe() const
333{
334 return getFailSafeMode();
335}