blob: 92a332e6daaed23effa83b410b57a89c86eb26f3 [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 {
135 int value;
136 std::ifstream ifs;
137 ifs.open(setpointpath);
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700138 if (ifs.good())
139 {
Patrick Ventured8012182018-03-08 08:21:38 -0800140 ifs >> value;
141 max = value; // expecting RPM set-point, not pwm%
142 }
143 }
144 catch (const std::exception& e)
145 {
146 /* This exception is uninteresting. */
147 std::cerr << "Unable to read from '" << setpointpath << "'\n";
148 }
149#endif
150
151 _maximumRPMSetPt = max;
152 return;
153}
154
155#ifdef __TUNING_LOGGING__
156void PIDZone::initializeLog(void)
157{
Patrick Venture5f02ad22018-04-24 10:18:40 -0700158 /* Print header for log file:
159 * epoch_ms,setpt,fan1,fan2,fanN,sensor1,sensor2,sensorN,failsafe
160 */
Patrick Ventured8012182018-03-08 08:21:38 -0800161
162 _log << "epoch_ms,setpt";
163
164 for (auto& f : _fanInputs)
165 {
166 _log << "," << f;
167 }
Patrick Venture5f02ad22018-04-24 10:18:40 -0700168 for (auto& t : _thermalInputs)
169 {
170 _log << "," << t;
171 }
172 _log << ",failsafe";
Patrick Ventured8012182018-03-08 08:21:38 -0800173 _log << std::endl;
174
175 return;
176}
177
178std::ofstream& PIDZone::getLogHandle(void)
179{
180 return _log;
181}
182#endif
183
184/*
185 * TODO(venture) This is effectively updating the cache and should check if the
186 * values they're using to update it are new or old, or whatnot. For instance,
187 * if we haven't heard from the host in X time we need to detect this failure.
188 *
189 * I haven't decided if the Sensor should have a lastUpdated method or whether
190 * that should be for the ReadInterface or etc...
191 */
192
193/**
194 * We want the PID loop to run with values cached, so this will get all the
195 * fan tachs for the loop.
196 */
197void PIDZone::updateFanTelemetry(void)
198{
199 /* TODO(venture): Should I just make _log point to /dev/null when logging
200 * is disabled? I think it's a waste to try and log things even if the
201 * data is just being dropped though.
202 */
203#ifdef __TUNING_LOGGING__
204 tstamp now = std::chrono::high_resolution_clock::now();
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700205 _log << std::chrono::duration_cast<std::chrono::milliseconds>(
206 now.time_since_epoch())
207 .count();
Patrick Ventured8012182018-03-08 08:21:38 -0800208 _log << "," << _maximumRPMSetPt;
209#endif
210
211 for (auto& f : _fanInputs)
212 {
Patrick Venturea58197c2018-06-11 15:29:45 -0700213 auto sensor = _mgr.getSensor(f);
Patrick Ventured8012182018-03-08 08:21:38 -0800214 ReadReturn r = sensor->read();
215 _cachedValuesByName[f] = r.value;
216
217 /*
218 * TODO(venture): We should check when these were last read.
219 * However, these are the fans, so if I'm not getting updated values
220 * for them... what should I do?
221 */
222#ifdef __TUNING_LOGGING__
223 _log << "," << r.value;
224#endif
225 }
226
Patrick Venture5f02ad22018-04-24 10:18:40 -0700227#ifdef __TUNING_LOGGING__
228 for (auto& t : _thermalInputs)
229 {
230 _log << "," << _cachedValuesByName[t];
231 }
232#endif
233
Patrick Ventured8012182018-03-08 08:21:38 -0800234 return;
235}
236
237void PIDZone::updateSensors(void)
238{
239 using namespace std::chrono;
240 /* margin and temp are stored as temp */
241 tstamp now = high_resolution_clock::now();
242
243 for (auto& t : _thermalInputs)
244 {
Patrick Venturea58197c2018-06-11 15:29:45 -0700245 auto sensor = _mgr.getSensor(t);
Patrick Ventured8012182018-03-08 08:21:38 -0800246 ReadReturn r = sensor->read();
247 int64_t timeout = sensor->GetTimeout();
248
249 _cachedValuesByName[t] = r.value;
250 tstamp then = r.updated;
251
252 /* Only go into failsafe if the timeout is set for
253 * the sensor.
254 */
255 if (timeout > 0)
256 {
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700257 auto duration =
258 duration_cast<std::chrono::seconds>(now - then).count();
Patrick Ventured8012182018-03-08 08:21:38 -0800259 auto period = std::chrono::seconds(timeout).count();
260 if (duration >= period)
261 {
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700262 // std::cerr << "Entering fail safe mode.\n";
Patrick Ventured8012182018-03-08 08:21:38 -0800263 _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())
270 {
271 _failSafeSensors.erase(kt);
272 }
273 }
274 }
275 }
276
277 return;
278}
279
280void PIDZone::initializeCache(void)
281{
282 for (auto& f : _fanInputs)
283 {
284 _cachedValuesByName[f] = 0;
285 }
286
287 for (auto& t : _thermalInputs)
288 {
289 _cachedValuesByName[t] = 0;
290
291 // Start all sensors in fail-safe mode.
292 _failSafeSensors.insert(t);
293 }
294}
295
296void PIDZone::dumpCache(void)
297{
298 std::cerr << "Cache values now: \n";
299 for (auto& k : _cachedValuesByName)
300 {
301 std::cerr << k.first << ": " << k.second << "\n";
302 }
303}
304
305void PIDZone::process_fans(void)
306{
307 for (auto& p : _fans)
308 {
James Feist22c257a2018-08-31 14:07:12 -0700309 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800310 }
311}
312
313void PIDZone::process_thermals(void)
314{
315 for (auto& p : _thermals)
316 {
James Feist22c257a2018-08-31 14:07:12 -0700317 p->process();
Patrick Ventured8012182018-03-08 08:21:38 -0800318 }
319}
320
Patrick Venturea58197c2018-06-11 15:29:45 -0700321Sensor* PIDZone::getSensor(std::string name)
Patrick Ventured8012182018-03-08 08:21:38 -0800322{
Patrick Venturefe75b192018-06-08 11:19:43 -0700323 return _mgr.getSensor(name);
Patrick Ventured8012182018-03-08 08:21:38 -0800324}
325
326bool PIDZone::manual(bool value)
327{
328 std::cerr << "manual: " << value << std::endl;
329 setManualMode(value);
330 return ModeObject::manual(value);
331}
332
333bool PIDZone::failSafe() const
334{
335 return getFailSafeMode();
336}