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