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 <algorithm>
18 #include <iostream>
19 
20 #include "fancontroller.hpp"
21 #include "util.hpp"
22 #include "zone.hpp"
23 
24 std::unique_ptr<PIDController> FanController::CreateFanPid(
25     ZoneInterface* owner,
26     const std::string& id,
27     std::vector<std::string>& inputs,
28     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->get_pid_info();
36 
37     InitializePIDStruct(info, &initial);
38 
39     return fan;
40 }
41 
42 float FanController::input_proc(void)
43 {
44     float sum = 0;
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                 sum += value;
66             }
67         }
68     }
69     catch (const std::exception& e)
70     {
71         std::cerr << "exception on input_proc.\n";
72         throw;
73     }
74 
75     /* Reset the value from the above loop. */
76     value = 0;
77     if (values.size() > 0)
78     {
79         /* When this is a configuration option in a later update, this code
80          * will be updated.
81          */
82         //value = sum / _inputs.size();
83 
84         /* the fan PID algorithm was unstable with average, and seemed to work
85          * better with minimum.  I had considered making this choice a variable
86          * in the configuration, and I will.
87          */
88         result = std::min_element(values.begin(), values.end());
89         value = *result;
90     }
91 
92     return static_cast<float>(value);
93 }
94 
95 float FanController::setpt_proc(void)
96 {
97     float maxRPM = _owner->getMaxRPMRequest();
98 
99     // store for reference, and check if more or less.
100     float prev = get_setpoint();
101 
102     if (maxRPM > prev)
103     {
104         setFanDirection(FanSpeedDirection::UP);
105     }
106     else if (prev > maxRPM)
107     {
108         setFanDirection(FanSpeedDirection::DOWN);
109     }
110     else
111     {
112         setFanDirection(FanSpeedDirection::NEUTRAL);
113     }
114 
115     set_setpoint(maxRPM);
116 
117     return (maxRPM);
118 }
119 
120 void FanController::output_proc(float value)
121 {
122     float percent = value;
123 
124     /* If doing tuning logging, don't go into failsafe mode. */
125 #ifndef __TUNING_LOGGING__
126     if (_owner->getFailSafeMode())
127     {
128         /* In case it's being set to 100% */
129         if (percent < _owner->getFailSafePercent())
130         {
131             percent = _owner->getFailSafePercent();
132         }
133     }
134 #endif
135 
136     // value and kFanFailSafeDutyCycle are 10 for 10% so let's fix that.
137     percent /= 100;
138 
139     // PidSensorMap for writing.
140     for (auto& it : _inputs)
141     {
142         auto sensor = _owner->getSensor(it);
143         sensor->write(static_cast<double>(percent));
144     }
145 
146     return;
147 }
148 
149