blob: d5e26469c053963b44ea72bde1260c4ddec013d5 [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
166 for (auto& f : _fanInputs)
167 {
168 _log << "," << f;
169 }
Patrick Venture5f02ad22018-04-24 10:18:40 -0700170 for (auto& t : _thermalInputs)
171 {
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
213 for (auto& f : _fanInputs)
214 {
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__
230 for (auto& t : _thermalInputs)
231 {
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
245 for (auto& t : _thermalInputs)
246 {
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();
249 int64_t timeout = sensor->GetTimeout();
250
251 _cachedValuesByName[t] = r.value;
252 tstamp then = r.updated;
253
254 /* Only go into failsafe if the timeout is set for
255 * the sensor.
256 */
257 if (timeout > 0)
258 {
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700259 auto duration =
260 duration_cast<std::chrono::seconds>(now - then).count();
Patrick Ventured8012182018-03-08 08:21:38 -0800261 auto period = std::chrono::seconds(timeout).count();
262 if (duration >= period)
263 {
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700264 // std::cerr << "Entering fail safe mode.\n";
Patrick Ventured8012182018-03-08 08:21:38 -0800265 _failSafeSensors.insert(t);
266 }
267 else
268 {
269 // Check if it's in there: remove it.
270 auto kt = _failSafeSensors.find(t);
271 if (kt != _failSafeSensors.end())
272 {
273 _failSafeSensors.erase(kt);
274 }
275 }
276 }
277 }
278
279 return;
280}
281
282void PIDZone::initializeCache(void)
283{
284 for (auto& f : _fanInputs)
285 {
286 _cachedValuesByName[f] = 0;
287 }
288
289 for (auto& t : _thermalInputs)
290 {
291 _cachedValuesByName[t] = 0;
292
293 // Start all sensors in fail-safe mode.
294 _failSafeSensors.insert(t);
295 }
296}
297
298void PIDZone::dumpCache(void)
299{
300 std::cerr << "Cache values now: \n";
301 for (auto& k : _cachedValuesByName)
302 {
303 std::cerr << k.first << ": " << k.second << "\n";
304 }
305}
306
307void PIDZone::process_fans(void)
308{
309 for (auto& p : _fans)
310 {
James Feist22c257a2018-08-31 14:07:12 -0700311 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800312 }
313}
314
315void PIDZone::process_thermals(void)
316{
317 for (auto& p : _thermals)
318 {
James Feist22c257a2018-08-31 14:07:12 -0700319 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800320 }
321}
322
Patrick Venturea58197c2018-06-11 15:29:45 -0700323Sensor* PIDZone::getSensor(std::string name)
Patrick Ventured8012182018-03-08 08:21:38 -0800324{
Patrick Venturefe75b192018-06-08 11:19:43 -0700325 return _mgr.getSensor(name);
Patrick Ventured8012182018-03-08 08:21:38 -0800326}
327
328bool PIDZone::manual(bool value)
329{
330 std::cerr << "manual: " << value << std::endl;
331 setManualMode(value);
332 return ModeObject::manual(value);
333}
334
335bool PIDZone::failSafe() const
336{
337 return getFailSafeMode();
338}