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 "thermalcontroller.hpp"
18 
19 #include "errors/exception.hpp"
20 #include "tuning.hpp"
21 #include "util.hpp"
22 #include "zone.hpp"
23 
24 #include <algorithm>
25 #include <cmath>
26 #include <iostream>
27 
28 namespace pid_control
29 {
30 
31 ThermalType getThermalType(const std::string& typeString)
32 {
33     if (typeString == "margin")
34     {
35         return ThermalType::margin;
36     }
37     if ((typeString == "temp") || (typeString == "power"))
38     {
39         return ThermalType::absolute;
40     }
41     if (typeString == "powersum")
42     {
43         return ThermalType::summation;
44     }
45 
46     throw ControllerBuildException("Unrecognized PID Type/Class string");
47 }
48 
49 bool isThermalType(const std::string& typeString)
50 {
51     static const std::vector<std::string> thermalTypes = {"temp", "margin",
52                                                           "power", "powersum"};
53     return std::count(thermalTypes.begin(), thermalTypes.end(), typeString);
54 }
55 
56 std::unique_ptr<PIDController> ThermalController::createThermalPid(
57     ZoneInterface* owner, const std::string& id,
58     const std::vector<std::string>& inputs, double setpoint,
59     const ec::pidinfo& initial, const ThermalType& type)
60 {
61     // ThermalController requires at least 1 input
62     if (inputs.empty())
63     {
64         throw ControllerBuildException("Thermal controller missing inputs");
65     }
66 
67     auto thermal = std::make_unique<ThermalController>(id, inputs, type, owner);
68 
69     ec::pid_info_t* info = thermal->getPIDInfo();
70     thermal->setSetpoint(setpoint);
71 
72     initializePIDStruct(info, initial);
73 
74     return thermal;
75 }
76 
77 // bmc_host_sensor_value_double
78 double ThermalController::inputProc(void)
79 {
80     double value;
81     const double& (*compare)(const double&, const double&);
82     bool doSummation = false;
83 
84     if (type == ThermalType::margin)
85     {
86         value = std::numeric_limits<double>::max();
87         compare = std::min<double>;
88     }
89     else if (type == ThermalType::absolute)
90     {
91         value = std::numeric_limits<double>::lowest();
92         compare = std::max<double>;
93     }
94     else if (type == ThermalType::summation)
95     {
96         doSummation = true;
97         value = 0.0;
98     }
99     else
100     {
101         throw ControllerBuildException("Unrecognized ThermalType");
102     }
103 
104     std::string leaderName = *(_inputs.begin());
105 
106     bool acceptable = false;
107     for (const auto& in : _inputs)
108     {
109         double cachedValue = _owner->getCachedValue(in);
110 
111         // Less than 0 is perfectly OK for temperature, but must not be NAN
112         if (!(std::isfinite(cachedValue)))
113         {
114             continue;
115         }
116 
117         double oldValue = value;
118 
119         if (doSummation)
120         {
121             value += cachedValue;
122         }
123         else
124         {
125             value = compare(value, cachedValue);
126         }
127 
128         if (oldValue != value)
129         {
130             leaderName = in;
131             _owner->updateThermalPowerDebugInterface(_id, leaderName, value, 0);
132         }
133 
134         acceptable = true;
135     }
136 
137     if (!acceptable)
138     {
139         // While not optimal, zero is better than garbage
140         value = 0;
141     }
142 
143     if (debugEnabled)
144     {
145         std::cerr << getID() << " choose the temperature value: " << value
146                   << " " << leaderName << "\n";
147     }
148 
149     return value;
150 }
151 
152 // bmc_get_setpt
153 double ThermalController::setptProc(void)
154 {
155     double setpoint = getSetpoint();
156 
157     /* TODO(venture): Thermal setpoint invalid? */
158 #if 0
159     if (-1 == setpoint)
160     {
161         return 0.0f;
162     }
163     else
164     {
165         return setpoint;
166     }
167 #endif
168     return setpoint;
169 }
170 
171 // bmc_set_pid_output
172 void ThermalController::outputProc(double value)
173 {
174     _owner->addSetPoint(value, _id);
175     _owner->updateThermalPowerDebugInterface(_id, "", 0, value);
176 
177     if (debugEnabled)
178     {
179         std::cerr << getID() << " pid output pwm: " << value << "\n";
180     }
181 
182     return;
183 }
184 
185 } // namespace pid_control
186