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