blob: 2af330718da3ea36a307bc0c84ffc06b3ceb681c [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>
32#include <libconfig.h++>
33#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 Ventured8012182018-03-08 08:21:38 -080038float PIDZone::getMaxRPMRequest(void) const
39{
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
59int64_t PIDZone::getZoneId(void) const
60{
61 return _zoneId;
62}
63
64void PIDZone::addRPMSetPoint(float setpoint)
65{
66 _RPMSetPoints.push_back(setpoint);
67}
68
69void PIDZone::clearRPMSetPoints(void)
70{
71 _RPMSetPoints.clear();
72}
73
74float PIDZone::getFailSafePercent(void) const
75{
76 return _failSafePercent;
77}
78
79float PIDZone::getMinThermalRpmSetPt(void) const
80{
81 return _minThermalRpmSetPt;
82}
83
James Feist22c257a2018-08-31 14:07:12 -070084void PIDZone::addFanPID(std::unique_ptr<Controller> pid)
Patrick Ventured8012182018-03-08 08:21:38 -080085{
86 _fans.push_back(std::move(pid));
87}
88
James Feist22c257a2018-08-31 14:07:12 -070089void PIDZone::addThermalPID(std::unique_ptr<Controller> pid)
Patrick Ventured8012182018-03-08 08:21:38 -080090{
91 _thermals.push_back(std::move(pid));
92}
93
94double PIDZone::getCachedValue(const std::string& name)
95{
96 return _cachedValuesByName.at(name);
97}
98
99void PIDZone::addFanInput(std::string fan)
100{
101 _fanInputs.push_back(fan);
102}
103
104void PIDZone::addThermalInput(std::string therm)
105{
106 _thermalInputs.push_back(therm);
107}
108
109void PIDZone::determineMaxRPMRequest(void)
110{
111 float max = 0;
112 std::vector<float>::iterator result;
113
114 if (_RPMSetPoints.size() > 0)
115 {
116 result = std::max_element(_RPMSetPoints.begin(), _RPMSetPoints.end());
117 max = *result;
118 }
119
120 /*
121 * If the maximum RPM set-point output is below the minimum RPM
122 * set-point, set it to the minimum.
123 */
124 max = std::max(getMinThermalRpmSetPt(), max);
125
126#ifdef __TUNING_LOGGING__
127 /*
128 * We received no set-points from thermal sensors.
129 * This is a case experienced during tuning where they only specify
130 * fan sensors and one large fan PID for all the fans.
131 */
132 static constexpr auto setpointpath = "/etc/thermal.d/set-point";
133 try
134 {
Patrick Ventured8012182018-03-08 08:21:38 -0800135 std::ifstream ifs;
136 ifs.open(setpointpath);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700137 if (ifs.good())
138 {
Patrick Venturedf766f22018-10-13 09:30:58 -0700139 int value;
Patrick Ventured8012182018-03-08 08:21:38 -0800140 ifs >> value;
Patrick Venturedf766f22018-10-13 09:30:58 -0700141
142 /* expecting RPM set-point, not pwm% */
143 max = static_cast<float>(value);
Patrick Ventured8012182018-03-08 08:21:38 -0800144 }
145 }
146 catch (const std::exception& e)
147 {
148 /* This exception is uninteresting. */
149 std::cerr << "Unable to read from '" << setpointpath << "'\n";
150 }
151#endif
152
153 _maximumRPMSetPt = max;
154 return;
155}
156
157#ifdef __TUNING_LOGGING__
158void PIDZone::initializeLog(void)
159{
Patrick Venture5f02ad22018-04-24 10:18:40 -0700160 /* Print header for log file:
161 * epoch_ms,setpt,fan1,fan2,fanN,sensor1,sensor2,sensorN,failsafe
162 */
Patrick Ventured8012182018-03-08 08:21:38 -0800163
164 _log << "epoch_ms,setpt";
165
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700166 for (const auto& f : _fanInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800167 {
168 _log << "," << f;
169 }
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700170 for (const auto& t : _thermalInputs)
Patrick Venture5f02ad22018-04-24 10:18:40 -0700171 {
172 _log << "," << t;
173 }
174 _log << ",failsafe";
Patrick Ventured8012182018-03-08 08:21:38 -0800175 _log << std::endl;
176
177 return;
178}
179
180std::ofstream& PIDZone::getLogHandle(void)
181{
182 return _log;
183}
184#endif
185
186/*
187 * TODO(venture) This is effectively updating the cache and should check if the
188 * values they're using to update it are new or old, or whatnot. For instance,
189 * if we haven't heard from the host in X time we need to detect this failure.
190 *
191 * I haven't decided if the Sensor should have a lastUpdated method or whether
192 * that should be for the ReadInterface or etc...
193 */
194
195/**
196 * We want the PID loop to run with values cached, so this will get all the
197 * fan tachs for the loop.
198 */
199void PIDZone::updateFanTelemetry(void)
200{
201 /* TODO(venture): Should I just make _log point to /dev/null when logging
202 * is disabled? I think it's a waste to try and log things even if the
203 * data is just being dropped though.
204 */
205#ifdef __TUNING_LOGGING__
206 tstamp now = std::chrono::high_resolution_clock::now();
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700207 _log << std::chrono::duration_cast<std::chrono::milliseconds>(
208 now.time_since_epoch())
209 .count();
Patrick Ventured8012182018-03-08 08:21:38 -0800210 _log << "," << _maximumRPMSetPt;
211#endif
212
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700213 for (const auto& f : _fanInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800214 {
Patrick Venturea58197c2018-06-11 15:29:45 -0700215 auto sensor = _mgr.getSensor(f);
Patrick Ventured8012182018-03-08 08:21:38 -0800216 ReadReturn r = sensor->read();
217 _cachedValuesByName[f] = r.value;
218
219 /*
220 * TODO(venture): We should check when these were last read.
221 * However, these are the fans, so if I'm not getting updated values
222 * for them... what should I do?
223 */
224#ifdef __TUNING_LOGGING__
225 _log << "," << r.value;
226#endif
227 }
228
Patrick Venture5f02ad22018-04-24 10:18:40 -0700229#ifdef __TUNING_LOGGING__
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700230 for (const auto& t : _thermalInputs)
Patrick Venture5f02ad22018-04-24 10:18:40 -0700231 {
232 _log << "," << _cachedValuesByName[t];
233 }
234#endif
235
Patrick Ventured8012182018-03-08 08:21:38 -0800236 return;
237}
238
239void PIDZone::updateSensors(void)
240{
241 using namespace std::chrono;
242 /* margin and temp are stored as temp */
243 tstamp now = high_resolution_clock::now();
244
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700245 for (const auto& t : _thermalInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800246 {
Patrick Venturea58197c2018-06-11 15:29:45 -0700247 auto sensor = _mgr.getSensor(t);
Patrick Ventured8012182018-03-08 08:21:38 -0800248 ReadReturn r = sensor->read();
Patrick Venture563a3562018-10-30 09:31:26 -0700249 int64_t timeout = sensor->getTimeout();
Patrick Ventured8012182018-03-08 08:21:38 -0800250
251 _cachedValuesByName[t] = r.value;
252 tstamp then = r.updated;
253
James Feist36b7d8e2018-10-05 15:39:01 -0700254 if (sensor->getFailed())
255 {
256 _failSafeSensors.insert(t);
257 }
Patrick Ventured8012182018-03-08 08:21:38 -0800258 /* Only go into failsafe if the timeout is set for
259 * the sensor.
260 */
James Feist36b7d8e2018-10-05 15:39:01 -0700261 else if (timeout > 0)
Patrick Ventured8012182018-03-08 08:21:38 -0800262 {
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700263 auto duration =
264 duration_cast<std::chrono::seconds>(now - then).count();
Patrick Ventured8012182018-03-08 08:21:38 -0800265 auto period = std::chrono::seconds(timeout).count();
266 if (duration >= period)
267 {
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700268 // std::cerr << "Entering fail safe mode.\n";
Patrick Ventured8012182018-03-08 08:21:38 -0800269 _failSafeSensors.insert(t);
270 }
271 else
272 {
273 // Check if it's in there: remove it.
274 auto kt = _failSafeSensors.find(t);
275 if (kt != _failSafeSensors.end())
276 {
277 _failSafeSensors.erase(kt);
278 }
279 }
280 }
281 }
282
283 return;
284}
285
286void PIDZone::initializeCache(void)
287{
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700288 for (const auto& f : _fanInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800289 {
290 _cachedValuesByName[f] = 0;
291 }
292
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700293 for (const auto& t : _thermalInputs)
Patrick Ventured8012182018-03-08 08:21:38 -0800294 {
295 _cachedValuesByName[t] = 0;
296
297 // Start all sensors in fail-safe mode.
298 _failSafeSensors.insert(t);
299 }
300}
301
302void PIDZone::dumpCache(void)
303{
304 std::cerr << "Cache values now: \n";
Patrick Venture4a2dc4d2018-10-23 09:02:55 -0700305 for (const auto& k : _cachedValuesByName)
Patrick Ventured8012182018-03-08 08:21:38 -0800306 {
307 std::cerr << k.first << ": " << k.second << "\n";
308 }
309}
310
Patrick Venture563a3562018-10-30 09:31:26 -0700311void PIDZone::processFans(void)
Patrick Ventured8012182018-03-08 08:21:38 -0800312{
313 for (auto& p : _fans)
314 {
James Feist22c257a2018-08-31 14:07:12 -0700315 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800316 }
317}
318
Patrick Venture563a3562018-10-30 09:31:26 -0700319void PIDZone::processThermals(void)
Patrick Ventured8012182018-03-08 08:21:38 -0800320{
321 for (auto& p : _thermals)
322 {
James Feist22c257a2018-08-31 14:07:12 -0700323 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800324 }
325}
326
Patrick Venturea58197c2018-06-11 15:29:45 -0700327Sensor* PIDZone::getSensor(std::string name)
Patrick Ventured8012182018-03-08 08:21:38 -0800328{
Patrick Venturefe75b192018-06-08 11:19:43 -0700329 return _mgr.getSensor(name);
Patrick Ventured8012182018-03-08 08:21:38 -0800330}
331
332bool PIDZone::manual(bool value)
333{
334 std::cerr << "manual: " << value << std::endl;
335 setManualMode(value);
336 return ModeObject::manual(value);
337}
338
339bool PIDZone::failSafe() const
340{
341 return getFailSafeMode();
342}