1d8012181SPatrick Venture /**
2d8012181SPatrick Venture * Copyright 2017 Google Inc.
3d8012181SPatrick Venture *
4d8012181SPatrick Venture * Licensed under the Apache License, Version 2.0 (the "License");
5d8012181SPatrick Venture * you may not use this file except in compliance with the License.
6d8012181SPatrick Venture * You may obtain a copy of the License at
7d8012181SPatrick Venture *
8d8012181SPatrick Venture * http://www.apache.org/licenses/LICENSE-2.0
9d8012181SPatrick Venture *
10d8012181SPatrick Venture * Unless required by applicable law or agreed to in writing, software
11d8012181SPatrick Venture * distributed under the License is distributed on an "AS IS" BASIS,
12d8012181SPatrick Venture * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d8012181SPatrick Venture * See the License for the specific language governing permissions and
14d8012181SPatrick Venture * limitations under the License.
15d8012181SPatrick Venture */
16*9f1532ddSJonico Eustaquio #include "config.h"
17d8012181SPatrick Venture
18d8012181SPatrick Venture #include "fancontroller.hpp"
19da4a5dd1SPatrick Venture
20c32e3fc5SPatrick Venture #include "tuning.hpp"
21d8012181SPatrick Venture #include "util.hpp"
22d8012181SPatrick Venture #include "zone.hpp"
23d8012181SPatrick Venture
24da4a5dd1SPatrick Venture #include <algorithm>
25ca791156SJosh Lehan #include <cmath>
26da4a5dd1SPatrick Venture #include <iostream>
27da4a5dd1SPatrick Venture
28a076487aSPatrick Venture namespace pid_control
29a076487aSPatrick Venture {
30a076487aSPatrick Venture
31da4a5dd1SPatrick Venture std::unique_ptr<PIDController>
createFanPid(ZoneInterface * owner,const std::string & id,const std::vector<std::string> & inputs,const ec::pidinfo & initial)32563a356fSPatrick Venture FanController::createFanPid(ZoneInterface* owner, const std::string& id,
334a2dc4d8SPatrick Venture const std::vector<std::string>& inputs,
34f77d5a57SPatrick Venture const ec::pidinfo& initial)
35d8012181SPatrick Venture {
36566a1518SPatrick Venture if (inputs.size() == 0)
37566a1518SPatrick Venture {
38566a1518SPatrick Venture return nullptr;
39566a1518SPatrick Venture }
40d8012181SPatrick Venture auto fan = std::make_unique<FanController>(id, inputs, owner);
41563a356fSPatrick Venture ec::pid_info_t* info = fan->getPIDInfo();
42d8012181SPatrick Venture
437af157b1SPatrick Venture initializePIDStruct(info, initial);
44d8012181SPatrick Venture
45d8012181SPatrick Venture return fan;
46d8012181SPatrick Venture }
47d8012181SPatrick Venture
inputProc(void)485f59c0fdSPatrick Venture double FanController::inputProc(void)
49d8012181SPatrick Venture {
50d38ae279SJosh Lehan double value = 0.0;
51d38ae279SJosh Lehan std::vector<double> values;
52d38ae279SJosh Lehan std::vector<double>::iterator result;
53d8012181SPatrick Venture
54d8012181SPatrick Venture try
55d8012181SPatrick Venture {
56d8012181SPatrick Venture for (const auto& name : _inputs)
57d8012181SPatrick Venture {
58d38ae279SJosh Lehan // Read the unscaled value, to correctly recover the RPM
59d38ae279SJosh Lehan value = _owner->getCachedValues(name).unscaled;
60d38ae279SJosh Lehan
61d8012181SPatrick Venture /* If we have a fan we can't read, its value will be 0 for at least
62d8012181SPatrick Venture * some boards, while others... the fan will drop off dbus (if
63d8012181SPatrick Venture * that's how it's being read and in that case its value will never
64d8012181SPatrick Venture * be updated anymore, which is relatively harmless, except, when
65d8012181SPatrick Venture * something tries to read its value through IPMI, and can't, they
66d8012181SPatrick Venture * sort of have to guess -- all the other fans are reporting, why
67d8012181SPatrick Venture * not this one? Maybe it's unable to be read, so it's "bad."
68d8012181SPatrick Venture */
69ca791156SJosh Lehan if (!(std::isfinite(value)))
70d8012181SPatrick Venture {
71ca791156SJosh Lehan continue;
72d8012181SPatrick Venture }
73d38ae279SJosh Lehan if (value <= 0.0)
74ca791156SJosh Lehan {
75ca791156SJosh Lehan continue;
76ca791156SJosh Lehan }
77ca791156SJosh Lehan
78ca791156SJosh Lehan values.push_back(value);
79d8012181SPatrick Venture }
80d8012181SPatrick Venture }
81d8012181SPatrick Venture catch (const std::exception& e)
82d8012181SPatrick Venture {
83563a356fSPatrick Venture std::cerr << "exception on inputProc.\n";
84d8012181SPatrick Venture throw;
85d8012181SPatrick Venture }
86d8012181SPatrick Venture
87566a1518SPatrick Venture /* Reset the value from the above loop. */
88d38ae279SJosh Lehan value = 0.0;
89d8012181SPatrick Venture if (values.size() > 0)
90d8012181SPatrick Venture {
91d8012181SPatrick Venture /* the fan PID algorithm was unstable with average, and seemed to work
92d8012181SPatrick Venture * better with minimum. I had considered making this choice a variable
93df766f25SPatrick Venture * in the configuration, and it's a nice-to-have..
94d8012181SPatrick Venture */
95d8012181SPatrick Venture result = std::min_element(values.begin(), values.end());
96d8012181SPatrick Venture value = *result;
97d8012181SPatrick Venture }
98d8012181SPatrick Venture
995f59c0fdSPatrick Venture return value;
100d8012181SPatrick Venture }
101d8012181SPatrick Venture
setptProc(void)1025f59c0fdSPatrick Venture double FanController::setptProc(void)
103d8012181SPatrick Venture {
104f7a2dd5cSPatrick Venture double maxRPM = _owner->getMaxSetPointRequest();
105d8012181SPatrick Venture
106d8012181SPatrick Venture // store for reference, and check if more or less.
1075f59c0fdSPatrick Venture double prev = getSetpoint();
108d8012181SPatrick Venture
109d8012181SPatrick Venture if (maxRPM > prev)
110d8012181SPatrick Venture {
111d8012181SPatrick Venture setFanDirection(FanSpeedDirection::UP);
112d8012181SPatrick Venture }
113d8012181SPatrick Venture else if (prev > maxRPM)
114d8012181SPatrick Venture {
115d8012181SPatrick Venture setFanDirection(FanSpeedDirection::DOWN);
116d8012181SPatrick Venture }
117d8012181SPatrick Venture else
118d8012181SPatrick Venture {
119d8012181SPatrick Venture setFanDirection(FanSpeedDirection::NEUTRAL);
120d8012181SPatrick Venture }
121d8012181SPatrick Venture
122563a356fSPatrick Venture setSetpoint(maxRPM);
123d8012181SPatrick Venture
124d8012181SPatrick Venture return (maxRPM);
125d8012181SPatrick Venture }
126d8012181SPatrick Venture
outputProc(double value)1275f59c0fdSPatrick Venture void FanController::outputProc(double value)
128d8012181SPatrick Venture {
1295f59c0fdSPatrick Venture double percent = value;
130d8012181SPatrick Venture
131de79ee05SPatrick Venture /* If doing tuning, don't go into failsafe mode. */
132de79ee05SPatrick Venture if (!tuningEnabled)
133c32e3fc5SPatrick Venture {
134df597657SJosh Lehan bool failsafeCurrState = _owner->getFailSafeMode();
135df597657SJosh Lehan
136df597657SJosh Lehan // Note when failsafe state transitions happen
137df597657SJosh Lehan if (failsafePrevState != failsafeCurrState)
138df597657SJosh Lehan {
139df597657SJosh Lehan failsafePrevState = failsafeCurrState;
140df597657SJosh Lehan failsafeTransition = true;
141df597657SJosh Lehan }
142df597657SJosh Lehan
143df597657SJosh Lehan if (failsafeCurrState)
144d8012181SPatrick Venture {
145bcdeb83cSBrandon Kim double failsafePercent = _owner->getFailSafePercent();
146bcdeb83cSBrandon Kim
147bcdeb83cSBrandon Kim #ifdef STRICT_FAILSAFE_PWM
148bcdeb83cSBrandon Kim // Unconditionally replace the computed PWM with the
149bcdeb83cSBrandon Kim // failsafe PWM if STRICT_FAILSAFE_PWM is defined.
150bcdeb83cSBrandon Kim percent = failsafePercent;
151bcdeb83cSBrandon Kim #else
152bcdeb83cSBrandon Kim // Ensure PWM is never lower than the failsafe PWM.
153bcdeb83cSBrandon Kim // The computed PWM is still allowed to rise higher than
154bcdeb83cSBrandon Kim // failsafe PWM if STRICT_FAILSAFE_PWM is NOT defined.
155bcdeb83cSBrandon Kim // This is the default behavior.
156bcdeb83cSBrandon Kim if (percent < failsafePercent)
157d8012181SPatrick Venture {
158bcdeb83cSBrandon Kim percent = failsafePercent;
159d8012181SPatrick Venture }
160bcdeb83cSBrandon Kim #endif
161d8012181SPatrick Venture }
162df597657SJosh Lehan
163df597657SJosh Lehan // Always print if debug enabled
164df597657SJosh Lehan if (debugEnabled)
165df597657SJosh Lehan {
166df597657SJosh Lehan std::cerr << "Zone " << _owner->getZoneID() << " fans, "
167df597657SJosh Lehan << (failsafeCurrState ? "failsafe" : "normal")
168df597657SJosh Lehan << " mode, output pwm: " << percent << "\n";
169df597657SJosh Lehan }
170c51ba919SBonnie Lo else
171c51ba919SBonnie Lo {
172df597657SJosh Lehan // Only print once per transition when not debugging
173df597657SJosh Lehan if (failsafeTransition)
174df597657SJosh Lehan {
175df597657SJosh Lehan failsafeTransition = false;
176df597657SJosh Lehan std::cerr << "Zone " << _owner->getZoneID() << " fans, "
177df597657SJosh Lehan << (failsafeCurrState ? "entering failsafe"
178df597657SJosh Lehan : "returning to normal")
179df597657SJosh Lehan << " mode, output pwm: " << percent << "\n";
180df597657SJosh Lehan }
181df597657SJosh Lehan }
182df597657SJosh Lehan }
183df597657SJosh Lehan else
184df597657SJosh Lehan {
185c51ba919SBonnie Lo if (debugEnabled)
186c51ba919SBonnie Lo {
187c51ba919SBonnie Lo std::cerr << "Zone " << _owner->getZoneID()
188df597657SJosh Lehan << " fans, tuning mode, bypassing failsafe, output pwm: "
189df597657SJosh Lehan << percent << "\n";
190c51ba919SBonnie Lo }
191c32e3fc5SPatrick Venture }
192d8012181SPatrick Venture
193d8012181SPatrick Venture // value and kFanFailSafeDutyCycle are 10 for 10% so let's fix that.
194d38ae279SJosh Lehan percent /= 100.0;
195d8012181SPatrick Venture
196d8012181SPatrick Venture // PidSensorMap for writing.
1974a2dc4d8SPatrick Venture for (const auto& it : _inputs)
198d8012181SPatrick Venture {
199a58197cfSPatrick Venture auto sensor = _owner->getSensor(it);
200a4146eb1SJosh Lehan auto redundantWrite = _owner->getRedundantWrite();
2013f0f7bc3SJosh Lehan int64_t rawWritten = -1;
202a4146eb1SJosh Lehan sensor->write(percent, redundantWrite, &rawWritten);
203b300575eSJosh Lehan
204b300575eSJosh Lehan // The outputCache will be used later,
205b300575eSJosh Lehan // to store a record of the PWM commanded,
206b300575eSJosh Lehan // so that this information can be included during logging.
207b300575eSJosh Lehan auto unscaledWritten = static_cast<double>(rawWritten);
208b300575eSJosh Lehan _owner->setOutputCache(sensor->getName(), {percent, unscaledWritten});
209d8012181SPatrick Venture }
210d8012181SPatrick Venture
211d8012181SPatrick Venture return;
212d8012181SPatrick Venture }
213a076487aSPatrick Venture
~FanController()2147e63502aSPatrick Rudolph FanController::~FanController()
2157e63502aSPatrick Rudolph {
2167e63502aSPatrick Rudolph #ifdef OFFLINE_FAILSAFE_PWM
2177e63502aSPatrick Rudolph double percent = _owner->getFailSafePercent();
2187e63502aSPatrick Rudolph if (debugEnabled)
2197e63502aSPatrick Rudolph {
2207e63502aSPatrick Rudolph std::cerr << "Zone " << _owner->getZoneID()
2217e63502aSPatrick Rudolph << " offline fans output pwm: " << percent << "\n";
2227e63502aSPatrick Rudolph }
2237e63502aSPatrick Rudolph
2247e63502aSPatrick Rudolph // value and kFanFailSafeDutyCycle are 10 for 10% so let's fix that.
2257e63502aSPatrick Rudolph percent /= 100.0;
2267e63502aSPatrick Rudolph
2277e63502aSPatrick Rudolph // PidSensorMap for writing.
2287e63502aSPatrick Rudolph for (const auto& it : _inputs)
2297e63502aSPatrick Rudolph {
2307e63502aSPatrick Rudolph auto sensor = _owner->getSensor(it);
2317e63502aSPatrick Rudolph auto redundantWrite = _owner->getRedundantWrite();
2327e63502aSPatrick Rudolph int64_t rawWritten;
2337e63502aSPatrick Rudolph sensor->write(percent, redundantWrite, &rawWritten);
2347e63502aSPatrick Rudolph
2357e63502aSPatrick Rudolph // The outputCache will be used later,
2367e63502aSPatrick Rudolph // to store a record of the PWM commanded,
2377e63502aSPatrick Rudolph // so that this information can be included during logging.
2387e63502aSPatrick Rudolph auto unscaledWritten = static_cast<double>(rawWritten);
2397e63502aSPatrick Rudolph _owner->setOutputCache(sensor->getName(), {percent, unscaledWritten});
2407e63502aSPatrick Rudolph }
2417e63502aSPatrick Rudolph #endif
2427e63502aSPatrick Rudolph }
2437e63502aSPatrick Rudolph
244a076487aSPatrick Venture } // namespace pid_control
245