1 /**
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 #include "fancontroller.hpp"
18 
19 #include "tuning.hpp"
20 #include "util.hpp"
21 #include "zone.hpp"
22 
23 #include <algorithm>
24 #include <iostream>
25 
26 std::unique_ptr<PIDController>
27     FanController::createFanPid(ZoneInterface* owner, const std::string& id,
28                                 const std::vector<std::string>& inputs,
29                                 const ec::pidinfo& initial)
30 {
31     if (inputs.size() == 0)
32     {
33         return nullptr;
34     }
35     auto fan = std::make_unique<FanController>(id, inputs, owner);
36     ec::pid_info_t* info = fan->getPIDInfo();
37 
38     initializePIDStruct(info, initial);
39 
40     return fan;
41 }
42 
43 double FanController::inputProc(void)
44 {
45     double value = 0;
46     std::vector<int64_t> values;
47     std::vector<int64_t>::iterator result;
48 
49     try
50     {
51         for (const auto& name : _inputs)
52         {
53             value = _owner->getCachedValue(name);
54             /* If we have a fan we can't read, its value will be 0 for at least
55              * some boards, while others... the fan will drop off dbus (if
56              * that's how it's being read and in that case its value will never
57              * be updated anymore, which is relatively harmless, except, when
58              * something tries to read its value through IPMI, and can't, they
59              * sort of have to guess -- all the other fans are reporting, why
60              * not this one?  Maybe it's unable to be read, so it's "bad."
61              */
62             if (value > 0)
63             {
64                 values.push_back(value);
65             }
66         }
67     }
68     catch (const std::exception& e)
69     {
70         std::cerr << "exception on inputProc.\n";
71         throw;
72     }
73 
74     /* Reset the value from the above loop. */
75     value = 0;
76     if (values.size() > 0)
77     {
78         /* the fan PID algorithm was unstable with average, and seemed to work
79          * better with minimum.  I had considered making this choice a variable
80          * in the configuration, and it's a nice-to-have..
81          */
82         result = std::min_element(values.begin(), values.end());
83         value = *result;
84     }
85 
86     return value;
87 }
88 
89 double FanController::setptProc(void)
90 {
91     double maxRPM = _owner->getMaxSetPointRequest();
92 
93     // store for reference, and check if more or less.
94     double prev = getSetpoint();
95 
96     if (maxRPM > prev)
97     {
98         setFanDirection(FanSpeedDirection::UP);
99     }
100     else if (prev > maxRPM)
101     {
102         setFanDirection(FanSpeedDirection::DOWN);
103     }
104     else
105     {
106         setFanDirection(FanSpeedDirection::NEUTRAL);
107     }
108 
109     setSetpoint(maxRPM);
110 
111     return (maxRPM);
112 }
113 
114 void FanController::outputProc(double value)
115 {
116     double percent = value;
117 
118     /* If doing tuning, don't go into failsafe mode. */
119     if (!tuningEnabled)
120     {
121         if (_owner->getFailSafeMode())
122         {
123             /* In case it's being set to 100% */
124             if (percent < _owner->getFailSafePercent())
125             {
126                 percent = _owner->getFailSafePercent();
127             }
128         }
129     }
130 
131     // value and kFanFailSafeDutyCycle are 10 for 10% so let's fix that.
132     percent /= 100;
133 
134     // PidSensorMap for writing.
135     for (const auto& it : _inputs)
136     {
137         auto sensor = _owner->getSensor(it);
138         sensor->write(percent);
139     }
140 
141     return;
142 }
143